tapimo 0.14.0 → 0.15.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 (1141) hide show
  1. package/README.md +7 -4
  2. package/dist/ApiKey.js +1 -1
  3. package/dist/ApiKey.js.map +1 -1
  4. package/dist/ApiKeys.d.ts +95 -14
  5. package/dist/ApiKeys.d.ts.map +1 -1
  6. package/dist/ApiKeys.js +360 -49
  7. package/dist/ApiKeys.js.map +1 -1
  8. package/dist/App.d.ts +12457 -10365
  9. package/dist/App.d.ts.map +1 -1
  10. package/dist/App.js +300 -22
  11. package/dist/App.js.map +1 -1
  12. package/dist/Assets.d.ts.map +1 -1
  13. package/dist/Assets.js +2 -1
  14. package/dist/Assets.js.map +1 -1
  15. package/dist/Handler.d.ts +0 -1
  16. package/dist/Handler.d.ts.map +1 -1
  17. package/dist/Handler.js +0 -1
  18. package/dist/Handler.js.map +1 -1
  19. package/dist/Scope.d.ts +13 -5
  20. package/dist/Scope.d.ts.map +1 -1
  21. package/dist/Scope.js +16 -5
  22. package/dist/Scope.js.map +1 -1
  23. package/dist/Webhooks.js +1 -1
  24. package/dist/Webhooks.js.map +1 -1
  25. package/dist/admin/App.d.ts +3057 -549
  26. package/dist/admin/App.d.ts.map +1 -1
  27. package/dist/admin/App.js +26 -5
  28. package/dist/admin/App.js.map +1 -1
  29. package/dist/admin/apps/api-keys.d.ts +159 -7
  30. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  31. package/dist/admin/apps/api-keys.js +95 -43
  32. package/dist/admin/apps/api-keys.js.map +1 -1
  33. package/dist/admin/apps/earn-vaults.d.ts +1398 -0
  34. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  35. package/dist/admin/apps/earn-vaults.js +463 -7
  36. package/dist/admin/apps/earn-vaults.js.map +1 -1
  37. package/dist/admin/apps/organizations.d.ts +267 -15
  38. package/dist/admin/apps/organizations.d.ts.map +1 -1
  39. package/dist/admin/apps/organizations.js +241 -25
  40. package/dist/admin/apps/organizations.js.map +1 -1
  41. package/dist/admin/apps/routes-support.d.ts +95 -0
  42. package/dist/admin/apps/routes-support.d.ts.map +1 -0
  43. package/dist/admin/apps/routes-support.js +151 -0
  44. package/dist/admin/apps/routes-support.js.map +1 -0
  45. package/dist/admin/apps/routes.d.ts +2764 -0
  46. package/dist/admin/apps/routes.d.ts.map +1 -0
  47. package/dist/admin/apps/routes.js +728 -0
  48. package/dist/admin/apps/routes.js.map +1 -0
  49. package/dist/admin/apps/subsidies.d.ts +501 -0
  50. package/dist/admin/apps/subsidies.d.ts.map +1 -0
  51. package/dist/admin/apps/subsidies.js +384 -0
  52. package/dist/admin/apps/subsidies.js.map +1 -0
  53. package/dist/admin/apps/verified-tokens.d.ts +88 -88
  54. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  55. package/dist/admin/apps/verified-tokens.js +3 -3
  56. package/dist/admin/apps/verified-tokens.js.map +1 -1
  57. package/dist/admin/ui.generated.d.ts +1 -1
  58. package/dist/admin/ui.generated.d.ts.map +1 -1
  59. package/dist/admin/ui.generated.js +129 -69
  60. package/dist/admin/ui.generated.js.map +1 -1
  61. package/dist/analytics/Analytics.d.ts +5 -2
  62. package/dist/analytics/Analytics.d.ts.map +1 -1
  63. package/dist/analytics/Analytics.js +31 -1
  64. package/dist/analytics/Analytics.js.map +1 -1
  65. package/dist/analytics/tables/requestEvents.d.ts +2 -1
  66. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  67. package/dist/analytics/tables/requestEvents.js +10 -1
  68. package/dist/analytics/tables/requestEvents.js.map +1 -1
  69. package/dist/analytics/tables/routesLifecycleIntervals.d.ts +72 -0
  70. package/dist/analytics/tables/routesLifecycleIntervals.d.ts.map +1 -0
  71. package/dist/analytics/tables/routesLifecycleIntervals.js +56 -0
  72. package/dist/analytics/tables/routesLifecycleIntervals.js.map +1 -0
  73. package/dist/apps/Zones.d.ts +21 -12526
  74. package/dist/apps/Zones.d.ts.map +1 -1
  75. package/dist/apps/Zones.js +37 -5
  76. package/dist/apps/Zones.js.map +1 -1
  77. package/dist/apps/data/App.d.ts +3971 -2595
  78. package/dist/apps/data/App.d.ts.map +1 -1
  79. package/dist/apps/data/App.js +76 -21
  80. package/dist/apps/data/App.js.map +1 -1
  81. package/dist/apps/data/routes/activities.d.ts +364 -34
  82. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  83. package/dist/apps/data/routes/activities.js +495 -190
  84. package/dist/apps/data/routes/activities.js.map +1 -1
  85. package/dist/apps/data/routes/addresses.d.ts +72 -0
  86. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  87. package/dist/apps/data/routes/blocks.d.ts +37 -37
  88. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  89. package/dist/apps/data/routes/coingecko.js +47 -21
  90. package/dist/apps/data/routes/coingecko.js.map +1 -1
  91. package/dist/apps/data/routes/earn.d.ts +1450 -337
  92. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  93. package/dist/apps/data/routes/earn.js +1226 -144
  94. package/dist/apps/data/routes/earn.js.map +1 -1
  95. package/dist/apps/data/routes/fee-amm.d.ts +5 -0
  96. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  97. package/dist/apps/data/routes/fee-amm.js +31 -19
  98. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  99. package/dist/apps/data/routes/indexer.d.ts +125 -0
  100. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  101. package/dist/apps/data/routes/indexer.js +83 -27
  102. package/dist/apps/data/routes/indexer.js.map +1 -1
  103. package/dist/apps/data/routes/rpc.d.ts +507 -33
  104. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  105. package/dist/apps/data/routes/rpc.js +266 -156
  106. package/dist/apps/data/routes/rpc.js.map +1 -1
  107. package/dist/apps/data/routes/tokenlist.d.ts +47 -15
  108. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  109. package/dist/apps/data/routes/tokenlist.js +68 -11
  110. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  111. package/dist/apps/data/routes/tokens.d.ts +2 -0
  112. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  113. package/dist/apps/data/routes/tokens.js +26 -14
  114. package/dist/apps/data/routes/tokens.js.map +1 -1
  115. package/dist/apps/data/routes/transfers.d.ts +28 -12
  116. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  117. package/dist/apps/data/routes/transfers.js +9 -7
  118. package/dist/apps/data/routes/transfers.js.map +1 -1
  119. package/dist/apps/data/routes/valuation.d.ts +30 -30
  120. package/dist/apps/data/routes/verified-tokens.d.ts +9 -9
  121. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  122. package/dist/apps/data/routes/verified-tokens.js +64 -20
  123. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  124. package/dist/apps/data/routes/webhooks.d.ts +613 -176
  125. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  126. package/dist/apps/data/routes/webhooks.js +291 -195
  127. package/dist/apps/data/routes/webhooks.js.map +1 -1
  128. package/dist/apps/data/routes/zones.d.ts.map +1 -1
  129. package/dist/apps/data/routes/zones.js +20 -12
  130. package/dist/apps/data/routes/zones.js.map +1 -1
  131. package/dist/apps/docs/reference.js +2 -2
  132. package/dist/apps/docs/reference.js.map +1 -1
  133. package/dist/apps/index.d.ts +1 -1
  134. package/dist/apps/index.d.ts.map +1 -1
  135. package/dist/apps/index.js +1 -1
  136. package/dist/apps/index.js.map +1 -1
  137. package/dist/apps/management/App.d.ts +3763 -3385
  138. package/dist/apps/management/App.d.ts.map +1 -1
  139. package/dist/apps/management/App.js +5 -14
  140. package/dist/apps/management/App.js.map +1 -1
  141. package/dist/apps/management/Billing.d.ts +48 -7
  142. package/dist/apps/management/Billing.d.ts.map +1 -1
  143. package/dist/apps/management/Billing.js +204 -25
  144. package/dist/apps/management/Billing.js.map +1 -1
  145. package/dist/apps/management/routes/api-keys.d.ts +106 -4
  146. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  147. package/dist/apps/management/routes/api-keys.js +43 -15
  148. package/dist/apps/management/routes/api-keys.js.map +1 -1
  149. package/dist/apps/management/routes/billing.d.ts +290 -4
  150. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  151. package/dist/apps/management/routes/billing.js +274 -49
  152. package/dist/apps/management/routes/billing.js.map +1 -1
  153. package/dist/apps/management/routes/faucet.d.ts +6 -6
  154. package/dist/apps/management/routes/faucet.js +1 -1
  155. package/dist/apps/management/routes/faucet.js.map +1 -1
  156. package/dist/apps/management/routes/invitations.d.ts +77 -2
  157. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  158. package/dist/apps/management/routes/invitations.js +117 -13
  159. package/dist/apps/management/routes/invitations.js.map +1 -1
  160. package/dist/apps/management/routes/invite-links.d.ts +4 -4
  161. package/dist/apps/management/routes/me.d.ts.map +1 -1
  162. package/dist/apps/management/routes/me.js +6 -2
  163. package/dist/apps/management/routes/me.js.map +1 -1
  164. package/dist/apps/management/routes/members.d.ts +30 -30
  165. package/dist/apps/management/routes/orgs.d.ts +18 -2
  166. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  167. package/dist/apps/management/routes/orgs.js +52 -12
  168. package/dist/apps/management/routes/orgs.js.map +1 -1
  169. package/dist/apps/management/routes/projects.d.ts +4 -4
  170. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  171. package/dist/apps/management/routes/projects.js +6 -1
  172. package/dist/apps/management/routes/projects.js.map +1 -1
  173. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  174. package/dist/apps/management/routes/scopes.js +4 -1
  175. package/dist/apps/management/routes/scopes.js.map +1 -1
  176. package/dist/apps/management/routes/usage.d.ts +24 -24
  177. package/dist/apps/management/routes/usage.js +21 -4
  178. package/dist/apps/management/routes/usage.js.map +1 -1
  179. package/dist/apps/management/routes/verified-token-requests.d.ts +2 -2
  180. package/dist/apps/management/routes/webhooks.d.ts +280 -214
  181. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  182. package/dist/apps/management/routes/webhooks.js +58 -12
  183. package/dist/apps/management/routes/webhooks.js.map +1 -1
  184. package/dist/apps/mcp.d.ts +1 -1
  185. package/dist/apps/mcp.js +7 -7
  186. package/dist/apps/mcp.js.map +1 -1
  187. package/dist/apps/mpp/App.d.ts.map +1 -1
  188. package/dist/apps/mpp/App.js +35 -7
  189. package/dist/apps/mpp/App.js.map +1 -1
  190. package/dist/apps/mpp/internal/Adapter.d.ts +2 -0
  191. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
  192. package/dist/apps/mpp/internal/Adapter.js +30 -1
  193. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  194. package/dist/apps/mpp/internal/Policy.d.ts +22 -4
  195. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
  196. package/dist/apps/mpp/internal/Policy.js +24 -6
  197. package/dist/apps/mpp/internal/Policy.js.map +1 -1
  198. package/dist/apps/mpp/internal/Sponsorship.d.ts +3 -0
  199. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
  200. package/dist/apps/mpp/internal/Sponsorship.js +6 -3
  201. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
  202. package/dist/apps/relay/App.d.ts +2 -0
  203. package/dist/apps/relay/App.d.ts.map +1 -1
  204. package/dist/apps/relay/App.js +134 -75
  205. package/dist/apps/relay/App.js.map +1 -1
  206. package/dist/apps/relay/Sponsorships.d.ts +5 -3
  207. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  208. package/dist/apps/relay/Sponsorships.js +209 -28
  209. package/dist/apps/relay/Sponsorships.js.map +1 -1
  210. package/dist/apps/{funding → routes}/Access.d.ts +4 -4
  211. package/dist/apps/routes/Access.d.ts.map +1 -0
  212. package/dist/apps/{funding → routes}/Access.js +4 -4
  213. package/dist/apps/routes/Access.js.map +1 -0
  214. package/dist/apps/{funding → routes}/App.d.ts +418 -124
  215. package/dist/apps/{funding → routes}/App.d.ts.map +1 -1
  216. package/dist/apps/{funding → routes}/App.js +16 -16
  217. package/dist/apps/routes/App.js.map +1 -0
  218. package/dist/apps/routes/Inventory.d.ts.map +1 -0
  219. package/dist/apps/{funding → routes}/Inventory.js +2 -2
  220. package/dist/apps/routes/Inventory.js.map +1 -0
  221. package/dist/apps/routes/index.d.ts +2 -0
  222. package/dist/apps/routes/index.d.ts.map +1 -0
  223. package/dist/apps/routes/index.js +2 -0
  224. package/dist/apps/routes/index.js.map +1 -0
  225. package/dist/apps/{funding → routes}/routes/chains.d.ts +7 -7
  226. package/dist/apps/routes/routes/chains.d.ts.map +1 -0
  227. package/dist/apps/{funding → routes}/routes/chains.js +25 -25
  228. package/dist/apps/routes/routes/chains.js.map +1 -0
  229. package/dist/apps/{funding → routes}/routes/deposit-addresses.d.ts +107 -43
  230. package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -0
  231. package/dist/apps/{funding → routes}/routes/deposit-addresses.js +497 -222
  232. package/dist/apps/routes/routes/deposit-addresses.js.map +1 -0
  233. package/dist/apps/{funding → routes}/routes/providers.d.ts +8 -8
  234. package/dist/apps/routes/routes/providers.d.ts.map +1 -0
  235. package/dist/apps/routes/routes/providers.js +57 -0
  236. package/dist/apps/routes/routes/providers.js.map +1 -0
  237. package/dist/apps/{funding → routes}/routes/quotes.d.ts +19 -16
  238. package/dist/apps/routes/routes/quotes.d.ts.map +1 -0
  239. package/dist/apps/{funding → routes}/routes/quotes.js +55 -38
  240. package/dist/apps/routes/routes/quotes.js.map +1 -0
  241. package/dist/apps/{funding → routes}/routes/transfers.d.ts +379 -74
  242. package/dist/apps/routes/routes/transfers.d.ts.map +1 -0
  243. package/dist/apps/routes/routes/transfers.js +1140 -0
  244. package/dist/apps/routes/routes/transfers.js.map +1 -0
  245. package/dist/cli/Cli.d.ts +30 -0
  246. package/dist/cli/Cli.d.ts.map +1 -1
  247. package/dist/cli/commands/admin.d.ts +36 -0
  248. package/dist/cli/commands/admin.d.ts.map +1 -1
  249. package/dist/cli/commands/admin.js +273 -0
  250. package/dist/cli/commands/admin.js.map +1 -1
  251. package/dist/cloudflare.d.ts +16 -14
  252. package/dist/cloudflare.d.ts.map +1 -1
  253. package/dist/cloudflare.js +26 -21
  254. package/dist/cloudflare.js.map +1 -1
  255. package/dist/db/Db.d.ts +63 -21
  256. package/dist/db/Db.d.ts.map +1 -1
  257. package/dist/db/Db.js +1030 -15
  258. package/dist/db/Db.js.map +1 -1
  259. package/dist/db/Schema.d.ts +780 -133
  260. package/dist/db/Schema.d.ts.map +1 -1
  261. package/dist/db/Schema.js +575 -162
  262. package/dist/db/Schema.js.map +1 -1
  263. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts +4 -0
  264. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts.map +1 -0
  265. package/dist/db/tables/apiKeyAdmissionBootstraps.js +2 -0
  266. package/dist/db/tables/apiKeyAdmissionBootstraps.js.map +1 -0
  267. package/dist/db/tables/apiKeyAdmissions.d.ts +73 -0
  268. package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -0
  269. package/dist/db/tables/apiKeyAdmissions.js +142 -0
  270. package/dist/db/tables/apiKeyAdmissions.js.map +1 -0
  271. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts +4 -0
  272. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts.map +1 -0
  273. package/dist/db/tables/apiKeyOwnerAdmissions.js +2 -0
  274. package/dist/db/tables/apiKeyOwnerAdmissions.js.map +1 -0
  275. package/dist/db/tables/apiKeyOwnerTombstones.d.ts +20 -0
  276. package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -0
  277. package/dist/db/tables/apiKeyOwnerTombstones.js +57 -0
  278. package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -0
  279. package/dist/db/tables/apiKeyRevocations.d.ts +21 -0
  280. package/dist/db/tables/apiKeyRevocations.d.ts.map +1 -0
  281. package/dist/db/tables/apiKeyRevocations.js +98 -0
  282. package/dist/db/tables/apiKeyRevocations.js.map +1 -0
  283. package/dist/db/tables/authAccounts.d.ts +47 -0
  284. package/dist/db/tables/authAccounts.d.ts.map +1 -0
  285. package/dist/db/tables/authAccounts.js +72 -0
  286. package/dist/db/tables/authAccounts.js.map +1 -0
  287. package/dist/db/tables/authSessions.d.ts +12 -0
  288. package/dist/db/tables/authSessions.d.ts.map +1 -0
  289. package/dist/db/tables/authSessions.js +2 -0
  290. package/dist/db/tables/authSessions.js.map +1 -0
  291. package/dist/db/tables/authVerifications.d.ts +12 -0
  292. package/dist/db/tables/authVerifications.d.ts.map +1 -0
  293. package/dist/db/tables/authVerifications.js +2 -0
  294. package/dist/db/tables/authVerifications.js.map +1 -0
  295. package/dist/db/tables/invitations.d.ts +17 -1
  296. package/dist/db/tables/invitations.d.ts.map +1 -1
  297. package/dist/db/tables/invitations.js +123 -43
  298. package/dist/db/tables/invitations.js.map +1 -1
  299. package/dist/db/tables/memberships.d.ts +8 -0
  300. package/dist/db/tables/memberships.d.ts.map +1 -1
  301. package/dist/db/tables/memberships.js +51 -2
  302. package/dist/db/tables/memberships.js.map +1 -1
  303. package/dist/db/tables/organizations.d.ts +32 -10
  304. package/dist/db/tables/organizations.d.ts.map +1 -1
  305. package/dist/db/tables/organizations.js +110 -49
  306. package/dist/db/tables/organizations.js.map +1 -1
  307. package/dist/db/tables/projects.d.ts +2 -0
  308. package/dist/db/tables/projects.d.ts.map +1 -1
  309. package/dist/db/tables/projects.js +12 -3
  310. package/dist/db/tables/projects.js.map +1 -1
  311. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  312. package/dist/db/tables/requestUsage.js.map +1 -1
  313. package/dist/db/tables/rewardAccounts.d.ts +86 -0
  314. package/dist/db/tables/rewardAccounts.d.ts.map +1 -0
  315. package/dist/db/tables/rewardAccounts.js +88 -0
  316. package/dist/db/tables/rewardAccounts.js.map +1 -0
  317. package/dist/db/tables/rewardCampaignDistributors.d.ts +36 -0
  318. package/dist/db/tables/rewardCampaignDistributors.d.ts.map +1 -0
  319. package/dist/db/tables/rewardCampaignDistributors.js +30 -0
  320. package/dist/db/tables/rewardCampaignDistributors.js.map +1 -0
  321. package/dist/db/tables/rewardCampaigns.d.ts +171 -0
  322. package/dist/db/tables/rewardCampaigns.d.ts.map +1 -0
  323. package/dist/db/tables/rewardCampaigns.js +392 -0
  324. package/dist/db/tables/rewardCampaigns.js.map +1 -0
  325. package/dist/db/tables/rewardCredits.d.ts +53 -0
  326. package/dist/db/tables/rewardCredits.d.ts.map +1 -0
  327. package/dist/db/tables/rewardCredits.js +63 -0
  328. package/dist/db/tables/rewardCredits.js.map +1 -0
  329. package/dist/db/tables/rewardEligibilityAssociations.d.ts +61 -0
  330. package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -0
  331. package/dist/db/tables/rewardEligibilityAssociations.js +103 -0
  332. package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -0
  333. package/dist/db/tables/rewardRuns.d.ts +188 -0
  334. package/dist/db/tables/rewardRuns.d.ts.map +1 -0
  335. package/dist/db/tables/rewardRuns.js +278 -0
  336. package/dist/db/tables/rewardRuns.js.map +1 -0
  337. package/dist/db/tables/rewardTransactionAttempts.d.ts +100 -0
  338. package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -0
  339. package/dist/db/tables/rewardTransactionAttempts.js +163 -0
  340. package/dist/db/tables/rewardTransactionAttempts.js.map +1 -0
  341. package/dist/db/tables/routesCatalog.d.ts +58 -0
  342. package/dist/db/tables/routesCatalog.d.ts.map +1 -0
  343. package/dist/db/tables/{fundingCatalog.js → routesCatalog.js} +19 -19
  344. package/dist/db/tables/routesCatalog.js.map +1 -0
  345. package/dist/db/tables/routesDepositAddresses.d.ts +476 -0
  346. package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -0
  347. package/dist/db/tables/routesDepositAddresses.js +587 -0
  348. package/dist/db/tables/routesDepositAddresses.js.map +1 -0
  349. package/dist/db/tables/{fundingDepositRequestObservations.d.ts → routesDepositRequestObservations.d.ts} +6 -6
  350. package/dist/db/tables/routesDepositRequestObservations.d.ts.map +1 -0
  351. package/dist/db/tables/{fundingDepositRequestObservations.js → routesDepositRequestObservations.js} +6 -6
  352. package/dist/db/tables/routesDepositRequestObservations.js.map +1 -0
  353. package/dist/db/tables/{fundingDeposits.d.ts → routesDeposits.d.ts} +210 -22
  354. package/dist/db/tables/routesDeposits.d.ts.map +1 -0
  355. package/dist/db/tables/routesDeposits.js +576 -0
  356. package/dist/db/tables/routesDeposits.js.map +1 -0
  357. package/dist/db/tables/routesIdempotency.d.ts +166 -0
  358. package/dist/db/tables/routesIdempotency.d.ts.map +1 -0
  359. package/dist/db/tables/routesIdempotency.js +330 -0
  360. package/dist/db/tables/routesIdempotency.js.map +1 -0
  361. package/dist/db/tables/routesSubsidies.d.ts +27 -0
  362. package/dist/db/tables/routesSubsidies.d.ts.map +1 -0
  363. package/dist/db/tables/routesSubsidies.js +45 -0
  364. package/dist/db/tables/routesSubsidies.js.map +1 -0
  365. package/dist/db/tables/{fundingTransferEvents.d.ts → routesTransferEvents.d.ts} +3 -3
  366. package/dist/db/tables/routesTransferEvents.d.ts.map +1 -0
  367. package/dist/db/tables/{fundingTransferEvents.js → routesTransferEvents.js} +3 -3
  368. package/dist/db/tables/routesTransferEvents.js.map +1 -0
  369. package/dist/db/tables/routesTransferSubsidies.d.ts +19 -0
  370. package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -0
  371. package/dist/db/tables/routesTransferSubsidies.js +17 -0
  372. package/dist/db/tables/routesTransferSubsidies.js.map +1 -0
  373. package/dist/db/tables/routesTransferSubsidyNonces.d.ts +51 -0
  374. package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -0
  375. package/dist/db/tables/routesTransferSubsidyNonces.js +82 -0
  376. package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -0
  377. package/dist/db/tables/routesTransferTransactions.d.ts +77 -0
  378. package/dist/db/tables/routesTransferTransactions.d.ts.map +1 -0
  379. package/dist/db/tables/{fundingTransferTransactions.js → routesTransferTransactions.js} +31 -13
  380. package/dist/db/tables/routesTransferTransactions.js.map +1 -0
  381. package/dist/db/tables/routesTransfers.d.ts +319 -0
  382. package/dist/db/tables/routesTransfers.d.ts.map +1 -0
  383. package/dist/db/tables/routesTransfers.js +372 -0
  384. package/dist/db/tables/routesTransfers.js.map +1 -0
  385. package/dist/db/tables/sponsoredTransactions.d.ts +75 -20
  386. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  387. package/dist/db/tables/sponsoredTransactions.js +124 -54
  388. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  389. package/dist/db/tables/sponsorshipAttributions.d.ts +69 -0
  390. package/dist/db/tables/sponsorshipAttributions.d.ts.map +1 -0
  391. package/dist/db/tables/sponsorshipAttributions.js +77 -0
  392. package/dist/db/tables/sponsorshipAttributions.js.map +1 -0
  393. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts +22 -0
  394. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts.map +1 -0
  395. package/dist/db/tables/sponsorshipReconciliationCursors.js +45 -0
  396. package/dist/db/tables/sponsorshipReconciliationCursors.js.map +1 -0
  397. package/dist/db/tables/stripeCustomers.d.ts +11 -0
  398. package/dist/db/tables/stripeCustomers.d.ts.map +1 -1
  399. package/dist/db/tables/stripeCustomers.js +13 -0
  400. package/dist/db/tables/stripeCustomers.js.map +1 -1
  401. package/dist/db/tables/users.d.ts +17 -1
  402. package/dist/db/tables/users.d.ts.map +1 -1
  403. package/dist/db/tables/users.js +47 -2
  404. package/dist/db/tables/users.js.map +1 -1
  405. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  406. package/dist/db/tables/webhookQueueEvents.js +5 -1
  407. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  408. package/dist/db/tables/webhookSubscriptions.d.ts +19 -11
  409. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  410. package/dist/db/tables/webhookSubscriptions.js +45 -13
  411. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  412. package/dist/handlers/internal/executionError.d.ts +2 -2
  413. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  414. package/dist/handlers/internal/executionError.js +1 -2
  415. package/dist/handlers/internal/executionError.js.map +1 -1
  416. package/dist/handlers/internal/multisig.d.ts +4 -232
  417. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  418. package/dist/handlers/internal/multisig.js +11 -537
  419. package/dist/handlers/internal/multisig.js.map +1 -1
  420. package/dist/handlers/internal/sponsorship.d.ts +19 -8
  421. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  422. package/dist/handlers/internal/sponsorship.js +13 -9
  423. package/dist/handlers/internal/sponsorship.js.map +1 -1
  424. package/dist/handlers/relay.d.ts +9 -12
  425. package/dist/handlers/relay.d.ts.map +1 -1
  426. package/dist/handlers/relay.js +81 -85
  427. package/dist/handlers/relay.js.map +1 -1
  428. package/dist/index.d.ts +0 -17
  429. package/dist/index.d.ts.map +1 -1
  430. package/dist/index.js +0 -17
  431. package/dist/index.js.map +1 -1
  432. package/dist/internal/AssetResponse.d.ts +12 -0
  433. package/dist/internal/AssetResponse.d.ts.map +1 -0
  434. package/dist/internal/AssetResponse.js +21 -0
  435. package/dist/internal/AssetResponse.js.map +1 -0
  436. package/dist/internal/Auth.d.ts +48 -12
  437. package/dist/internal/Auth.d.ts.map +1 -1
  438. package/dist/internal/Auth.js +603 -175
  439. package/dist/internal/Auth.js.map +1 -1
  440. package/dist/internal/BetterAuth.d.ts +1488 -0
  441. package/dist/internal/BetterAuth.d.ts.map +1 -0
  442. package/dist/internal/BetterAuth.js +253 -0
  443. package/dist/internal/BetterAuth.js.map +1 -0
  444. package/dist/internal/Cache.d.ts +2 -0
  445. package/dist/internal/Cache.d.ts.map +1 -1
  446. package/dist/internal/Cache.js +2 -0
  447. package/dist/internal/Cache.js.map +1 -1
  448. package/dist/internal/EarnRates.d.ts +26 -3
  449. package/dist/internal/EarnRates.d.ts.map +1 -1
  450. package/dist/internal/EarnRates.js +84 -19
  451. package/dist/internal/EarnRates.js.map +1 -1
  452. package/dist/internal/EarnSharePrices.d.ts +120 -0
  453. package/dist/internal/EarnSharePrices.d.ts.map +1 -0
  454. package/dist/internal/EarnSharePrices.js +179 -0
  455. package/dist/internal/EarnSharePrices.js.map +1 -0
  456. package/dist/internal/EarnVaults.d.ts +4 -0
  457. package/dist/internal/EarnVaults.d.ts.map +1 -1
  458. package/dist/internal/EarnVaults.js +27 -10
  459. package/dist/internal/EarnVaults.js.map +1 -1
  460. package/dist/internal/Email.d.ts +37 -0
  461. package/dist/internal/Email.d.ts.map +1 -0
  462. package/dist/internal/Email.js +16 -0
  463. package/dist/internal/Email.js.map +1 -0
  464. package/dist/internal/Log.d.ts +32 -28
  465. package/dist/internal/Log.d.ts.map +1 -1
  466. package/dist/internal/Log.js +18 -11
  467. package/dist/internal/Log.js.map +1 -1
  468. package/dist/internal/MetricSink.d.ts +3 -0
  469. package/dist/internal/MetricSink.d.ts.map +1 -1
  470. package/dist/internal/MetricSink.js +67 -8
  471. package/dist/internal/MetricSink.js.map +1 -1
  472. package/dist/internal/Mpp.d.ts.map +1 -1
  473. package/dist/internal/Mpp.js +20 -25
  474. package/dist/internal/Mpp.js.map +1 -1
  475. package/dist/internal/OpenApi.d.ts +26 -3
  476. package/dist/internal/OpenApi.d.ts.map +1 -1
  477. package/dist/internal/OpenApi.js +25 -10
  478. package/dist/internal/OpenApi.js.map +1 -1
  479. package/dist/internal/Provider.d.ts +1 -1
  480. package/dist/internal/Provider.d.ts.map +1 -1
  481. package/dist/internal/RateLimit.d.ts.map +1 -1
  482. package/dist/internal/RateLimit.js +13 -1
  483. package/dist/internal/RateLimit.js.map +1 -1
  484. package/dist/internal/Store.d.ts +16 -0
  485. package/dist/internal/Store.d.ts.map +1 -1
  486. package/dist/internal/Store.js +59 -8
  487. package/dist/internal/Store.js.map +1 -1
  488. package/dist/internal/Tidx.d.ts +9 -2
  489. package/dist/internal/Tidx.d.ts.map +1 -1
  490. package/dist/internal/Tidx.js +78 -11
  491. package/dist/internal/Tidx.js.map +1 -1
  492. package/dist/internal/Timing.d.ts +26 -0
  493. package/dist/internal/Timing.d.ts.map +1 -1
  494. package/dist/internal/Timing.js +63 -5
  495. package/dist/internal/Timing.js.map +1 -1
  496. package/dist/internal/VercelOidc.d.ts +94 -0
  497. package/dist/internal/VercelOidc.d.ts.map +1 -0
  498. package/dist/internal/VercelOidc.js +128 -0
  499. package/dist/internal/VercelOidc.js.map +1 -0
  500. package/dist/internal/VerifiedTokens.d.ts +2 -8
  501. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  502. package/dist/internal/VerifiedTokens.js +24 -24
  503. package/dist/internal/VerifiedTokens.js.map +1 -1
  504. package/dist/internal/Viem.d.ts +30 -2067
  505. package/dist/internal/Viem.d.ts.map +1 -1
  506. package/dist/internal/Viem.js +167 -17
  507. package/dist/internal/Viem.js.map +1 -1
  508. package/dist/internal/Webhooks.d.ts +12 -4
  509. package/dist/internal/Webhooks.d.ts.map +1 -1
  510. package/dist/internal/Webhooks.js +9 -3
  511. package/dist/internal/Webhooks.js.map +1 -1
  512. package/dist/internal/ZoneRpc.d.ts +7 -0
  513. package/dist/internal/ZoneRpc.d.ts.map +1 -0
  514. package/dist/internal/ZoneRpc.js +49 -0
  515. package/dist/internal/ZoneRpc.js.map +1 -0
  516. package/dist/internal/index.d.ts +3 -1
  517. package/dist/internal/index.d.ts.map +1 -1
  518. package/dist/internal/index.js +3 -1
  519. package/dist/internal/index.js.map +1 -1
  520. package/dist/internal/rewards/Audit.d.ts +88 -0
  521. package/dist/internal/rewards/Audit.d.ts.map +1 -0
  522. package/dist/internal/rewards/Audit.js +134 -0
  523. package/dist/internal/rewards/Audit.js.map +1 -0
  524. package/dist/internal/rewards/Campaigns.d.ts +572 -0
  525. package/dist/internal/rewards/Campaigns.d.ts.map +1 -0
  526. package/dist/internal/rewards/Campaigns.js +531 -0
  527. package/dist/internal/rewards/Campaigns.js.map +1 -0
  528. package/dist/internal/rewards/Credits.d.ts +76 -0
  529. package/dist/internal/rewards/Credits.d.ts.map +1 -0
  530. package/dist/internal/rewards/Credits.js +95 -0
  531. package/dist/internal/rewards/Credits.js.map +1 -0
  532. package/dist/internal/rewards/Events.d.ts +100 -0
  533. package/dist/internal/rewards/Events.d.ts.map +1 -0
  534. package/dist/internal/rewards/Events.js +340 -0
  535. package/dist/internal/rewards/Events.js.map +1 -0
  536. package/dist/internal/rewards/Projection.d.ts +237 -0
  537. package/dist/internal/rewards/Projection.d.ts.map +1 -0
  538. package/dist/internal/rewards/Projection.js +616 -0
  539. package/dist/internal/rewards/Projection.js.map +1 -0
  540. package/dist/internal/rewards/Rates.d.ts +44 -0
  541. package/dist/internal/rewards/Rates.d.ts.map +1 -0
  542. package/dist/internal/rewards/Rates.js +60 -0
  543. package/dist/internal/rewards/Rates.js.map +1 -0
  544. package/dist/internal/rewards/Read.d.ts +158 -0
  545. package/dist/internal/rewards/Read.d.ts.map +1 -0
  546. package/dist/internal/rewards/Read.js +292 -0
  547. package/dist/internal/rewards/Read.js.map +1 -0
  548. package/dist/internal/rewards/Runner.d.ts +164 -0
  549. package/dist/internal/rewards/Runner.d.ts.map +1 -0
  550. package/dist/internal/rewards/Runner.js +1778 -0
  551. package/dist/internal/rewards/Runner.js.map +1 -0
  552. package/dist/internal/rewards/Signer.d.ts +224 -0
  553. package/dist/internal/rewards/Signer.d.ts.map +1 -0
  554. package/dist/internal/rewards/Signer.js +135 -0
  555. package/dist/internal/rewards/Signer.js.map +1 -0
  556. package/dist/internal/rewards/index.d.ts +15 -0
  557. package/dist/internal/rewards/index.d.ts.map +1 -0
  558. package/dist/internal/rewards/index.js +15 -0
  559. package/dist/internal/rewards/index.js.map +1 -0
  560. package/dist/internal/{funding → routes}/Action.d.ts +12 -1
  561. package/dist/internal/routes/Action.d.ts.map +1 -0
  562. package/dist/internal/{funding → routes}/Action.js +17 -9
  563. package/dist/internal/routes/Action.js.map +1 -0
  564. package/dist/internal/{funding → routes}/Catalog.d.ts +20 -20
  565. package/dist/internal/routes/Catalog.d.ts.map +1 -0
  566. package/dist/internal/{funding → routes}/Catalog.js +29 -29
  567. package/dist/internal/routes/Catalog.js.map +1 -0
  568. package/dist/internal/{funding → routes}/Chain.d.ts +10 -10
  569. package/dist/internal/routes/Chain.d.ts.map +1 -0
  570. package/dist/internal/{funding → routes}/Chain.js +3 -3
  571. package/dist/internal/routes/Chain.js.map +1 -0
  572. package/dist/internal/{funding → routes}/Deposit.d.ts +45 -23
  573. package/dist/internal/routes/Deposit.d.ts.map +1 -0
  574. package/dist/internal/{funding → routes}/Deposit.js +60 -27
  575. package/dist/internal/routes/Deposit.js.map +1 -0
  576. package/dist/internal/{funding → routes}/DepositAddress.d.ts +56 -27
  577. package/dist/internal/routes/DepositAddress.d.ts.map +1 -0
  578. package/dist/internal/{funding → routes}/DepositAddress.js +95 -33
  579. package/dist/internal/routes/DepositAddress.js.map +1 -0
  580. package/dist/internal/{funding → routes}/DepositWebhook.d.ts +4 -4
  581. package/dist/internal/routes/DepositWebhook.d.ts.map +1 -0
  582. package/dist/internal/{funding → routes}/DepositWebhook.js +4 -4
  583. package/dist/internal/routes/DepositWebhook.js.map +1 -0
  584. package/dist/internal/routes/Economics.d.ts +25 -0
  585. package/dist/internal/routes/Economics.d.ts.map +1 -0
  586. package/dist/internal/routes/Economics.js +28 -0
  587. package/dist/internal/routes/Economics.js.map +1 -0
  588. package/dist/internal/{funding → routes}/Evidence.d.ts +3 -3
  589. package/dist/internal/routes/Evidence.d.ts.map +1 -0
  590. package/dist/internal/{funding → routes}/Evidence.js +7 -7
  591. package/dist/internal/routes/Evidence.js.map +1 -0
  592. package/dist/internal/routes/Observability.d.ts +154 -0
  593. package/dist/internal/routes/Observability.d.ts.map +1 -0
  594. package/dist/internal/routes/Observability.js +389 -0
  595. package/dist/internal/routes/Observability.js.map +1 -0
  596. package/dist/internal/{funding → routes}/Provider.d.ts +311 -105
  597. package/dist/internal/routes/Provider.d.ts.map +1 -0
  598. package/dist/internal/{funding → routes}/Provider.js +229 -98
  599. package/dist/internal/routes/Provider.js.map +1 -0
  600. package/dist/internal/{funding → routes}/Reconciliation.d.ts +11 -6
  601. package/dist/internal/routes/Reconciliation.d.ts.map +1 -0
  602. package/dist/internal/{funding → routes}/Reconciliation.js +408 -158
  603. package/dist/internal/routes/Reconciliation.js.map +1 -0
  604. package/dist/internal/routes/Route.d.ts +21 -0
  605. package/dist/internal/routes/Route.d.ts.map +1 -0
  606. package/dist/internal/{funding → routes}/Route.js +1 -1
  607. package/dist/internal/routes/Route.js.map +1 -0
  608. package/dist/internal/{funding → routes}/SourceObservation.d.ts +6 -6
  609. package/dist/internal/routes/SourceObservation.d.ts.map +1 -0
  610. package/dist/internal/{funding → routes}/SourceObservation.js +8 -8
  611. package/dist/internal/routes/SourceObservation.js.map +1 -0
  612. package/dist/internal/{funding → routes}/Subsidy.d.ts +48 -3
  613. package/dist/internal/routes/Subsidy.d.ts.map +1 -0
  614. package/dist/internal/{funding → routes}/Subsidy.js +43 -3
  615. package/dist/internal/routes/Subsidy.js.map +1 -0
  616. package/dist/internal/{funding → routes}/Token.d.ts +4 -4
  617. package/dist/internal/routes/Token.d.ts.map +1 -0
  618. package/dist/internal/{funding → routes}/Token.js +3 -3
  619. package/dist/internal/routes/Token.js.map +1 -0
  620. package/dist/internal/{funding → routes}/Transfer.d.ts +241 -46
  621. package/dist/internal/routes/Transfer.d.ts.map +1 -0
  622. package/dist/internal/routes/Transfer.js +728 -0
  623. package/dist/internal/routes/Transfer.js.map +1 -0
  624. package/dist/internal/{funding → routes}/TransferReconciliation.d.ts +13 -10
  625. package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -0
  626. package/dist/internal/routes/TransferReconciliation.js +422 -0
  627. package/dist/internal/routes/TransferReconciliation.js.map +1 -0
  628. package/dist/internal/routes/TransferSubsidy.d.ts +201 -0
  629. package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -0
  630. package/dist/internal/routes/TransferSubsidy.js +256 -0
  631. package/dist/internal/routes/TransferSubsidy.js.map +1 -0
  632. package/dist/internal/{funding → routes}/TransferWebhook.d.ts +5 -5
  633. package/dist/internal/routes/TransferWebhook.d.ts.map +1 -0
  634. package/dist/internal/{funding → routes}/TransferWebhook.js +3 -3
  635. package/dist/internal/routes/TransferWebhook.js.map +1 -0
  636. package/dist/internal/{funding → routes}/index.d.ts +2 -0
  637. package/dist/internal/routes/index.d.ts.map +1 -0
  638. package/dist/internal/{funding → routes}/index.js +2 -0
  639. package/dist/internal/routes/index.js.map +1 -0
  640. package/dist/internal/{funding → routes}/providers/across.d.ts +4 -4
  641. package/dist/internal/routes/providers/across.d.ts.map +1 -0
  642. package/dist/internal/{funding → routes}/providers/across.js +13 -13
  643. package/dist/internal/routes/providers/across.js.map +1 -0
  644. package/dist/internal/{funding → routes}/providers/relay.d.ts +21 -12
  645. package/dist/internal/routes/providers/relay.d.ts.map +1 -0
  646. package/dist/internal/{funding → routes}/providers/relay.js +194 -110
  647. package/dist/internal/routes/providers/relay.js.map +1 -0
  648. package/dist/internal/{funding → routes}/providers/rhino.d.ts +4 -4
  649. package/dist/internal/routes/providers/rhino.d.ts.map +1 -0
  650. package/dist/internal/{funding → routes}/providers/rhino.js +10 -10
  651. package/dist/internal/routes/providers/rhino.js.map +1 -0
  652. package/dist/internal/{funding → routes}/providers/squid.d.ts +4 -4
  653. package/dist/internal/routes/providers/squid.d.ts.map +1 -0
  654. package/dist/internal/{funding → routes}/providers/squid.js +14 -14
  655. package/dist/internal/routes/providers/squid.js.map +1 -0
  656. package/dist/internal/{funding → routes}/providers/stargate.d.ts +46 -18
  657. package/dist/internal/routes/providers/stargate.d.ts.map +1 -0
  658. package/dist/internal/routes/providers/stargate.js +898 -0
  659. package/dist/internal/routes/providers/stargate.js.map +1 -0
  660. package/dist/internal/{funding → routes}/providers/symbiosis.d.ts +4 -4
  661. package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -0
  662. package/dist/internal/{funding → routes}/providers/symbiosis.js +30 -20
  663. package/dist/internal/routes/providers/symbiosis.js.map +1 -0
  664. package/dist/internal/webhookDestinations/slack.js +12 -12
  665. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  666. package/dist/providers/index.d.ts +12 -12
  667. package/dist/providers/index.d.ts.map +1 -1
  668. package/dist/providers/index.js +12 -12
  669. package/dist/providers/index.js.map +1 -1
  670. package/dist/server.d.ts +19 -0
  671. package/dist/server.d.ts.map +1 -0
  672. package/dist/server.js +19 -0
  673. package/dist/server.js.map +1 -0
  674. package/package.json +15 -4
  675. package/src/ApiKey.ts +1 -1
  676. package/src/ApiKeys.test.ts +1033 -3
  677. package/src/ApiKeys.ts +489 -48
  678. package/src/App.test-d.ts +1 -1
  679. package/src/App.test.ts +216 -25
  680. package/src/App.ts +334 -31
  681. package/src/Assets.test.ts +2 -2
  682. package/src/Assets.ts +3 -1
  683. package/src/Auth.test-d.ts +1 -1
  684. package/src/Client.test-d.ts +539 -110
  685. package/src/Handler.ts +0 -1
  686. package/src/OpenApi.contract.test.ts +195 -0
  687. package/src/Scope.ts +17 -5
  688. package/src/Webhooks.ts +1 -1
  689. package/src/admin/App.test.ts +30 -2
  690. package/src/admin/App.ts +42 -6
  691. package/src/admin/apps/api-keys.test.ts +267 -5
  692. package/src/admin/apps/api-keys.ts +100 -50
  693. package/src/admin/apps/earn-vaults.test.ts +158 -0
  694. package/src/admin/apps/earn-vaults.ts +679 -6
  695. package/src/admin/apps/organizations.test.ts +306 -17
  696. package/src/admin/apps/organizations.ts +299 -24
  697. package/src/admin/apps/routes-support.test.ts +200 -0
  698. package/src/admin/apps/routes-support.ts +201 -0
  699. package/src/admin/apps/routes.test.ts +450 -0
  700. package/src/admin/apps/routes.ts +896 -0
  701. package/src/admin/apps/scopes.test.ts +15 -5
  702. package/src/admin/apps/subsidies.test.ts +306 -0
  703. package/src/admin/apps/subsidies.ts +457 -0
  704. package/src/admin/apps/verified-tokens.test.ts +8 -3
  705. package/src/admin/apps/verified-tokens.ts +3 -3
  706. package/src/admin/ui/plugin.ts +1 -2
  707. package/src/admin/ui/src/components/RewardCampaignForm.tsx +1009 -0
  708. package/src/admin/ui/src/lib/config.ts +11 -2
  709. package/src/admin/ui/src/lib/earn-vaults.ts +2 -2
  710. package/src/admin/ui/src/lib/organizations.test.ts +83 -50
  711. package/src/admin/ui/src/lib/organizations.ts +106 -25
  712. package/src/admin/ui/src/lib/query-client.ts +1 -1
  713. package/src/admin/ui/src/lib/reward-campaigns.ts +136 -0
  714. package/src/admin/ui/src/lib/routes.test.ts +88 -0
  715. package/src/admin/ui/src/lib/routes.ts +304 -0
  716. package/src/admin/ui/src/lib/subsidies.ts +75 -0
  717. package/src/admin/ui/src/routeTree.gen.ts +63 -0
  718. package/src/admin/ui/src/routes/__root.tsx +12 -1
  719. package/src/admin/ui/src/routes/api-keys.tsx +196 -84
  720. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +105 -70
  721. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +239 -23
  722. package/src/admin/ui/src/routes/routes.tsx +1615 -0
  723. package/src/admin/ui/src/routes/routes_.support.tsx +248 -0
  724. package/src/admin/ui/src/routes/subsidies.tsx +799 -0
  725. package/src/admin/ui.generated.ts +144 -69
  726. package/src/analytics/Analytics.test.ts +108 -18
  727. package/src/analytics/Analytics.ts +38 -6
  728. package/src/analytics/tables/requestEvents.test.ts +100 -0
  729. package/src/analytics/tables/requestEvents.ts +10 -1
  730. package/src/analytics/tables/routesLifecycleIntervals.ts +140 -0
  731. package/src/apps/Zones.test.ts +15 -0
  732. package/src/apps/Zones.ts +52 -5
  733. package/src/apps/composition.test.ts +68 -60
  734. package/src/apps/data/App.test.ts +154 -2
  735. package/src/apps/data/App.ts +88 -28
  736. package/src/apps/data/routes/activities.test.ts +419 -7
  737. package/src/apps/data/routes/activities.ts +683 -216
  738. package/src/apps/data/routes/balances.test.ts +2 -2
  739. package/src/apps/data/routes/blocks.test.ts +46 -1
  740. package/src/apps/data/routes/coingecko.test.ts +419 -1
  741. package/src/apps/data/routes/coingecko.ts +309 -253
  742. package/src/apps/data/routes/earn.discovery.test.ts +274 -0
  743. package/src/apps/data/routes/earn.test.ts +1294 -17
  744. package/src/apps/data/routes/earn.ts +1838 -321
  745. package/src/apps/data/routes/exchanges.test.ts +12 -15
  746. package/src/apps/data/routes/fee-amm.test.ts +224 -0
  747. package/src/apps/data/routes/fee-amm.ts +191 -160
  748. package/src/apps/data/routes/indexer.test.ts +389 -0
  749. package/src/apps/data/routes/indexer.ts +165 -69
  750. package/src/apps/data/routes/receipts.test.ts +1 -1
  751. package/src/apps/data/routes/rpc.test.ts +352 -8
  752. package/src/apps/data/routes/rpc.ts +407 -219
  753. package/src/apps/data/routes/rpc.zone.test.ts +340 -0
  754. package/src/apps/data/routes/tokenlist.test.ts +285 -0
  755. package/src/apps/data/routes/tokenlist.ts +173 -88
  756. package/src/apps/data/routes/tokens.test.ts +93 -3
  757. package/src/apps/data/routes/tokens.ts +39 -23
  758. package/src/apps/data/routes/transactions.test.ts +1 -1
  759. package/src/apps/data/routes/transfers.test.ts +56 -24
  760. package/src/apps/data/routes/transfers.ts +19 -8
  761. package/src/apps/data/routes/valuation.test.ts +1 -1
  762. package/src/apps/data/routes/verified-tokens.test.ts +345 -0
  763. package/src/apps/data/routes/verified-tokens.ts +99 -29
  764. package/src/apps/data/routes/webhooks.test.ts +645 -83
  765. package/src/apps/data/routes/webhooks.ts +1089 -857
  766. package/src/apps/data/routes/zones.test.ts +148 -71
  767. package/src/apps/data/routes/zones.ts +71 -55
  768. package/src/apps/docs/reference.ts +2 -2
  769. package/src/apps/index.ts +1 -1
  770. package/src/apps/management/App.ts +9 -51
  771. package/src/apps/management/Billing.test.ts +375 -14
  772. package/src/apps/management/Billing.ts +265 -29
  773. package/src/apps/management/routes/api-keys.test.ts +269 -7
  774. package/src/apps/management/routes/api-keys.ts +61 -20
  775. package/src/apps/management/routes/billing.test.ts +477 -15
  776. package/src/apps/management/routes/billing.ts +377 -50
  777. package/src/apps/management/routes/faucet.test.ts +1 -1
  778. package/src/apps/management/routes/faucet.ts +1 -1
  779. package/src/apps/management/routes/invitations.test.ts +270 -3
  780. package/src/apps/management/routes/invitations.ts +185 -15
  781. package/src/apps/management/routes/invoicePreview.test.ts +549 -0
  782. package/src/apps/management/routes/me.test.ts +246 -5
  783. package/src/apps/management/routes/me.ts +7 -2
  784. package/src/apps/management/routes/members.test.ts +108 -0
  785. package/src/apps/management/routes/orgs.test.ts +191 -3
  786. package/src/apps/management/routes/orgs.ts +58 -11
  787. package/src/apps/management/routes/projects.test.ts +33 -3
  788. package/src/apps/management/routes/projects.ts +6 -1
  789. package/src/apps/management/routes/scopes.test.ts +30 -2
  790. package/src/apps/management/routes/scopes.ts +4 -1
  791. package/src/apps/management/routes/usage.test.ts +32 -0
  792. package/src/apps/management/routes/usage.ts +22 -4
  793. package/src/apps/management/routes/webhooks.test.ts +49 -26
  794. package/src/apps/management/routes/webhooks.ts +78 -22
  795. package/src/apps/mcp.test.ts +6 -6
  796. package/src/apps/mcp.ts +7 -7
  797. package/src/apps/mpp/App.ts +37 -7
  798. package/src/apps/mpp/Mpp.test.ts +49 -8
  799. package/src/apps/mpp/Mpp.zone.test.ts +219 -0
  800. package/src/apps/mpp/internal/Adapter.ts +34 -0
  801. package/src/apps/mpp/internal/Policy.test.ts +86 -31
  802. package/src/apps/mpp/internal/Policy.ts +49 -7
  803. package/src/apps/mpp/internal/Sponsorship.test.ts +207 -0
  804. package/src/apps/mpp/internal/Sponsorship.ts +9 -3
  805. package/src/apps/relay/App.test.ts +839 -64
  806. package/src/apps/relay/App.ts +148 -81
  807. package/src/apps/relay/App.zone-billing.test.ts +257 -0
  808. package/src/apps/relay/Sponsorships.test.ts +67 -4
  809. package/src/apps/relay/Sponsorships.ts +233 -32
  810. package/src/apps/relay/Sponsorships.zone.test.ts +102 -0
  811. package/src/apps/{funding → routes}/Access.ts +6 -6
  812. package/src/apps/routes/App.test.ts +165 -0
  813. package/src/apps/routes/App.ts +96 -0
  814. package/src/apps/{funding → routes}/Inventory.ts +2 -2
  815. package/src/apps/routes/index.ts +1 -0
  816. package/src/apps/{funding → routes}/routes/chains.test.ts +66 -66
  817. package/src/apps/{funding → routes}/routes/chains.ts +23 -23
  818. package/src/apps/{funding → routes}/routes/deposit-addresses.test.ts +1358 -458
  819. package/src/apps/{funding → routes}/routes/deposit-addresses.ts +566 -246
  820. package/src/apps/{funding → routes}/routes/providers.test.ts +27 -33
  821. package/src/apps/{funding → routes}/routes/providers.ts +23 -23
  822. package/src/apps/{funding → routes}/routes/quotes.test.ts +298 -41
  823. package/src/apps/{funding → routes}/routes/quotes.ts +57 -40
  824. package/src/apps/{funding → routes}/routes/transfers.test.ts +1100 -353
  825. package/src/apps/routes/routes/transfers.ts +1332 -0
  826. package/src/cli/Cli.test.ts +1 -1
  827. package/src/cli/commands/admin.test.ts +748 -1
  828. package/src/cli/commands/admin.ts +278 -0
  829. package/src/cloudflare.ts +52 -47
  830. package/src/db/Db.test.ts +245 -2
  831. package/src/db/Db.ts +1476 -34
  832. package/src/db/Schema.ts +729 -228
  833. package/src/db/tables/apiKeyAdmissionBootstraps.ts +4 -0
  834. package/src/db/tables/apiKeyAdmissions.ts +218 -0
  835. package/src/db/tables/apiKeyOwnerAdmissions.ts +4 -0
  836. package/src/db/tables/apiKeyOwnerTombstones.ts +81 -0
  837. package/src/db/tables/apiKeyRevocations.test.ts +74 -0
  838. package/src/db/tables/apiKeyRevocations.ts +121 -0
  839. package/src/db/tables/authAccounts.ts +105 -0
  840. package/src/db/tables/authSessions.ts +13 -0
  841. package/src/db/tables/authVerifications.ts +13 -0
  842. package/src/db/tables/invitations.ts +142 -45
  843. package/src/db/tables/memberships.ts +79 -3
  844. package/src/db/tables/organizations.test.ts +690 -13
  845. package/src/db/tables/organizations.ts +139 -55
  846. package/src/db/tables/projects.test.ts +16 -0
  847. package/src/db/tables/projects.ts +13 -3
  848. package/src/db/tables/requestUsage.ts +4 -1
  849. package/src/db/tables/rewardAccounts.ts +178 -0
  850. package/src/db/tables/rewardCampaignDistributors.ts +68 -0
  851. package/src/db/tables/rewardCampaigns.test.ts +1388 -0
  852. package/src/db/tables/rewardCampaigns.ts +647 -0
  853. package/src/db/tables/rewardCredits.ts +110 -0
  854. package/src/db/tables/rewardEligibilityAssociations.test.ts +271 -0
  855. package/src/db/tables/rewardEligibilityAssociations.ts +184 -0
  856. package/src/db/tables/rewardRuns.ts +514 -0
  857. package/src/db/tables/rewardTransactionAttempts.ts +272 -0
  858. package/src/db/tables/routesCatalog.test.ts +185 -0
  859. package/src/db/tables/routesCatalog.ts +163 -0
  860. package/src/db/tables/routesDepositAddresses.test.ts +1048 -0
  861. package/src/db/tables/routesDepositAddresses.ts +1008 -0
  862. package/src/db/tables/{fundingDepositRequestObservations.test.ts → routesDepositRequestObservations.test.ts} +13 -14
  863. package/src/db/tables/{fundingDepositRequestObservations.ts → routesDepositRequestObservations.ts} +10 -10
  864. package/src/db/tables/{fundingDeposits.test.ts → routesDeposits.test.ts} +316 -46
  865. package/src/db/tables/routesDeposits.ts +1062 -0
  866. package/src/db/tables/routesIdempotency.test.ts +796 -0
  867. package/src/db/tables/routesIdempotency.ts +496 -0
  868. package/src/db/tables/routesSubsidies.test.ts +120 -0
  869. package/src/db/tables/routesSubsidies.ts +71 -0
  870. package/src/db/tables/{fundingTransferEvents.ts → routesTransferEvents.ts} +4 -4
  871. package/src/db/tables/routesTransferSubsidies.ts +38 -0
  872. package/src/db/tables/routesTransferSubsidyNonces.ts +124 -0
  873. package/src/db/tables/routesTransferTransactions.test.ts +195 -0
  874. package/src/db/tables/{fundingTransferTransactions.ts → routesTransferTransactions.ts} +63 -18
  875. package/src/db/tables/routesTransfers.test.ts +338 -0
  876. package/src/db/tables/routesTransfers.ts +662 -0
  877. package/src/db/tables/sponsoredTransactions.test.ts +269 -45
  878. package/src/db/tables/sponsoredTransactions.ts +225 -65
  879. package/src/db/tables/sponsorshipAttributions.test.ts +160 -0
  880. package/src/db/tables/sponsorshipAttributions.ts +168 -0
  881. package/src/db/tables/sponsorshipReconciliationCursors.test.ts +115 -0
  882. package/src/db/tables/sponsorshipReconciliationCursors.ts +67 -0
  883. package/src/db/tables/stripeCustomers.test.ts +85 -0
  884. package/src/db/tables/stripeCustomers.ts +24 -0
  885. package/src/db/tables/users.test.ts +129 -0
  886. package/src/db/tables/users.ts +49 -3
  887. package/src/db/tables/webhookQueueEvents.ts +8 -1
  888. package/src/db/tables/webhookSubscriptions.ts +95 -20
  889. package/src/handlers/internal/executionError.ts +3 -4
  890. package/src/handlers/internal/multisig.ts +13 -816
  891. package/src/handlers/internal/sponsorship.ts +26 -10
  892. package/src/handlers/relay.test-d.ts +9 -1
  893. package/src/handlers/relay.test.ts +823 -3
  894. package/src/handlers/relay.ts +127 -107
  895. package/src/index.test-d.ts +7 -0
  896. package/src/index.ts +0 -17
  897. package/src/internal/AssetResponse.ts +26 -0
  898. package/src/internal/Auth.test.ts +1564 -9
  899. package/src/internal/Auth.ts +833 -239
  900. package/src/internal/BetterAuth.test.ts +242 -0
  901. package/src/internal/BetterAuth.ts +349 -0
  902. package/src/internal/Cache.ts +2 -0
  903. package/src/internal/EarnRates.test.ts +17 -0
  904. package/src/internal/EarnRates.ts +124 -31
  905. package/src/internal/EarnSharePrices.test.ts +182 -0
  906. package/src/internal/EarnSharePrices.ts +313 -0
  907. package/src/internal/EarnVaults.test.ts +21 -0
  908. package/src/internal/EarnVaults.ts +33 -11
  909. package/src/internal/Email.ts +54 -0
  910. package/src/internal/Idempotency.test.ts +2 -2
  911. package/src/internal/Log.test-d.ts +8 -7
  912. package/src/internal/Log.test.ts +114 -16
  913. package/src/internal/Log.ts +53 -40
  914. package/src/internal/MetricSink.test.ts +313 -26
  915. package/src/internal/MetricSink.ts +73 -8
  916. package/src/internal/Mpp.test.ts +31 -0
  917. package/src/internal/Mpp.ts +21 -24
  918. package/src/internal/OpenApi.ts +45 -12
  919. package/src/internal/Provider.test.ts +1 -1
  920. package/src/internal/Provider.ts +1 -1
  921. package/src/internal/RateLimit.test.ts +21 -3
  922. package/src/internal/RateLimit.ts +12 -1
  923. package/src/internal/Store.test-d.ts +1 -1
  924. package/src/internal/Store.test.ts +151 -8
  925. package/src/internal/Store.ts +76 -9
  926. package/src/internal/Tidx.test.ts +59 -8
  927. package/src/internal/Tidx.ts +86 -14
  928. package/src/internal/Timing.test.ts +77 -0
  929. package/src/internal/Timing.ts +83 -6
  930. package/src/internal/VercelOidc.test.ts +188 -0
  931. package/src/internal/VercelOidc.ts +193 -0
  932. package/src/internal/VerifiedTokens.test.ts +60 -6
  933. package/src/internal/VerifiedTokens.ts +21 -24
  934. package/src/internal/Viem.batch.test.ts +80 -0
  935. package/src/internal/Viem.test.ts +201 -2
  936. package/src/internal/Viem.ts +221 -25
  937. package/src/internal/Webhooks.test.ts +85 -6
  938. package/src/internal/Webhooks.ts +22 -4
  939. package/src/internal/ZoneRpc.ts +50 -0
  940. package/src/internal/exchange/Provider.test.ts +2 -2
  941. package/src/internal/index.test.ts +3 -1
  942. package/src/internal/index.ts +3 -1
  943. package/src/internal/rewards/Audit.ts +231 -0
  944. package/src/internal/rewards/Campaigns.test.ts +1234 -0
  945. package/src/internal/rewards/Campaigns.ts +1205 -0
  946. package/src/internal/rewards/Credits.ts +165 -0
  947. package/src/internal/rewards/Events.local.test.ts +113 -0
  948. package/src/internal/rewards/Events.test.ts +152 -0
  949. package/src/internal/rewards/Events.ts +468 -0
  950. package/src/internal/rewards/Projection.test.ts +681 -0
  951. package/src/internal/rewards/Projection.ts +987 -0
  952. package/src/internal/rewards/Rates.test.ts +171 -0
  953. package/src/internal/rewards/Rates.ts +110 -0
  954. package/src/internal/rewards/Read.test.ts +501 -0
  955. package/src/internal/rewards/Read.ts +476 -0
  956. package/src/internal/rewards/Runner.checkpoint.test.ts +80 -0
  957. package/src/internal/rewards/Runner.states.test.ts +188 -0
  958. package/src/internal/rewards/Runner.test.ts +90 -0
  959. package/src/internal/rewards/Runner.ts +2229 -0
  960. package/src/internal/rewards/Signer.test.ts +79 -0
  961. package/src/internal/rewards/Signer.ts +180 -0
  962. package/src/internal/rewards/index.ts +14 -0
  963. package/src/internal/{funding → routes}/Action.test.ts +63 -14
  964. package/src/internal/{funding → routes}/Action.ts +35 -8
  965. package/src/internal/{funding → routes}/Catalog.test.ts +3 -3
  966. package/src/internal/{funding → routes}/Catalog.ts +39 -39
  967. package/src/internal/{funding → routes}/Chain.test.ts +1 -1
  968. package/src/internal/{funding → routes}/Chain.ts +11 -11
  969. package/src/internal/{funding → routes}/Deposit.test.ts +1 -1
  970. package/src/internal/{funding → routes}/Deposit.ts +93 -45
  971. package/src/internal/{funding → routes}/DepositAddress.test.ts +11 -3
  972. package/src/internal/{funding → routes}/DepositAddress.ts +132 -53
  973. package/src/internal/{funding → routes}/DepositWebhook.test.ts +61 -18
  974. package/src/internal/{funding → routes}/DepositWebhook.ts +9 -9
  975. package/src/internal/routes/Economics.test.ts +42 -0
  976. package/src/internal/routes/Economics.ts +47 -0
  977. package/src/internal/{funding → routes}/Evidence.test.ts +6 -6
  978. package/src/internal/{funding → routes}/Evidence.ts +9 -9
  979. package/src/internal/routes/Observability.test.ts +636 -0
  980. package/src/internal/routes/Observability.ts +668 -0
  981. package/src/internal/{funding → routes}/Provider.test.ts +489 -153
  982. package/src/internal/{funding → routes}/Provider.ts +489 -186
  983. package/src/internal/{funding → routes}/Reconciliation.local.test.ts +84 -12
  984. package/src/internal/{funding → routes}/Reconciliation.test.ts +1213 -279
  985. package/src/internal/{funding → routes}/Reconciliation.ts +501 -203
  986. package/src/internal/routes/Reconciliation.unattributed.test.ts +250 -0
  987. package/src/internal/{funding → routes}/Route.ts +7 -7
  988. package/src/internal/{funding → routes}/SourceObservation.test.ts +13 -14
  989. package/src/internal/{funding → routes}/SourceObservation.ts +13 -13
  990. package/src/internal/{funding → routes}/Subsidy.local.test.ts +7 -7
  991. package/src/internal/routes/Subsidy.test.ts +169 -0
  992. package/src/internal/{funding → routes}/Subsidy.ts +86 -6
  993. package/src/internal/{funding → routes}/Token.test.ts +1 -1
  994. package/src/internal/{funding → routes}/Token.ts +5 -5
  995. package/src/internal/routes/Transfer.test.ts +1199 -0
  996. package/src/internal/routes/Transfer.ts +1206 -0
  997. package/src/internal/routes/TransferReconciliation.test.ts +731 -0
  998. package/src/internal/routes/TransferReconciliation.ts +527 -0
  999. package/src/internal/routes/TransferSubsidy.test.ts +172 -0
  1000. package/src/internal/routes/TransferSubsidy.ts +447 -0
  1001. package/src/internal/{funding → routes}/TransferWebhook.test.ts +13 -13
  1002. package/src/internal/{funding → routes}/TransferWebhook.ts +7 -7
  1003. package/src/internal/{funding → routes}/index.test.ts +5 -3
  1004. package/src/internal/{funding → routes}/index.ts +2 -0
  1005. package/src/internal/{funding → routes}/providers/across.ts +16 -16
  1006. package/src/internal/{funding → routes}/providers/index.test.ts +157 -44
  1007. package/src/internal/{funding → routes}/providers/relay.test.ts +165 -32
  1008. package/src/internal/{funding → routes}/providers/relay.ts +228 -121
  1009. package/src/internal/{funding → routes}/providers/rhino.ts +16 -16
  1010. package/src/internal/{funding → routes}/providers/squid.ts +17 -17
  1011. package/src/internal/routes/providers/stargate.test.ts +728 -0
  1012. package/src/internal/routes/providers/stargate.ts +1276 -0
  1013. package/src/internal/{funding → routes}/providers/symbiosis.ts +64 -24
  1014. package/src/internal/webhookDestinations/slack.ts +10 -10
  1015. package/src/providers/index.ts +13 -13
  1016. package/src/server.ts +18 -0
  1017. package/dist/apps/funding/Access.d.ts.map +0 -1
  1018. package/dist/apps/funding/Access.js.map +0 -1
  1019. package/dist/apps/funding/App.js.map +0 -1
  1020. package/dist/apps/funding/Inventory.d.ts.map +0 -1
  1021. package/dist/apps/funding/Inventory.js.map +0 -1
  1022. package/dist/apps/funding/ProviderAttempts.d.ts +0 -5
  1023. package/dist/apps/funding/ProviderAttempts.d.ts.map +0 -1
  1024. package/dist/apps/funding/ProviderAttempts.js +0 -24
  1025. package/dist/apps/funding/ProviderAttempts.js.map +0 -1
  1026. package/dist/apps/funding/index.d.ts +0 -2
  1027. package/dist/apps/funding/index.d.ts.map +0 -1
  1028. package/dist/apps/funding/index.js +0 -2
  1029. package/dist/apps/funding/index.js.map +0 -1
  1030. package/dist/apps/funding/routes/chains.d.ts.map +0 -1
  1031. package/dist/apps/funding/routes/chains.js.map +0 -1
  1032. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +0 -1
  1033. package/dist/apps/funding/routes/deposit-addresses.js.map +0 -1
  1034. package/dist/apps/funding/routes/providers.d.ts.map +0 -1
  1035. package/dist/apps/funding/routes/providers.js +0 -57
  1036. package/dist/apps/funding/routes/providers.js.map +0 -1
  1037. package/dist/apps/funding/routes/quotes.d.ts.map +0 -1
  1038. package/dist/apps/funding/routes/quotes.js.map +0 -1
  1039. package/dist/apps/funding/routes/transfers.d.ts.map +0 -1
  1040. package/dist/apps/funding/routes/transfers.js +0 -828
  1041. package/dist/apps/funding/routes/transfers.js.map +0 -1
  1042. package/dist/db/tables/fundingCatalog.d.ts +0 -58
  1043. package/dist/db/tables/fundingCatalog.d.ts.map +0 -1
  1044. package/dist/db/tables/fundingCatalog.js.map +0 -1
  1045. package/dist/db/tables/fundingDepositAddresses.d.ts +0 -192
  1046. package/dist/db/tables/fundingDepositAddresses.d.ts.map +0 -1
  1047. package/dist/db/tables/fundingDepositAddresses.js +0 -235
  1048. package/dist/db/tables/fundingDepositAddresses.js.map +0 -1
  1049. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +0 -1
  1050. package/dist/db/tables/fundingDepositRequestObservations.js.map +0 -1
  1051. package/dist/db/tables/fundingDeposits.d.ts.map +0 -1
  1052. package/dist/db/tables/fundingDeposits.js +0 -295
  1053. package/dist/db/tables/fundingDeposits.js.map +0 -1
  1054. package/dist/db/tables/fundingIdempotency.d.ts +0 -91
  1055. package/dist/db/tables/fundingIdempotency.d.ts.map +0 -1
  1056. package/dist/db/tables/fundingIdempotency.js +0 -103
  1057. package/dist/db/tables/fundingIdempotency.js.map +0 -1
  1058. package/dist/db/tables/fundingTransferEvents.d.ts.map +0 -1
  1059. package/dist/db/tables/fundingTransferEvents.js.map +0 -1
  1060. package/dist/db/tables/fundingTransferTransactions.d.ts +0 -53
  1061. package/dist/db/tables/fundingTransferTransactions.d.ts.map +0 -1
  1062. package/dist/db/tables/fundingTransferTransactions.js.map +0 -1
  1063. package/dist/db/tables/fundingTransfers.d.ts +0 -99
  1064. package/dist/db/tables/fundingTransfers.d.ts.map +0 -1
  1065. package/dist/db/tables/fundingTransfers.js +0 -110
  1066. package/dist/db/tables/fundingTransfers.js.map +0 -1
  1067. package/dist/internal/funding/Action.d.ts.map +0 -1
  1068. package/dist/internal/funding/Action.js.map +0 -1
  1069. package/dist/internal/funding/Catalog.d.ts.map +0 -1
  1070. package/dist/internal/funding/Catalog.js.map +0 -1
  1071. package/dist/internal/funding/Chain.d.ts.map +0 -1
  1072. package/dist/internal/funding/Chain.js.map +0 -1
  1073. package/dist/internal/funding/Deposit.d.ts.map +0 -1
  1074. package/dist/internal/funding/Deposit.js.map +0 -1
  1075. package/dist/internal/funding/DepositAddress.d.ts.map +0 -1
  1076. package/dist/internal/funding/DepositAddress.js.map +0 -1
  1077. package/dist/internal/funding/DepositWebhook.d.ts.map +0 -1
  1078. package/dist/internal/funding/DepositWebhook.js.map +0 -1
  1079. package/dist/internal/funding/Evidence.d.ts.map +0 -1
  1080. package/dist/internal/funding/Evidence.js.map +0 -1
  1081. package/dist/internal/funding/Provider.d.ts.map +0 -1
  1082. package/dist/internal/funding/Provider.js.map +0 -1
  1083. package/dist/internal/funding/Reconciliation.d.ts.map +0 -1
  1084. package/dist/internal/funding/Reconciliation.js.map +0 -1
  1085. package/dist/internal/funding/Route.d.ts +0 -21
  1086. package/dist/internal/funding/Route.d.ts.map +0 -1
  1087. package/dist/internal/funding/Route.js.map +0 -1
  1088. package/dist/internal/funding/SourceObservation.d.ts.map +0 -1
  1089. package/dist/internal/funding/SourceObservation.js.map +0 -1
  1090. package/dist/internal/funding/Subsidy.d.ts.map +0 -1
  1091. package/dist/internal/funding/Subsidy.js.map +0 -1
  1092. package/dist/internal/funding/Token.d.ts.map +0 -1
  1093. package/dist/internal/funding/Token.js.map +0 -1
  1094. package/dist/internal/funding/Transfer.d.ts.map +0 -1
  1095. package/dist/internal/funding/Transfer.js +0 -462
  1096. package/dist/internal/funding/Transfer.js.map +0 -1
  1097. package/dist/internal/funding/TransferReconciliation.d.ts.map +0 -1
  1098. package/dist/internal/funding/TransferReconciliation.js +0 -192
  1099. package/dist/internal/funding/TransferReconciliation.js.map +0 -1
  1100. package/dist/internal/funding/TransferWebhook.d.ts.map +0 -1
  1101. package/dist/internal/funding/TransferWebhook.js.map +0 -1
  1102. package/dist/internal/funding/index.d.ts.map +0 -1
  1103. package/dist/internal/funding/index.js.map +0 -1
  1104. package/dist/internal/funding/providers/across.d.ts.map +0 -1
  1105. package/dist/internal/funding/providers/across.js.map +0 -1
  1106. package/dist/internal/funding/providers/relay.d.ts.map +0 -1
  1107. package/dist/internal/funding/providers/relay.js.map +0 -1
  1108. package/dist/internal/funding/providers/rhino.d.ts.map +0 -1
  1109. package/dist/internal/funding/providers/rhino.js.map +0 -1
  1110. package/dist/internal/funding/providers/squid.d.ts.map +0 -1
  1111. package/dist/internal/funding/providers/squid.js.map +0 -1
  1112. package/dist/internal/funding/providers/stargate.d.ts.map +0 -1
  1113. package/dist/internal/funding/providers/stargate.js +0 -579
  1114. package/dist/internal/funding/providers/stargate.js.map +0 -1
  1115. package/dist/internal/funding/providers/symbiosis.d.ts.map +0 -1
  1116. package/dist/internal/funding/providers/symbiosis.js.map +0 -1
  1117. package/src/apps/funding/App.test.ts +0 -78
  1118. package/src/apps/funding/App.ts +0 -88
  1119. package/src/apps/funding/ProviderAttempts.ts +0 -26
  1120. package/src/apps/funding/index.ts +0 -1
  1121. package/src/apps/funding/routes/transfers.ts +0 -987
  1122. package/src/db/tables/fundingCatalog.test.ts +0 -122
  1123. package/src/db/tables/fundingCatalog.ts +0 -163
  1124. package/src/db/tables/fundingDepositAddresses.test.ts +0 -608
  1125. package/src/db/tables/fundingDepositAddresses.ts +0 -443
  1126. package/src/db/tables/fundingDeposits.ts +0 -572
  1127. package/src/db/tables/fundingIdempotency.test.ts +0 -182
  1128. package/src/db/tables/fundingIdempotency.ts +0 -180
  1129. package/src/db/tables/fundingTransferTransactions.test.ts +0 -112
  1130. package/src/db/tables/fundingTransfers.test.ts +0 -115
  1131. package/src/db/tables/fundingTransfers.ts +0 -189
  1132. package/src/handlers/internal/multisig.test.ts +0 -818
  1133. package/src/internal/funding/Subsidy.test.ts +0 -75
  1134. package/src/internal/funding/Transfer.test.ts +0 -524
  1135. package/src/internal/funding/Transfer.ts +0 -762
  1136. package/src/internal/funding/TransferReconciliation.test.ts +0 -147
  1137. package/src/internal/funding/TransferReconciliation.ts +0 -264
  1138. package/src/internal/funding/providers/stargate.test.ts +0 -328
  1139. package/src/internal/funding/providers/stargate.ts +0 -856
  1140. /package/dist/apps/{funding → routes}/Inventory.d.ts +0 -0
  1141. /package/src/internal/{funding → routes}/Route.test.ts +0 -0
@@ -1,23 +1,30 @@
1
- import { Value } from 'ox'
1
+ import { AbiFunction, Hex, Value } from 'ox'
2
2
  import { decodeFunctionData, erc20Abi, maxUint256 } from 'viem'
3
3
  import { base } from 'viem/chains'
4
4
  import { tempoMainnet } from 'viem/tempo/chains'
5
5
 
6
6
  import * as Anvil from '../../../../test/Anvil.js'
7
7
  import * as TestApp from '../../../../test/App.js'
8
- import * as TestFunding from '../../../../test/Funding.js'
9
- import * as FundingIdempotencyTable from '../../../db/tables/fundingIdempotency.js'
8
+ import * as TestRoutes from '../../../../test/Routes.js'
9
+ import * as RoutesIdempotencyTable from '../../../db/tables/routesIdempotency.js'
10
+ import * as Organizations from '../../../db/tables/organizations.js'
11
+ import * as RoutesSubsidies from '../../../db/tables/routesSubsidies.js'
12
+ import * as StripeCustomers from '../../../db/tables/stripeCustomers.js'
10
13
  import * as Cache from '../../../internal/Cache.js'
11
- import * as FundingCatalog from '../../../internal/funding/Catalog.js'
12
- import * as FundingChain from '../../../internal/funding/Chain.js'
13
- import * as FundingProvider from '../../../internal/funding/Provider.js'
14
- import * as FundingTransfer from '../../../internal/funding/Transfer.js'
15
- import * as FundingTransferReconciliation from '../../../internal/funding/TransferReconciliation.js'
14
+ import type * as RoutesAction from '../../../internal/routes/Action.js'
15
+ import * as RoutesCatalog from '../../../internal/routes/Catalog.js'
16
+ import * as RoutesChain from '../../../internal/routes/Chain.js'
17
+ import type * as RoutesObservability from '../../../internal/routes/Observability.js'
18
+ import * as RoutesProvider from '../../../internal/routes/Provider.js'
19
+ import * as RoutesTransfer from '../../../internal/routes/Transfer.js'
20
+ import * as RoutesTransferReconciliation from '../../../internal/routes/TransferReconciliation.js'
21
+ import * as RoutesTransferSubsidy from '../../../internal/routes/TransferSubsidy.js'
16
22
  import * as Idempotency from '../../../internal/Idempotency.js'
17
- import * as Relay from '../../../internal/funding/providers/relay.js'
18
- import * as Stargate from '../../../internal/funding/providers/stargate.js'
23
+ import type * as Log from '../../../internal/Log.js'
24
+ import * as Relay from '../../../internal/routes/providers/relay.js'
25
+ import * as Stargate from '../../../internal/routes/providers/stargate.js'
19
26
  import * as Webhooks from '../../../internal/Webhooks.js'
20
- import * as Funding from './transfers.js'
27
+ import * as Routes from './transfers.js'
21
28
 
22
29
  const db = TestApp.database()
23
30
  const destinationTransactionHash =
@@ -37,14 +44,14 @@ const executableProviders = [
37
44
  id: 'stargate',
38
45
  },
39
46
  ] as const
40
- const stargateConfiguration = TestFunding.snapshot.routesByProvider
47
+ const stargateConfiguration = TestRoutes.snapshot.routesByProvider
41
48
  .get('stargate')!
42
49
  .find((candidate) => candidate.route.source.chain.id === 'eip155:8453')!.configuration!
43
50
 
44
- beforeAll(() => TestFunding.publish(db))
51
+ beforeAll(() => TestRoutes.publish(db))
45
52
 
46
53
  function create(options: TestApp.create.Options = {}) {
47
- return TestApp.create({ ...options, db })
54
+ return TestApp.create({ ...options, db: options.db ?? db })
48
55
  }
49
56
 
50
57
  type ContractResponse = {
@@ -55,6 +62,7 @@ type ContractResponse = {
55
62
  type JsonSchema = {
56
63
  $ref?: string | undefined
57
64
  additionalProperties?: boolean | undefined
65
+ description?: string | undefined
58
66
  examples?: readonly unknown[] | undefined
59
67
  properties?: Record<string, JsonSchema> | undefined
60
68
  required?: readonly string[] | undefined
@@ -70,11 +78,11 @@ function dereference(spec: ComponentSpec, schema: JsonSchema): JsonSchema {
70
78
  return spec.components.schemas[name]!
71
79
  }
72
80
 
73
- describe('GET /funding/transfers', () => {
74
- test('requires the funding:read scope', async () => {
81
+ describe('GET /routes/transfers', () => {
82
+ test('requires the routes:read scope', async () => {
75
83
  const app = transfersApp()
76
- expect((await app.request('/v1/funding/transfers')).status).toBe(401)
77
- const forbidden = await app.request('/v1/funding/transfers', as(TestApp.key))
84
+ expect((await app.request('/v1/routes/transfers')).status).toBe(401)
85
+ const forbidden = await app.request('/v1/routes/transfers', as(TestApp.key))
78
86
  expect(forbidden.status).toBe(403)
79
87
  })
80
88
 
@@ -83,10 +91,10 @@ describe('GET /funding/transfers', () => {
83
91
  const first = await seedTransfer()
84
92
  const second = await seedTransfer()
85
93
  const third = await seedTransfer()
86
- await seedTransfer({ orgId: fundingForeign.orgId })
94
+ await seedTransfer({ orgId: routesForeign.orgId })
87
95
 
88
- const response = await app.request('/v1/funding/transfers?limit=5', {
89
- headers: { 'tempo-api-key': fundingReader.token },
96
+ const response = await app.request('/v1/routes/transfers?limit=5', {
97
+ headers: { 'tempo-api-key': routesReader.token },
90
98
  })
91
99
  expect(response.status).toBe(200)
92
100
  expect(response.headers.get('cache-control')).toBe(Cache.policies.feed)
@@ -95,13 +103,13 @@ describe('GET /funding/transfers', () => {
95
103
  expect(response.headers.get('vary')).toBe(
96
104
  'Accept-Encoding, Authorization, Tempo-API-Key, X-API-Key',
97
105
  )
98
- const body = await TestApp.json(response, Funding.schema.listFundingTransfers.Response)
106
+ const body = await TestApp.json(response, Routes.schema.listRoutesTransfers.Response)
99
107
  expect(body.data.map((transfer) => transfer.id)).toEqual([third.id, second.id, first.id])
100
108
  expect(body.nextCursor).toBeNull()
101
109
  expect(body.meta).toBeUndefined()
102
110
 
103
- const unchanged = await app.request('/v1/funding/transfers?limit=5', {
104
- headers: { 'if-none-match': etag, 'tempo-api-key': fundingReader.token },
111
+ const unchanged = await app.request('/v1/routes/transfers?limit=5', {
112
+ headers: { 'if-none-match': etag, 'tempo-api-key': routesReader.token },
105
113
  })
106
114
  expect(unchanged.status).toBe(304)
107
115
  expect(unchanged.headers.get('cache-control')).toBe(Cache.policies.feed)
@@ -110,8 +118,8 @@ describe('GET /funding/transfers', () => {
110
118
  )
111
119
 
112
120
  const paged = await TestApp.json(
113
- await app.request(`/v1/funding/transfers?limit=5&cursor=${second.id}`, as(fundingReader)),
114
- Funding.schema.listFundingTransfers.Response,
121
+ await app.request(`/v1/routes/transfers?limit=5&cursor=${second.id}`, as(routesReader)),
122
+ Routes.schema.listRoutesTransfers.Response,
115
123
  )
116
124
  expect(paged.data.map((transfer) => transfer.id)).toEqual([first.id])
117
125
  })
@@ -119,17 +127,17 @@ describe('GET /funding/transfers', () => {
119
127
  test('filters by status and embeds the capped count on request', async () => {
120
128
  const app = transfersApp()
121
129
  const expired = await seedTransfer()
122
- await FundingTransfer.transition(db, {
130
+ await RoutesTransfer.transition(db, {
123
131
  expectedVersion: 1,
124
132
  id: expired.id,
125
133
  status: 'expired',
126
134
  })
127
135
 
128
136
  const response = await app.request(
129
- '/v1/funding/transfers?status=expired&include=totalCount',
130
- as(fundingReader),
137
+ '/v1/routes/transfers?status=expired&include=totalCount',
138
+ as(routesReader),
131
139
  )
132
- const body = await TestApp.json(response, Funding.schema.listFundingTransfers.Response)
140
+ const body = await TestApp.json(response, Routes.schema.listRoutesTransfers.Response)
133
141
  expect(body.data.map((transfer) => transfer.id)).toEqual([expired.id])
134
142
  expect(body.data[0]?.status).toBe('expired')
135
143
  expect(body.meta).toEqual({ totalCount: 1, totalCountCapped: false })
@@ -137,35 +145,35 @@ describe('GET /funding/transfers', () => {
137
145
 
138
146
  test('narrows project-attributed keys to their project', async () => {
139
147
  const app = transfersApp()
140
- const scoped = await seedTransfer({ projectId: fundingProjectReader.projectId })
148
+ const scoped = await seedTransfer({ projectId: routesProjectReader.projectId })
141
149
 
142
150
  const body = await TestApp.json(
143
- await app.request('/v1/funding/transfers?limit=200', as(fundingProjectReader)),
144
- Funding.schema.listFundingTransfers.Response,
151
+ await app.request('/v1/routes/transfers?limit=200', as(routesProjectReader)),
152
+ Routes.schema.listRoutesTransfers.Response,
145
153
  )
146
154
  expect(body.data.map((transfer) => transfer.id)).toEqual([scoped.id])
147
155
 
148
156
  // The organization-attributed key sees the project row too.
149
157
  const orgBody = await TestApp.json(
150
- await app.request('/v1/funding/transfers?limit=200', as(fundingReader)),
151
- Funding.schema.listFundingTransfers.Response,
158
+ await app.request('/v1/routes/transfers?limit=200', as(routesReader)),
159
+ Routes.schema.listRoutesTransfers.Response,
152
160
  )
153
161
  expect(orgBody.data.map((transfer) => transfer.id)).toContain(scoped.id)
154
162
  })
155
163
 
156
164
  test('rejects invalid query parameters', async () => {
157
165
  const app = transfersApp()
158
- const response = await app.request('/v1/funding/transfers?status=bogus', as(fundingReader))
166
+ const response = await app.request('/v1/routes/transfers?status=bogus', as(routesReader))
159
167
  expect(response.status).toBe(400)
160
168
  })
161
169
  })
162
170
 
163
- describe('GET /funding/transfers/:id', () => {
171
+ describe('GET /routes/transfers/:id', () => {
164
172
  test('returns the owned transfer directly, without an action', async () => {
165
173
  const app = transfersApp()
166
174
  const record = await seedTransfer()
167
- const response = await app.request(`/v1/funding/transfers/${record.id}`, {
168
- headers: { 'tempo-api-key': fundingReader.token },
175
+ const response = await app.request(`/v1/routes/transfers/${record.id}`, {
176
+ headers: { 'tempo-api-key': routesReader.token },
169
177
  })
170
178
  expect(response.status).toBe(200)
171
179
  expect({
@@ -180,12 +188,12 @@ describe('GET /funding/transfers/:id', () => {
180
188
  expect(response.headers.get('vary')).toBe(
181
189
  'Accept-Encoding, Authorization, Tempo-API-Key, X-API-Key',
182
190
  )
183
- const body = await TestApp.json(response, Funding.schema.getFundingTransfer.Response)
184
- expect(body).toEqual(FundingTransfer.toPublic(record))
191
+ const body = await TestApp.json(response, Routes.schema.getRoutesTransfer.Response)
192
+ expect(body).toEqual(RoutesTransfer.toPublic(record))
185
193
  expect('action' in body).toBe(false)
186
194
 
187
- const repeated = await app.request(`/v1/funding/transfers/${record.id}`, {
188
- headers: { 'if-none-match': '"stale"', 'tempo-api-key': fundingReader.token },
195
+ const repeated = await app.request(`/v1/routes/transfers/${record.id}`, {
196
+ headers: { 'if-none-match': '"stale"', 'tempo-api-key': routesReader.token },
189
197
  })
190
198
  expect({
191
199
  cacheControl: repeated.headers.get('cache-control'),
@@ -204,72 +212,199 @@ describe('GET /funding/transfers/:id', () => {
204
212
  const app = transfersApp()
205
213
  const record = await seedTransfer()
206
214
 
207
- const foreign = await app.request(`/v1/funding/transfers/${record.id}`, as(fundingForeign))
215
+ const foreign = await app.request(`/v1/routes/transfers/${record.id}`, as(routesForeign))
208
216
  expect(foreign.status).toBe(404)
209
217
  expect(((await foreign.json()) as { error: { code: string } }).error.code).toBe(
210
- 'funding_transfer_not_found',
218
+ 'routes_transfer_not_found',
211
219
  )
212
220
 
213
- const project = await app.request(
214
- `/v1/funding/transfers/${record.id}`,
215
- as(fundingProjectReader),
216
- )
221
+ const project = await app.request(`/v1/routes/transfers/${record.id}`, as(routesProjectReader))
217
222
  expect(project.status).toBe(404)
218
223
 
219
224
  const absent = await app.request(
220
- '/v1/funding/transfers/ftr_000000000000000_000000000000000000000000',
221
- as(fundingReader),
225
+ '/v1/routes/transfers/rtr_000000000000000_000000000000000000000000',
226
+ as(routesReader),
222
227
  )
223
228
  expect(absent.status).toBe(404)
224
229
  })
225
230
  })
226
231
 
227
- describe('POST /funding/transfers/:id/source-transactions', () => {
228
- test('requires funding write access and validates the transaction hash', async () => {
232
+ describe('POST /routes/transfers/:id/source-transactions', () => {
233
+ test('requires routes write access and validates the transaction hash array', async () => {
229
234
  const app = transfersApp()
230
235
  const transfer = await seedTransfer()
231
- const path = `/v1/funding/transfers/${transfer.id}/source-transactions`
236
+ const path = `/v1/routes/transfers/${transfer.id}/source-transactions`
232
237
  expect((await app.request(path, { method: 'POST' })).status).toBe(401)
233
- expect((await app.request(path, { ...as(fundingReader), method: 'POST' })).status).toBe(403)
234
- expect(
235
- (
236
- await app.request(path, {
237
- body: JSON.stringify({ transactionHash: '0xinvalid' }),
238
- headers: { ...as(fundingWriter).headers, 'content-type': 'application/json' },
239
- method: 'POST',
240
- })
241
- ).status,
242
- ).toBe(400)
238
+ expect((await app.request(path, { ...as(routesReader), method: 'POST' })).status).toBe(403)
239
+ for (const body of [
240
+ { transactionHashes: [] },
241
+ { transactionHashes: ['0xinvalid'] },
242
+ {
243
+ transactionHashes: Array.from(
244
+ { length: 11 },
245
+ (_, index) => `0x${index.toString(16).padStart(64, '0')}`,
246
+ ),
247
+ },
248
+ ])
249
+ expect(
250
+ (
251
+ await app.request(path, {
252
+ body: JSON.stringify(body),
253
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
254
+ method: 'POST',
255
+ })
256
+ ).status,
257
+ ).toBe(400)
243
258
  })
244
259
 
245
- test('reports when transfer reconciliation is not configured', async () => {
260
+ test('accepts singular and plural requests before reporting missing reconciliation', async () => {
246
261
  const app = transfersApp()
247
262
  const transfer = await seedTransfer()
248
- const response = await app.request(`/v1/funding/transfers/${transfer.id}/source-transactions`, {
249
- body: JSON.stringify({ transactionHash: `0x${'aa'.repeat(32)}` }),
250
- headers: { ...as(fundingWriter).headers, 'content-type': 'application/json' },
251
- method: 'POST',
263
+ const transactionHash = `0x${'aa'.repeat(32)}`
264
+ for (const body of [{ transactionHash }, { transactionHashes: [transactionHash] }]) {
265
+ const response = await app.request(
266
+ `/v1/routes/transfers/${transfer.id}/source-transactions`,
267
+ {
268
+ body: JSON.stringify(body),
269
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
270
+ method: 'POST',
271
+ },
272
+ )
273
+ expect(response.status).toBe(501)
274
+ }
275
+ })
276
+
277
+ test('records source routes and dispatches reconciliation once', async () => {
278
+ const database = TestApp.databaseFactory()
279
+ const setup = database()
280
+ await TestRoutes.publish(setup)
281
+ const firstVerified = Promise.withResolvers<void>()
282
+ const intervals: RoutesObservability.LifecycleInterval[] = []
283
+ const messages: RoutesTransferReconciliation.Message[] = []
284
+ const releaseFirst = Promise.withResolvers<void>()
285
+ const secondVerified = Promise.withResolvers<void>()
286
+ let verifications = 0
287
+ const provider = RoutesProvider.from({
288
+ ...Stargate.stargate(),
289
+ async verifySourceTransaction({ transactionHash }) {
290
+ verifications++
291
+ if (verifications === 1) {
292
+ firstVerified.resolve()
293
+ await releaseFirst.promise
294
+ } else secondVerified.resolve()
295
+ return {
296
+ destinationAmount: '980000',
297
+ providerState: {},
298
+ transactionHash,
299
+ type: 'verified',
300
+ }
301
+ },
252
302
  })
253
- expect(response.status).toBe(501)
303
+ const app = transfersApp({
304
+ db: database,
305
+ routes: {
306
+ async dispatchTransferReconciliation(message) {
307
+ messages.push(message)
308
+ },
309
+ async recordLifecycleInterval(interval) {
310
+ intervals.push(interval)
311
+ },
312
+ },
313
+ providers: [provider],
314
+ })
315
+ const transfer = await RoutesTransfer.create(setup, {
316
+ apiKeyId: routesReader.id,
317
+ environment: 'production',
318
+ id: RoutesTransfer.generateId(new Date(Date.UTC(2026, 0, 2, 0, 0, seedSecond++))),
319
+ orgId: routesReader.orgId,
320
+ snapshot: TestRoutes.transferSnapshot({ provider: { id: 'stargate', name: 'Stargate' } }),
321
+ })
322
+ const request = {
323
+ body: JSON.stringify({ transactionHashes: [`0x${'aa'.repeat(32)}`] }),
324
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
325
+ method: 'POST',
326
+ } as const
327
+ const path = `/v1/routes/transfers/${transfer.id}/source-transactions`
328
+
329
+ const first = app.request(path, request)
330
+ await firstVerified.promise
331
+ const second = app.request(path, request)
332
+ await secondVerified.promise
333
+ releaseFirst.resolve()
334
+ const [response, replay] = await Promise.all([first, second])
335
+ expect(response.status).toBe(200)
336
+ expect(await response.json()).toMatchObject({ destinationAmount: { baseUnits: '980000' } })
337
+ expect(replay.status).toBe(200)
338
+ expect(intervals).toEqual([
339
+ expect.objectContaining({
340
+ routesEnvironment: 'production',
341
+ method: 'transaction',
342
+ provider: 'stargate',
343
+ resourceId: transfer.id,
344
+ stage: 'source_routes',
345
+ }),
346
+ ])
347
+ expect(messages).toEqual([{ transferId: transfer.id, type: 'routes:transfer:reconcile' }])
254
348
  })
255
349
 
256
350
  test('does not expose a transfer owned by another organization', async () => {
257
- const tracker = FundingTransferReconciliation.createTracker({ db })
351
+ const tracker = RoutesTransferReconciliation.createTracker({
352
+ db,
353
+ providers: [Stargate.stargate()],
354
+ })
258
355
  const app = transfersApp({
259
- funding: {
356
+ routes: {
260
357
  async dispatchTransferReconciliation(message) {
261
358
  await tracker.reconcile(message)
262
359
  },
263
360
  },
264
361
  })
265
362
  const transfer = await seedTransfer()
266
- const response = await app.request(`/v1/funding/transfers/${transfer.id}/source-transactions`, {
267
- body: JSON.stringify({ transactionHash: `0x${'aa'.repeat(32)}` }),
268
- headers: { ...as(fundingForeign).headers, 'content-type': 'application/json' },
363
+ const response = await app.request(`/v1/routes/transfers/${transfer.id}/source-transactions`, {
364
+ body: JSON.stringify({ transactionHashes: [`0x${'aa'.repeat(32)}`] }),
365
+ headers: { ...as(routesForeign).headers, 'content-type': 'application/json' },
269
366
  method: 'POST',
270
367
  })
271
368
  expect(response.status).toBe(404)
272
369
  })
370
+
371
+ test('waits for every source verification before reporting provider attempts', async () => {
372
+ const entries: Log.Entry[] = []
373
+ const transactionHashes = [`0x${'aa'.repeat(32)}`, `0x${'bb'.repeat(32)}`]
374
+ const provider = RoutesProvider.from({
375
+ ...Stargate.stargate(),
376
+ async verifySourceTransaction({ transactionHash }) {
377
+ if (transactionHash === transactionHashes[0]) throw new Error('upstream unavailable')
378
+ await new Promise((resolve) => setTimeout(resolve, 0))
379
+ return { type: 'pending' }
380
+ },
381
+ })
382
+ const app = transfersApp({
383
+ routes: { async dispatchTransferReconciliation() {} },
384
+ logger: (entry) => void entries.push(entry),
385
+ providers: [provider],
386
+ })
387
+ const transfer = await seedTransfer({
388
+ snapshot: TestRoutes.transferSnapshot({ provider: { id: 'stargate', name: 'Stargate' } }),
389
+ })
390
+
391
+ const response = await app.request(`/v1/routes/transfers/${transfer.id}/source-transactions`, {
392
+ body: JSON.stringify({ transactionHashes }),
393
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
394
+ method: 'POST',
395
+ })
396
+
397
+ expect(response.status).toBe(502)
398
+ expect(
399
+ entries[0]?.routesProviderAttempts?.map(({ operation, outcome }) => ({
400
+ operation,
401
+ outcome,
402
+ })),
403
+ ).toEqual([
404
+ { operation: 'verifySourceTransaction', outcome: 'failed' },
405
+ { operation: 'verifySourceTransaction', outcome: 'success' },
406
+ ])
407
+ })
273
408
  })
274
409
 
275
410
  const createBody = {
@@ -311,10 +446,10 @@ function postTransfer(
311
446
  app: ReturnType<typeof create>,
312
447
  options: { body?: unknown; idempotencyKey?: string | undefined; key?: { token: string } } = {},
313
448
  ) {
314
- return app.request('/v1/funding/transfers', {
449
+ return app.request('/v1/routes/transfers', {
315
450
  body: JSON.stringify(options.body ?? createBody),
316
451
  headers: {
317
- authorization: `Bearer ${(options.key ?? fundingWriter).token}`,
452
+ authorization: `Bearer ${(options.key ?? routesWriter).token}`,
318
453
  'content-type': 'application/json',
319
454
  ...(options.idempotencyKey === undefined
320
455
  ? {}
@@ -324,15 +459,17 @@ function postTransfer(
324
459
  })
325
460
  }
326
461
 
327
- describe('POST /funding/transfers', () => {
328
- function providerResponse(): FundingProvider.prepareTransfer.ReturnType {
462
+ describe('POST /routes/transfers', () => {
463
+ function providerResponse(
464
+ options: { sourceToken?: string | undefined } = {},
465
+ ): RoutesProvider.prepareTransfer.ReturnType {
329
466
  const spender = `0x${'44'.repeat(20)}`
330
467
  return {
331
468
  action: {
332
469
  calls: [
333
470
  {
334
471
  data: `0x095ea7b3${spender.slice(2).padStart(64, '0')}${(1_000_000).toString(16).padStart(64, '0')}`,
335
- to: createBody.sourceToken,
472
+ to: options.sourceToken ?? createBody.sourceToken,
336
473
  value: '0x0',
337
474
  },
338
475
  { data: '0xe8017952', to: spender, value: '0x0' },
@@ -354,10 +491,10 @@ describe('POST /funding/transfers', () => {
354
491
  }
355
492
 
356
493
  function provider(
357
- prepareTransfer: NonNullable<FundingProvider.Provider['prepareTransfer']> = async () =>
494
+ prepareTransfer: NonNullable<RoutesProvider.Provider['prepareTransfer']> = async () =>
358
495
  providerResponse(),
359
496
  ) {
360
- return FundingProvider.from({
497
+ return RoutesProvider.from({
361
498
  ...Relay.relay(),
362
499
  prepareTransfer,
363
500
  })
@@ -365,16 +502,16 @@ describe('POST /funding/transfers', () => {
365
502
 
366
503
  let idempotencySequence = 0
367
504
  function nextIdempotencyKey() {
368
- return `funding_test_${idempotencySequence++}`
505
+ return `routes_test_${idempotencySequence++}`
369
506
  }
370
507
 
371
- test('requires funding:write and an Idempotency-Key', async () => {
508
+ test('requires routes:write and an Idempotency-Key', async () => {
372
509
  const app = transfersApp({
373
510
  providers: [provider()],
374
511
  })
375
512
  const forbidden = await postTransfer(app, {
376
513
  idempotencyKey: nextIdempotencyKey(),
377
- key: fundingReader,
514
+ key: routesReader,
378
515
  })
379
516
  expect(forbidden.status).toBe(403)
380
517
 
@@ -386,7 +523,7 @@ describe('POST /funding/transfers', () => {
386
523
  })
387
524
 
388
525
  test('creates a transfer with the selected executable quote', async () => {
389
- const inputs: FundingProvider.prepareTransfer.Parameters[] = []
526
+ const inputs: RoutesProvider.prepareTransfer.Parameters[] = []
390
527
  const app = transfersApp({
391
528
  providers: [
392
529
  provider(async (input) => {
@@ -401,7 +538,7 @@ describe('POST /funding/transfers', () => {
401
538
  })
402
539
  expect(response.status).toBe(200)
403
540
  expect(response.headers.get('cache-control')).toBe('no-store')
404
- const body = await TestApp.json(response, Funding.schema.createFundingTransfer.Response)
541
+ const body = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
405
542
 
406
543
  expect(inputs[0]).toMatchObject({
407
544
  method: 'transaction',
@@ -426,30 +563,232 @@ describe('POST /funding/transfers', () => {
426
563
 
427
564
  // Reads return the same transfer without the action; the private
428
565
  // correlation never leaves the row.
429
- const read = await app.request(`/v1/funding/transfers/${body.id}`, as(fundingWriter))
430
- const readBody = await TestApp.json(read, Funding.schema.getFundingTransfer.Response)
566
+ const read = await app.request(`/v1/routes/transfers/${body.id}`, as(routesWriter))
567
+ const readBody = await TestApp.json(read, Routes.schema.getRoutesTransfer.Response)
431
568
  expect('action' in readBody).toBe(false)
432
569
  expect(JSON.stringify(readBody)).not.toContain('ab'.repeat(32))
433
570
  const row = await db.kysely
434
- .selectFrom('funding_transfers')
571
+ .selectFrom('routes_transfers')
435
572
  .select(['providerState'])
436
573
  .where('id', '=', body.id)
437
574
  .executeTakeFirstOrThrow()
438
575
  expect(row.providerState).toEqual({ requestId: `0x${'ab'.repeat(32)}` })
439
576
  })
440
577
 
578
+ test('rejects completion when the owner is deleted after the initial claim', async () => {
579
+ const suffix = nextIdempotencyKey()
580
+ const key = {
581
+ id: `key_${suffix}`,
582
+ orgId: `org_${suffix}`,
583
+ scopes: ['routes:write'],
584
+ token: `secret_${suffix}`,
585
+ } satisfies TestApp.kvStore.Key
586
+ await Organizations.create(db, { id: key.orgId, name: 'Transfer deletion race' })
587
+ const entered = Promise.withResolvers<void>()
588
+ const release = Promise.withResolvers<void>()
589
+ const app = create({
590
+ auth: { keys: [key] },
591
+ defaultChainId: tempoMainnet.id,
592
+ providers: [
593
+ provider(async () => {
594
+ entered.resolve()
595
+ await release.promise
596
+ return providerResponse()
597
+ }),
598
+ ],
599
+ })
600
+ const idempotencyKey = nextIdempotencyKey()
601
+ const creation = postTransfer(app, { idempotencyKey, key })
602
+ await entered.promise
603
+
604
+ await expect(
605
+ db.kysely
606
+ .selectFrom('routes_idempotency_requests')
607
+ .select('status')
608
+ .where('apiKeyId', '=', key.id)
609
+ .where('keyHash', '=', Idempotency.inputHash(idempotencyKey))
610
+ .executeTakeFirstOrThrow(),
611
+ ).resolves.toEqual({ status: 'pending' })
612
+ expect(await Organizations.deleteOrganization(db, key.orgId)).toBe(true)
613
+ release.resolve()
614
+ const response = await creation
615
+
616
+ expect(response.status).toBe(403)
617
+ await expect(response.json()).resolves.toMatchObject({
618
+ error: {
619
+ code: 'api_key_forbidden',
620
+ message: 'API key organization no longer exists.',
621
+ },
622
+ })
623
+ await expect(
624
+ db.kysely
625
+ .selectFrom('routes_transfers')
626
+ .select('id')
627
+ .where('orgId', '=', key.orgId)
628
+ .execute(),
629
+ ).resolves.toEqual([])
630
+ await expect(
631
+ db.kysely
632
+ .selectFrom('routes_idempotency_requests')
633
+ .select(['response', 'status', 'transferId'])
634
+ .where('apiKeyId', '=', key.id)
635
+ .where('keyHash', '=', Idempotency.inputHash(idempotencyKey))
636
+ .execute(),
637
+ ).resolves.toEqual([])
638
+ })
639
+
640
+ test('requires configuration and records normalized subsidy terms', async () => {
641
+ const request = {
642
+ ...createBody,
643
+ destinationChain: 'base',
644
+ destinationToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
645
+ sourceChain: 'tempo',
646
+ sourceToken: '0x20c000000000000000000000b9537d11c60e8b50',
647
+ subsidize: true,
648
+ }
649
+ const outbound = RoutesProvider.from({
650
+ ...Relay.relay(),
651
+ id: 'stargate',
652
+ name: 'Stargate',
653
+ async prepareTransfer() {
654
+ return providerResponse({ sourceToken: request.sourceToken })
655
+ },
656
+ })
657
+ const idempotencyKey = nextIdempotencyKey()
658
+ const unavailable = await postTransfer(transfersApp({ providers: [outbound] }), {
659
+ body: request,
660
+ idempotencyKey,
661
+ })
662
+ expect(unavailable.status).toBe(501)
663
+
664
+ await Organizations.create(db, { id: routesWriter.orgId, name: routesWriter.orgId })
665
+ await RoutesSubsidies.upsertOrganization(db, {
666
+ frequency: 'tx',
667
+ maxAmount: '5',
668
+ orgId: routesWriter.orgId,
669
+ })
670
+ await StripeCustomers.create(db, {
671
+ orgId: routesWriter.orgId,
672
+ stripeCustomerId: 'cus_routes_transfer_subsidy',
673
+ })
674
+ await StripeCustomers.setStatus(db, routesWriter.orgId, 'active')
675
+ const upstream = await postTransfer(
676
+ transfersApp({
677
+ providers: [outbound],
678
+ routes: {
679
+ transferSubsidy: {
680
+ account: `0x${'77'.repeat(20)}`,
681
+ async check() {
682
+ throw new RoutesTransferSubsidy.InventoryUpstreamError(
683
+ new Error('Destination RPC unavailable.'),
684
+ )
685
+ },
686
+ policy: { maxAmount: '5' },
687
+ },
688
+ },
689
+ }),
690
+ { body: request, idempotencyKey: nextIdempotencyKey() },
691
+ )
692
+ expect(upstream.status).toBe(502)
693
+ await expect(upstream.json()).resolves.toMatchObject({ error: { code: 'upstream_error' } })
694
+ const limited = await postTransfer(
695
+ transfersApp({
696
+ providers: [outbound],
697
+ routes: { transferSubsidy: transferSubsidy('0.015') },
698
+ }),
699
+ { body: request, idempotencyKey: nextIdempotencyKey() },
700
+ )
701
+ expect(limited.status).toBe(403)
702
+ await expect(limited.json()).resolves.toMatchObject({
703
+ error: { code: 'routes_subsidy_limit_exceeded' },
704
+ })
705
+ const response = await postTransfer(
706
+ transfersApp({
707
+ providers: [outbound],
708
+ routes: { transferSubsidy: transferSubsidy('5') },
709
+ }),
710
+ {
711
+ body: request,
712
+ idempotencyKey,
713
+ },
714
+ )
715
+ expect(response.status).toBe(200)
716
+ const body = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
717
+ expect(body).toMatchObject({
718
+ destinationAmount: { baseUnits: '990000' },
719
+ destinationAmountRequired: { baseUnits: '1000000' },
720
+ subsidize: true,
721
+ })
722
+ const row = await db.kysely
723
+ .selectFrom('routes_transfers')
724
+ .select([
725
+ 'providerState',
726
+ 'subsidyAccount',
727
+ 'subsidyChainId',
728
+ 'subsidyCommitmentAmount',
729
+ 'subsidyNativeAmount',
730
+ 'subsidyTokenAddress',
731
+ ])
732
+ .where('id', '=', body.id)
733
+ .executeTakeFirstOrThrow()
734
+ expect(row.providerState).not.toHaveProperty('transferSubsidyPolicy')
735
+ expect(row).toMatchObject({
736
+ subsidyAccount: `0x${'77'.repeat(20)}`,
737
+ subsidyChainId: 'eip155:8453',
738
+ subsidyCommitmentAmount: { baseUnits: '20000' },
739
+ subsidyNativeAmount: '100',
740
+ subsidyTokenAddress: request.destinationToken,
741
+ })
742
+ const replay = await postTransfer(transfersApp({ providers: [outbound] }), {
743
+ body: request,
744
+ idempotencyKey,
745
+ })
746
+ expect(replay.status).toBe(200)
747
+ await expect(replay.json()).resolves.toMatchObject({ id: body.id })
748
+ const mismatch = await postTransfer(transfersApp({ providers: [outbound] }), {
749
+ body: { ...request, amount: '2000000' },
750
+ idempotencyKey,
751
+ })
752
+ expect(mismatch.status).toBe(409)
753
+ await expect(mismatch.json()).resolves.toMatchObject({
754
+ error: { code: 'idempotency_conflict' },
755
+ })
756
+
757
+ await RoutesCatalog.publish(db, {
758
+ ...TestRoutes.data,
759
+ tokens: TestRoutes.data.tokens.map((token) =>
760
+ token.id === 'usdce' ? { ...token, currency: 'EUR' } : token,
761
+ ),
762
+ })
763
+ try {
764
+ const incompatible = await postTransfer(
765
+ transfersApp({
766
+ providers: [outbound],
767
+ routes: { transferSubsidy: transferSubsidy('5') },
768
+ }),
769
+ { body: request, idempotencyKey: nextIdempotencyKey() },
770
+ )
771
+ expect(incompatible.status).toBe(400)
772
+ await expect(incompatible.json()).resolves.toMatchObject({
773
+ error: { code: 'body_invalid' },
774
+ })
775
+ } finally {
776
+ await TestRoutes.publish(db)
777
+ }
778
+ })
779
+
441
780
  test('dispatches staged webhook updates after creation commits', async () => {
442
781
  const subscription = await Webhooks.createSubscription(db, {
443
782
  chainId: tempoMainnet.id,
444
- destination: { type: 'url', url: 'https://example.com/webhooks/funding-transfers' },
783
+ destination: { type: 'url', url: 'https://example.com/webhooks/routes-transfers' },
445
784
  environment: 'production',
446
- eventType: 'funding:transfer.updated',
785
+ eventType: 'routes:transfer.updated',
447
786
  filters: { status: 'awaiting-source' },
448
- owner: { orgId: fundingWriter.orgId, type: 'api_key' },
787
+ owner: { orgId: routesWriter.orgId, type: 'api_key' },
449
788
  })
450
789
  const references: Webhooks.QueueReference[] = []
451
790
  const app = transfersApp({
452
- funding: {
791
+ routes: {
453
792
  async dispatchTransferUpdates(staged) {
454
793
  const reference = staged[0]!
455
794
  const queued = await db.kysely
@@ -480,7 +819,7 @@ describe('POST /funding/transfers', () => {
480
819
  })
481
820
 
482
821
  expect(response.status).toBe(200)
483
- const body = await TestApp.json(response, Funding.schema.createFundingTransfer.Response)
822
+ const body = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
484
823
  expect(body.provider.id).toBe('relay')
485
824
  })
486
825
 
@@ -524,7 +863,7 @@ describe('POST /funding/transfers', () => {
524
863
  idempotencyKey: nextIdempotencyKey(),
525
864
  })
526
865
  expect(response.status).toBe(200)
527
- const body = await TestApp.json(response, Funding.schema.createFundingTransfer.Response)
866
+ const body = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
528
867
 
529
868
  expect(body.fees).toMatchObject([
530
869
  {
@@ -560,7 +899,7 @@ describe('POST /funding/transfers', () => {
560
899
  },
561
900
  idempotencyKey: nextIdempotencyKey(),
562
901
  })
563
- const body = await TestApp.json(response, Funding.schema.createFundingTransfer.Response)
902
+ const body = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
564
903
  results.push({
565
904
  destinationToken: body.destinationToken.symbol,
566
905
  name: identifier.name,
@@ -625,9 +964,12 @@ describe('POST /funding/transfers', () => {
625
964
  providers: [provider()],
626
965
  })
627
966
  const idempotencyKey = nextIdempotencyKey()
628
- await FundingIdempotencyTable.claim(db, {
629
- apiKeyId: fundingWriter.id,
967
+ await RoutesIdempotencyTable.claim(db, {
968
+ apiKeyId: routesWriter.id,
969
+ irrevocable: false,
630
970
  keyHash: Idempotency.inputHash(idempotencyKey),
971
+ operation: RoutesIdempotencyTable.operations.transfer,
972
+ orgId: routesWriter.orgId,
631
973
  requestHash: Idempotency.inputHash(createBody),
632
974
  ttlMs: 60_000,
633
975
  })
@@ -642,21 +984,24 @@ describe('POST /funding/transfers', () => {
642
984
  const idempotencyKey = nextIdempotencyKey()
643
985
  const keyHash = Idempotency.inputHash(idempotencyKey)
644
986
  const before = await db.kysely
645
- .selectFrom('funding_transfers')
987
+ .selectFrom('routes_transfers')
646
988
  .select(({ fn }) => fn.countAll<number>().as('count'))
647
989
  .executeTakeFirstOrThrow()
648
990
  const app = transfersApp({
649
991
  providers: [
650
992
  provider(async () => {
651
993
  await db.kysely
652
- .updateTable('funding_idempotency_requests')
994
+ .updateTable('routes_idempotency_requests')
653
995
  .set({ expiresAt: new Date(0).toISOString() })
654
- .where('apiKeyId', '=', fundingWriter.id)
996
+ .where('apiKeyId', '=', routesWriter.id)
655
997
  .where('keyHash', '=', keyHash)
656
998
  .execute()
657
- await FundingIdempotencyTable.claim(db, {
658
- apiKeyId: fundingWriter.id,
999
+ await RoutesIdempotencyTable.claim(db, {
1000
+ apiKeyId: routesWriter.id,
1001
+ irrevocable: false,
659
1002
  keyHash,
1003
+ operation: RoutesIdempotencyTable.operations.transfer,
1004
+ orgId: routesWriter.orgId,
660
1005
  requestHash: Idempotency.inputHash(createBody),
661
1006
  ttlMs: 60_000,
662
1007
  })
@@ -668,7 +1013,7 @@ describe('POST /funding/transfers', () => {
668
1013
  const response = await postTransfer(app, { idempotencyKey })
669
1014
  expect(response.status).toBe(500)
670
1015
  const after = await db.kysely
671
- .selectFrom('funding_transfers')
1016
+ .selectFrom('routes_transfers')
672
1017
  .select(({ fn }) => fn.countAll<number>().as('count'))
673
1018
  .executeTakeFirstOrThrow()
674
1019
  expect(after.count).toBe(before.count)
@@ -702,6 +1047,35 @@ describe('POST /funding/transfers', () => {
702
1047
  idempotencyKey: nextIdempotencyKey(),
703
1048
  })
704
1049
  expect(unknownToken.status).toBe(404)
1050
+
1051
+ const unknownChain = await postTransfer(app, {
1052
+ body: { ...createBody, destinationChain: 'bitcoin' },
1053
+ idempotencyKey: nextIdempotencyKey(),
1054
+ })
1055
+ expect(unknownChain.status).toBe(404)
1056
+ })
1057
+
1058
+ test.each([
1059
+ ['recipient', { recipient: 'invalid' }],
1060
+ ['sender', { sender: 'invalid' }],
1061
+ ] as const)('rejects a route-invalid %s before calling the provider', async (_field, body) => {
1062
+ let calls = 0
1063
+ const app = transfersApp({
1064
+ providers: [
1065
+ provider(async () => {
1066
+ calls++
1067
+ return providerResponse()
1068
+ }),
1069
+ ],
1070
+ })
1071
+ const response = await postTransfer(app, {
1072
+ body: { ...createBody, ...body },
1073
+ idempotencyKey: nextIdempotencyKey(),
1074
+ })
1075
+
1076
+ expect(response.status).toBe(400)
1077
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe('body_invalid')
1078
+ expect(calls).toBe(0)
705
1079
  })
706
1080
 
707
1081
  test('releases the claim after a provider failure', async () => {
@@ -751,7 +1125,7 @@ describe('POST /funding/transfers', () => {
751
1125
  })
752
1126
  })
753
1127
 
754
- describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', () => {
1128
+ describe.skipIf(!Anvil.available)('POST /routes/transfers (forked localnet)', () => {
755
1129
  /**
756
1130
  * Storage slot of Circle FiatToken's `balanceAndBlacklistStates` mapping.
757
1131
  * `anvil_dealERC20` cannot probe it because balances pack a blacklist bit,
@@ -762,7 +1136,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
762
1136
 
763
1137
  for (const executable of executableProviders) {
764
1138
  describe(executable.id, () => {
765
- const route = TestFunding.snapshot.routes.find(
1139
+ const route = TestRoutes.snapshot.routes.find(
766
1140
  (entry) =>
767
1141
  entry.route.source.chain.id === `eip155:${base.id}` &&
768
1142
  entry.route.source.symbol === 'USDC' &&
@@ -806,7 +1180,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
806
1180
 
807
1181
  const response = await createTransfer(`bridge_${executable.id}_${sender.address}`)
808
1182
  expect(response.status).toBe(200)
809
- const transfer = await TestApp.json(response, Funding.schema.createFundingTransfer.Response)
1183
+ const transfer = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
810
1184
  expect(transfer.action.type).toBe('evm:calls')
811
1185
  if (transfer.action.type !== 'evm:calls') return
812
1186
  expect(transfer.action.calls.length).toBeGreaterThan(0)
@@ -819,7 +1193,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
819
1193
  },
820
1194
  ])
821
1195
  const row = await db.kysely
822
- .selectFrom('funding_transfers')
1196
+ .selectFrom('routes_transfers')
823
1197
  .select('providerState')
824
1198
  .where('id', '=', transfer.id)
825
1199
  .executeTakeFirstOrThrow()
@@ -853,11 +1227,18 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
853
1227
  await expect(
854
1228
  Stargate.verifySourceTransaction({
855
1229
  configuration: stargateConfiguration,
1230
+ destinationAmountMin: transfer.destinationAmountMin!.baseUnits,
1231
+ destinationChain: RoutesChain.from({
1232
+ id: 'eip155:4217',
1233
+ name: 'Tempo',
1234
+ rpcUrls: [tempoMainnet.rpcUrls.default.http[0]!],
1235
+ slug: 'tempo',
1236
+ }),
856
1237
  destinationTokenAddress: transfer.destinationToken.address,
857
1238
  recipient: transfer.recipient,
858
1239
  sender: sender.address,
859
1240
  sourceAmount: transfer.sourceAmount.baseUnits,
860
- sourceChain: FundingChain.from({
1241
+ sourceChain: RoutesChain.from({
861
1242
  id: 'eip155:8453',
862
1243
  name: 'Base',
863
1244
  rpcUrls: [anvil.rpcUrl],
@@ -872,7 +1253,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
872
1253
  })
873
1254
 
874
1255
  if (executable.id === 'stargate')
875
- test('reopens registration when the source transaction is reorged out', async () => {
1256
+ test('registers non-atomic calls and reopens them after an execution reorg', async () => {
876
1257
  await Anvil.setErc20Balance(anvil.client, {
877
1258
  address: sender.address,
878
1259
  amount,
@@ -881,10 +1262,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
881
1262
  })
882
1263
  const response = await createTransfer(`bridge_reorg_${sender.address}`)
883
1264
  expect(response.status).toBe(200)
884
- const transfer = await TestApp.json(
885
- response,
886
- Funding.schema.createFundingTransfer.Response,
887
- )
1265
+ const transfer = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
888
1266
  expect(transfer.action.type).toBe('evm:calls')
889
1267
  if (transfer.action.type !== 'evm:calls') return
890
1268
 
@@ -908,15 +1286,15 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
908
1286
  )
909
1287
  const sourceReceipt = receipts.at(-1)!
910
1288
  const transactionHash = sourceReceipt.transactionHash
911
- await FundingCatalog.publish(db, {
912
- ...TestFunding.data,
913
- chains: TestFunding.data.chains.map((chain) =>
1289
+ await RoutesCatalog.publish(db, {
1290
+ ...TestRoutes.data,
1291
+ chains: TestRoutes.data.chains.map((chain) =>
914
1292
  chain.id === 'eip155:8453' ? { ...chain, rpcUrls: [anvil.rpcUrl] } : chain,
915
1293
  ),
916
1294
  })
917
- const messages: FundingTransferReconciliation.Message[] = []
1295
+ const messages: RoutesTransferReconciliation.Message[] = []
918
1296
  const app = transfersApp({
919
- funding: {
1297
+ routes: {
920
1298
  async dispatchTransferReconciliation(message) {
921
1299
  messages.push(message)
922
1300
  },
@@ -924,35 +1302,38 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
924
1302
  providers: [Stargate.stargate()],
925
1303
  })
926
1304
  const registered = await app.request(
927
- `/v1/funding/transfers/${transfer.id}/source-transactions`,
1305
+ `/v1/routes/transfers/${transfer.id}/source-transactions`,
928
1306
  {
929
- body: JSON.stringify({ transactionHash }),
930
- headers: { ...as(fundingWriter).headers, 'content-type': 'application/json' },
1307
+ body: JSON.stringify({
1308
+ transactionHashes: receipts.map((receipt) => receipt.transactionHash),
1309
+ }),
1310
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
931
1311
  method: 'POST',
932
1312
  },
933
1313
  )
934
1314
  expect(registered.status).toBe(200)
935
1315
  expect(messages).toEqual([
936
- { transferId: transfer.id, type: 'funding:transfer:reconcile' },
1316
+ { transferId: transfer.id, type: 'routes:transfer:reconcile' },
937
1317
  ])
938
1318
 
939
1319
  await anvil.client.revert({ id: checkpoint })
940
1320
  reverted = true
941
- const tracker = FundingTransferReconciliation.createTracker({
1321
+ const tracker = RoutesTransferReconciliation.createTracker({
942
1322
  db,
943
1323
  async dispatch(message) {
944
1324
  messages.push(message)
945
1325
  },
1326
+ providers: [Stargate.stargate()],
946
1327
  })
947
1328
  await expect(tracker.reconcile(messages[0]!)).resolves.toEqual({ type: 'pending' })
948
1329
  expect(messages[1]).toEqual({
949
1330
  attempt: 1,
950
1331
  transferId: transfer.id,
951
- type: 'funding:transfer:reconcile',
1332
+ type: 'routes:transfer:reconcile',
952
1333
  })
953
1334
 
954
1335
  const pending = await db.kysely
955
- .selectFrom('funding_transfers')
1336
+ .selectFrom('routes_transfers')
956
1337
  .select(['snapshot', 'status', 'version'])
957
1338
  .where('id', '=', transfer.id)
958
1339
  .executeTakeFirstOrThrow()
@@ -961,7 +1342,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
961
1342
  status: pending.status,
962
1343
  version: pending.version,
963
1344
  }).toEqual({
964
- sourceTransactionHashes: [transactionHash],
1345
+ sourceTransactionHashes: [transactionHash, receipts[0]!.transactionHash],
965
1346
  status: 'processing',
966
1347
  version: 2,
967
1348
  })
@@ -972,7 +1353,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
972
1353
  await expect(tracker.reconcile(messages[1]!)).resolves.toEqual({ type: 'reopened' })
973
1354
 
974
1355
  const record = await db.kysely
975
- .selectFrom('funding_transfers')
1356
+ .selectFrom('routes_transfers')
976
1357
  .select(['snapshot', 'status', 'version'])
977
1358
  .where('id', '=', transfer.id)
978
1359
  .executeTakeFirstOrThrow()
@@ -987,21 +1368,21 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
987
1368
  })
988
1369
  expect(
989
1370
  await db.kysely
990
- .selectFrom('funding_transfer_transactions')
1371
+ .selectFrom('routes_transfer_transactions')
991
1372
  .selectAll()
992
1373
  .where('transferId', '=', transfer.id)
993
1374
  .execute(),
994
1375
  ).toEqual([])
995
1376
  } finally {
996
1377
  if (!reverted) await anvil.client.revert({ id: checkpoint })
997
- await TestFunding.publish(db)
1378
+ await TestRoutes.publish(db)
998
1379
  }
999
1380
  })
1000
1381
 
1001
1382
  test('bounds the approval to the quoted source spend', async () => {
1002
1383
  const response = await createTransfer(`bridge_bounds_${executable.id}_${sender.address}`)
1003
1384
  expect(response.status).toBe(200)
1004
- const transfer = await TestApp.json(response, Funding.schema.createFundingTransfer.Response)
1385
+ const transfer = await TestApp.json(response, Routes.schema.createRoutesTransfer.Response)
1005
1386
  if (transfer.action.type !== 'evm:calls') return
1006
1387
 
1007
1388
  const approval = transfer.action.calls.find((call) =>
@@ -1024,7 +1405,7 @@ describe.skipIf(!Anvil.available)('POST /funding/transfers (forked localnet)', (
1024
1405
  })
1025
1406
 
1026
1407
  describe.skipIf(!Anvil.available)(
1027
- 'POST /funding/transfers/:id/source-transactions (forked localnet)',
1408
+ 'POST /routes/transfers/:id/source-transactions (forked localnet)',
1028
1409
  () => {
1029
1410
  const trackingDb = TestApp.database()
1030
1411
  let destination: Anvil.start.ReturnType
@@ -1035,9 +1416,9 @@ describe.skipIf(!Anvil.available)(
1035
1416
  Anvil.start({ chain: tempoMainnet, forkUrl: tempoMainnet.rpcUrls.default.http[0]! }),
1036
1417
  Anvil.start({ chain: base, forkUrl: base.rpcUrls.default.http[0]! }),
1037
1418
  ])
1038
- await FundingCatalog.publish(trackingDb, {
1039
- ...TestFunding.data,
1040
- chains: TestFunding.data.chains.map((chain) => {
1419
+ await RoutesCatalog.publish(trackingDb, {
1420
+ ...TestRoutes.data,
1421
+ chains: TestRoutes.data.chains.map((chain) => {
1041
1422
  if (chain.id === 'eip155:4217') return { ...chain, rpcUrls: [destination.rpcUrl] }
1042
1423
  if (chain.id === 'eip155:8453') return { ...chain, rpcUrls: [source.rpcUrl] }
1043
1424
  return chain
@@ -1049,7 +1430,7 @@ describe.skipIf(!Anvil.available)(
1049
1430
  await Promise.all([destination?.stop(), source?.stop()])
1050
1431
  })
1051
1432
 
1052
- test('backfills legacy state and completes from destination evidence', async () => {
1433
+ test('backfills legacy state and completes despite a foreign source claim', async () => {
1053
1434
  const destinationReceipt = await destination.client.getTransactionReceipt({
1054
1435
  hash: destinationTransactionHash,
1055
1436
  })
@@ -1067,7 +1448,7 @@ describe.skipIf(!Anvil.available)(
1067
1448
  formatted: '0.2',
1068
1449
  } as const
1069
1450
  const snapshot = {
1070
- ...TestFunding.transferSnapshot({
1451
+ ...TestRoutes.transferSnapshot({
1071
1452
  destinationAmount: amount,
1072
1453
  destinationAmountMin: amount,
1073
1454
  provider: { id: 'stargate', name: 'Stargate' },
@@ -1080,20 +1461,40 @@ describe.skipIf(!Anvil.available)(
1080
1461
  sampledAt,
1081
1462
  },
1082
1463
  }
1083
- const transfer = await FundingTransfer.create(trackingDb, {
1084
- apiKeyId: fundingWriter.id,
1464
+ const subscriptions = await Promise.all(
1465
+ [routesForeign, routesWriter].map((key) =>
1466
+ Webhooks.createSubscription(trackingDb, {
1467
+ chainId: 4217,
1468
+ destination: { type: 'url', url: 'https://example.com/routes' },
1469
+ environment: 'production',
1470
+ eventType: 'routes:transfer.updated',
1471
+ owner: { orgId: key.orgId, type: 'api_key' },
1472
+ }),
1473
+ ),
1474
+ )
1475
+ const transfer = await RoutesTransfer.create(trackingDb, {
1476
+ apiKeyId: routesWriter.id,
1477
+ environment: 'production',
1478
+ orgId: routesWriter.orgId,
1479
+ snapshot,
1480
+ })
1481
+ const foreign = await RoutesTransfer.create(trackingDb, {
1482
+ apiKeyId: routesForeign.id,
1085
1483
  environment: 'production',
1086
- orgId: fundingWriter.orgId,
1484
+ orgId: routesForeign.orgId,
1087
1485
  snapshot,
1088
1486
  })
1089
- const tracker = FundingTransferReconciliation.createTracker({ db: trackingDb })
1090
- const messages: FundingTransferReconciliation.Message[] = []
1487
+ const tracker = RoutesTransferReconciliation.createTracker({
1488
+ db: trackingDb,
1489
+ providers: [Stargate.stargate()],
1490
+ })
1491
+ const messages: RoutesTransferReconciliation.Message[] = []
1091
1492
  let dispatchEnabled = true
1092
1493
  const app = TestApp.create({
1093
- auth: { keys: [fundingWriter] },
1494
+ auth: { keys: [routesForeign, routesWriter] },
1094
1495
  db: trackingDb,
1095
1496
  defaultChainId: tempoMainnet.id,
1096
- funding: {
1497
+ routes: {
1097
1498
  async dispatchTransferReconciliation(message) {
1098
1499
  if (!dispatchEnabled) throw new Error('reconciliation unavailable')
1099
1500
  messages.push(message)
@@ -1101,19 +1502,32 @@ describe.skipIf(!Anvil.available)(
1101
1502
  },
1102
1503
  providers: [Stargate.stargate()],
1103
1504
  })
1104
- const path = `/v1/funding/transfers/${transfer.id}/source-transactions`
1505
+ const path = `/v1/routes/transfers/${transfer.id}/source-transactions`
1105
1506
  const registration = {
1106
1507
  body: JSON.stringify({
1107
- transactionHash: sourceTransactionHash,
1508
+ transactionHashes: [sourceTransactionHash],
1108
1509
  }),
1109
- headers: { ...as(fundingWriter).headers, 'content-type': 'application/json' },
1510
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
1110
1511
  method: 'POST',
1111
1512
  }
1513
+ const foreignResponse = await app.request(
1514
+ `/v1/routes/transfers/${foreign.id}/source-transactions`,
1515
+ {
1516
+ ...registration,
1517
+ headers: { ...as(routesForeign).headers, 'content-type': 'application/json' },
1518
+ },
1519
+ )
1520
+ expect(foreignResponse.status).toBe(200)
1521
+ expect(
1522
+ (await app.request(`/v1/routes/transfers/${foreign.id}`, as(routesWriter))).status,
1523
+ ).toBe(404)
1524
+ expect(await tracker.reconcile(messages[0]!)).toEqual({ type: 'completed' })
1525
+
1112
1526
  const response = await app.request(path, registration)
1113
1527
  expect(response.status).toBe(200)
1114
1528
  const registered = await TestApp.json(
1115
1529
  response,
1116
- Funding.schema.registerFundingTransferSourceTransaction.Response,
1530
+ Routes.schema.registerRoutesTransferSourceTransaction.Response,
1117
1531
  )
1118
1532
  expect({
1119
1533
  sourceTransactionHashes: registered.sourceTransactionHashes,
@@ -1127,7 +1541,7 @@ describe.skipIf(!Anvil.available)(
1127
1541
  version: 2,
1128
1542
  })
1129
1543
  const stored = await trackingDb.kysely
1130
- .selectFrom('funding_transfers')
1544
+ .selectFrom('routes_transfers')
1131
1545
  .select('providerState')
1132
1546
  .where('id', '=', transfer.id)
1133
1547
  .executeTakeFirstOrThrow()
@@ -1149,12 +1563,15 @@ describe.skipIf(!Anvil.available)(
1149
1563
  if (typeof destinationBlockNumber !== 'string')
1150
1564
  throw new Error('Expected a backfilled destination block number.')
1151
1565
  expect(BigInt(destinationBlockNumber)).toBeLessThanOrEqual(destinationReceipt.blockNumber)
1152
- expect(messages).toEqual([{ transferId: transfer.id, type: 'funding:transfer:reconcile' }])
1153
- await tracker.reconcile(messages[0]!)
1566
+ expect(messages).toEqual([
1567
+ { transferId: foreign.id, type: 'routes:transfer:reconcile' },
1568
+ { transferId: transfer.id, type: 'routes:transfer:reconcile' },
1569
+ ])
1570
+ await tracker.reconcile(messages[1]!)
1154
1571
 
1155
1572
  const completed = await TestApp.json(
1156
- await app.request(`/v1/funding/transfers/${transfer.id}`, as(fundingWriter)),
1157
- Funding.schema.getFundingTransfer.Response,
1573
+ await app.request(`/v1/routes/transfers/${transfer.id}`, as(routesWriter)),
1574
+ Routes.schema.getRoutesTransfer.Response,
1158
1575
  )
1159
1576
  expect({
1160
1577
  destinationTransactionHashes: completed.destinationTransactionHashes,
@@ -1166,24 +1583,47 @@ describe.skipIf(!Anvil.available)(
1166
1583
  version: 3,
1167
1584
  })
1168
1585
 
1586
+ const events = await trackingDb.kysely
1587
+ .selectFrom('webhook_queue_events')
1588
+ .select(['envelope', 'subscriptionId'])
1589
+ .where(
1590
+ 'subscriptionId',
1591
+ 'in',
1592
+ subscriptions.map(({ id }) => id),
1593
+ )
1594
+ .execute()
1595
+ for (const [index, record] of [foreign, transfer].entries()) {
1596
+ const own = events.filter(
1597
+ ({ subscriptionId }) => subscriptionId === subscriptions[index]!.id,
1598
+ )
1599
+ expect(own.map(({ envelope }) => envelope.data)).toEqual(
1600
+ expect.arrayContaining([
1601
+ expect.objectContaining({ id: record.id, status: 'awaiting-source', version: 1 }),
1602
+ expect.objectContaining({ id: record.id, status: 'processing', version: 2 }),
1603
+ expect.objectContaining({ id: record.id, status: 'completed', version: 3 }),
1604
+ ]),
1605
+ )
1606
+ expect(own).toHaveLength(3)
1607
+ }
1608
+
1169
1609
  dispatchEnabled = false
1170
1610
  const replayed = await TestApp.json(
1171
1611
  await app.request(path, registration),
1172
- Funding.schema.registerFundingTransferSourceTransaction.Response,
1612
+ Routes.schema.registerRoutesTransferSourceTransaction.Response,
1173
1613
  )
1174
1614
  expect({ status: replayed.status, version: replayed.version }).toEqual({
1175
1615
  status: 'completed',
1176
1616
  version: 3,
1177
1617
  })
1178
1618
 
1179
- const other = await FundingTransfer.create(trackingDb, {
1180
- apiKeyId: fundingWriter.id,
1619
+ const other = await RoutesTransfer.create(trackingDb, {
1620
+ apiKeyId: routesWriter.id,
1181
1621
  environment: 'production',
1182
- orgId: fundingWriter.orgId,
1622
+ orgId: routesWriter.orgId,
1183
1623
  snapshot,
1184
1624
  })
1185
1625
  const conflict = await app.request(
1186
- `/v1/funding/transfers/${other.id}/source-transactions`,
1626
+ `/v1/routes/transfers/${other.id}/source-transactions`,
1187
1627
  registration,
1188
1628
  )
1189
1629
  expect(conflict.status).toBe(409)
@@ -1191,17 +1631,17 @@ describe.skipIf(!Anvil.available)(
1191
1631
  'source_transaction_conflict',
1192
1632
  )
1193
1633
 
1194
- const pending = await FundingTransfer.create(trackingDb, {
1195
- apiKeyId: fundingWriter.id,
1634
+ const pending = await RoutesTransfer.create(trackingDb, {
1635
+ apiKeyId: routesWriter.id,
1196
1636
  environment: 'production',
1197
- orgId: fundingWriter.orgId,
1637
+ orgId: routesWriter.orgId,
1198
1638
  snapshot,
1199
1639
  })
1200
1640
  const pendingResponse = await app.request(
1201
- `/v1/funding/transfers/${pending.id}/source-transactions`,
1641
+ `/v1/routes/transfers/${pending.id}/source-transactions`,
1202
1642
  {
1203
1643
  ...registration,
1204
- body: JSON.stringify({ transactionHash: `0x${'99'.repeat(32)}` }),
1644
+ body: JSON.stringify({ transactionHashes: [`0x${'99'.repeat(32)}`] }),
1205
1645
  },
1206
1646
  )
1207
1647
  expect(pendingResponse.status).toBe(409)
@@ -1209,25 +1649,186 @@ describe.skipIf(!Anvil.available)(
1209
1649
  'source_transaction_pending',
1210
1650
  )
1211
1651
  })
1652
+
1653
+ test('registers all five Tempo Stargate calls and rejects mismatched fee terms', async () => {
1654
+ const sourceTransactionHashes = [
1655
+ '0xec00883936af1eb22402c471cf05827ac161d78e832ea771ff3b936d4e5d7517',
1656
+ '0x55e22e5308d6261bb3ef04233cf29d85f342b5bf7ab2db0e34382be52a029b7e',
1657
+ '0x75761272e9a8365636b0f4038f9908f18461d4f7083c6aa610831b81db681812',
1658
+ '0xe840e103cfe4d62f9de0439f14f1010bae8f455585797577cb85fef3a77bc56b',
1659
+ '0x56c9ad54ea6213b2091434efa82dcc072a800876c85c1a2068d1202af18e73b6',
1660
+ ]
1661
+ const routeConfiguration = {
1662
+ destinationPoolAddress: '0x27a16dc786820B16E5c9028b75B99F6f604b5d26',
1663
+ poolAddress: '0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392',
1664
+ }
1665
+ const amount = {
1666
+ baseUnits: '200000',
1667
+ currency: 'USD',
1668
+ decimals: 6,
1669
+ formatted: '0.2',
1670
+ } as const
1671
+ const fee = {
1672
+ amount: { baseUnits: '500000', currency: 'USD', decimals: 6, formatted: '0.5' },
1673
+ side: 'source',
1674
+ token: {
1675
+ address: '0x20c0000000000000000000000000000000000000',
1676
+ currency: 'USD',
1677
+ decimals: 6,
1678
+ name: 'pathUSD',
1679
+ standard: 'TIP-20',
1680
+ symbol: 'pathUSD',
1681
+ tokenKey: 'eip155:4217/erc20:0x20c0000000000000000000000000000000000000',
1682
+ verified: true,
1683
+ },
1684
+ type: 'provider',
1685
+ } as const
1686
+ const snapshot = TestRoutes.transferSnapshot({
1687
+ destinationAmount: {
1688
+ baseUnits: '199979',
1689
+ currency: 'USD',
1690
+ decimals: 6,
1691
+ formatted: '0.199979',
1692
+ },
1693
+ destinationAmountMin: {
1694
+ baseUnits: '199979',
1695
+ currency: 'USD',
1696
+ decimals: 6,
1697
+ formatted: '0.199979',
1698
+ },
1699
+ destinationChain: {
1700
+ addressFormat: 'hex',
1701
+ id: 'eip155:8453',
1702
+ kind: 'evm',
1703
+ name: 'Base',
1704
+ },
1705
+ destinationToken: {
1706
+ address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
1707
+ currency: 'USD',
1708
+ decimals: 6,
1709
+ name: 'USD Coin',
1710
+ standard: 'ERC-20',
1711
+ symbol: 'USDC',
1712
+ tokenKey: 'eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
1713
+ verified: true,
1714
+ },
1715
+ fees: [fee],
1716
+ provider: { id: 'stargate', name: 'Stargate' },
1717
+ quote: {
1718
+ expiresAt: '2026-09-03T16:04:31.000Z',
1719
+ sampledAt: '2026-09-03T16:03:31.000Z',
1720
+ },
1721
+ recipient: '0xa8a89a017cd431c31af421f7d829e9418a5016ba',
1722
+ sender: '0x3063088c6f4ac4dd3f96a411d5fb68173d113def',
1723
+ sourceAmount: amount,
1724
+ sourceChain: {
1725
+ addressFormat: 'hex',
1726
+ id: 'eip155:4217',
1727
+ kind: 'evm',
1728
+ name: 'Tempo',
1729
+ },
1730
+ sourceToken: {
1731
+ address: '0x20c000000000000000000000b9537d11c60e8b50',
1732
+ currency: 'USD',
1733
+ decimals: 6,
1734
+ name: 'Bridged USDC (Stargate)',
1735
+ standard: 'TIP-20',
1736
+ symbol: 'USDC.e',
1737
+ tokenKey: 'eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50',
1738
+ verified: true,
1739
+ },
1740
+ })
1741
+ const app = TestApp.create({
1742
+ auth: { keys: [routesWriter] },
1743
+ db: trackingDb,
1744
+ defaultChainId: tempoMainnet.id,
1745
+ providers: [
1746
+ Stargate.stargate({
1747
+ fetch(input, init) {
1748
+ // Anvil cannot decode Tempo type-0x76 receipts, so immutable source evidence is read from production.
1749
+ return globalThis.fetch(
1750
+ typeof input === 'string' && input.startsWith(destination.rpcUrl)
1751
+ ? tempoMainnet.rpcUrls.default.http[0]!
1752
+ : input,
1753
+ init,
1754
+ )
1755
+ },
1756
+ }),
1757
+ ],
1758
+ routes: { async dispatchTransferReconciliation() {} },
1759
+ })
1760
+ const transfer = await RoutesTransfer.create(trackingDb, {
1761
+ apiKeyId: routesWriter.id,
1762
+ environment: 'production',
1763
+ orgId: routesWriter.orgId,
1764
+ providerState: { destinationBlockNumber: '0', routeConfiguration },
1765
+ snapshot,
1766
+ })
1767
+ const response = await app.request(
1768
+ `/v1/routes/transfers/${transfer.id}/source-transactions`,
1769
+ {
1770
+ body: JSON.stringify({ transactionHashes: sourceTransactionHashes }),
1771
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
1772
+ method: 'POST',
1773
+ },
1774
+ )
1775
+ expect(response.status).toBe(200)
1776
+ const registered = await TestApp.json(
1777
+ response,
1778
+ Routes.schema.registerRoutesTransferSourceTransaction.Response,
1779
+ )
1780
+ expect(registered.sourceTransactionHashes).toEqual([
1781
+ sourceTransactionHashes[4],
1782
+ ...sourceTransactionHashes.slice(0, 4),
1783
+ ])
1784
+
1785
+ const malformed = await RoutesTransfer.create(trackingDb, {
1786
+ apiKeyId: routesWriter.id,
1787
+ environment: 'production',
1788
+ orgId: routesWriter.orgId,
1789
+ providerState: { destinationBlockNumber: '0', routeConfiguration },
1790
+ snapshot: {
1791
+ ...snapshot,
1792
+ fees: [
1793
+ {
1794
+ ...fee,
1795
+ amount: { ...fee.amount, baseUnits: '500001', formatted: '0.500001' },
1796
+ },
1797
+ ],
1798
+ },
1799
+ })
1800
+ const rejected = await app.request(
1801
+ `/v1/routes/transfers/${malformed.id}/source-transactions`,
1802
+ {
1803
+ body: JSON.stringify({ transactionHashes: sourceTransactionHashes }),
1804
+ headers: { ...as(routesWriter).headers, 'content-type': 'application/json' },
1805
+ method: 'POST',
1806
+ },
1807
+ )
1808
+ expect(rejected.status).toBe(400)
1809
+ expect(((await rejected.json()) as { error: { code: string } }).error.code).toBe(
1810
+ 'source_transaction_invalid',
1811
+ )
1812
+ })
1212
1813
  },
1213
1814
  )
1214
1815
 
1215
- describe('POST /funding/transfers/{destination}', () => {
1816
+ describe('POST /routes/transfers/{destination}', () => {
1216
1817
  test.each([
1217
1818
  { body: vaultTransferBody, destination: 'vault' },
1218
1819
  { body: zoneTransferBody, destination: 'zone' },
1219
1820
  ] as const)('validates and returns 501 for $destination transfers', async (options) => {
1220
1821
  const app = transfersApp()
1221
1822
  const before = await db.kysely
1222
- .selectFrom('funding_transfers')
1823
+ .selectFrom('routes_transfers')
1223
1824
  .select(({ fn }) => fn.countAll<number>().as('count'))
1224
1825
  .executeTakeFirstOrThrow()
1225
- const response = await app.request(`/v1/funding/transfers/${options.destination}`, {
1826
+ const response = await app.request(`/v1/routes/transfers/${options.destination}`, {
1226
1827
  body: JSON.stringify(options.body),
1227
1828
  headers: {
1228
- authorization: `Bearer ${fundingWriter.token}`,
1829
+ authorization: `Bearer ${routesWriter.token}`,
1229
1830
  'content-type': 'application/json',
1230
- 'idempotency-key': `funding_${options.destination}_unimplemented`,
1831
+ 'idempotency-key': `routes_${options.destination}_unimplemented`,
1231
1832
  },
1232
1833
  method: 'POST',
1233
1834
  })
@@ -1238,7 +1839,7 @@ describe('POST /funding/transfers/{destination}', () => {
1238
1839
  'not_implemented',
1239
1840
  )
1240
1841
  const after = await db.kysely
1241
- .selectFrom('funding_transfers')
1842
+ .selectFrom('routes_transfers')
1242
1843
  .select(({ fn }) => fn.countAll<number>().as('count'))
1243
1844
  .executeTakeFirstOrThrow()
1244
1845
  expect(after.count).toBe(before.count)
@@ -1246,10 +1847,10 @@ describe('POST /funding/transfers/{destination}', () => {
1246
1847
 
1247
1848
  test('requires the header and validates destination fields', async () => {
1248
1849
  const app = transfersApp()
1249
- const missingHeader = await app.request('/v1/funding/transfers/vault', {
1850
+ const missingHeader = await app.request('/v1/routes/transfers/vault', {
1250
1851
  body: JSON.stringify(vaultTransferBody),
1251
1852
  headers: {
1252
- authorization: `Bearer ${fundingWriter.token}`,
1853
+ authorization: `Bearer ${routesWriter.token}`,
1253
1854
  'content-type': 'application/json',
1254
1855
  },
1255
1856
  method: 'POST',
@@ -1259,12 +1860,12 @@ describe('POST /funding/transfers/{destination}', () => {
1259
1860
  'idempotency_key_required',
1260
1861
  )
1261
1862
 
1262
- const invalidZone = await app.request('/v1/funding/transfers/zone', {
1863
+ const invalidZone = await app.request('/v1/routes/transfers/zone', {
1263
1864
  body: JSON.stringify({ ...zoneTransferBody, destinationChain: '421700001' }),
1264
1865
  headers: {
1265
- authorization: `Bearer ${fundingWriter.token}`,
1866
+ authorization: `Bearer ${routesWriter.token}`,
1266
1867
  'content-type': 'application/json',
1267
- 'idempotency-key': 'funding_zone_invalid',
1868
+ 'idempotency-key': 'routes_zone_invalid',
1268
1869
  },
1269
1870
  method: 'POST',
1270
1871
  })
@@ -1275,21 +1876,21 @@ describe('POST /funding/transfers/{destination}', () => {
1275
1876
  })
1276
1877
  })
1277
1878
 
1278
- describe('funding transfers OpenAPI', () => {
1879
+ describe('route transfers OpenAPI', () => {
1279
1880
  test('publishes generator-ready transfer contracts', async () => {
1280
1881
  const spec = await (await create({ auth: false }).request('/openapi.json')).json()
1281
1882
  const detailPath = Object.keys(spec.paths).find(
1282
- (path) => path.includes('/v1/funding/transfers/{') && !path.endsWith('/source-transactions'),
1883
+ (path) => path.includes('/v1/routes/transfers/{') && !path.endsWith('/source-transactions'),
1283
1884
  )!
1284
1885
  const sourceTransactionPath = Object.keys(spec.paths).find((path) =>
1285
1886
  path.endsWith('/source-transactions'),
1286
1887
  )!
1287
1888
  const operations = {
1288
- create: spec.paths['/v1/funding/transfers'].post,
1289
- createVault: spec.paths['/v1/funding/transfers/vault'].post,
1290
- createZone: spec.paths['/v1/funding/transfers/zone'].post,
1889
+ create: spec.paths['/v1/routes/transfers'].post,
1890
+ createVault: spec.paths['/v1/routes/transfers/vault'].post,
1891
+ createZone: spec.paths['/v1/routes/transfers/zone'].post,
1291
1892
  get: spec.paths[detailPath].get,
1292
- list: spec.paths['/v1/funding/transfers'].get,
1893
+ list: spec.paths['/v1/routes/transfers'].get,
1293
1894
  registerSourceTransaction: spec.paths[sourceTransactionPath].post,
1294
1895
  }
1295
1896
  const summarize = (operation: (typeof operations)[keyof typeof operations]) => ({
@@ -1308,34 +1909,34 @@ describe('funding transfers OpenAPI', () => {
1308
1909
 
1309
1910
  expect({
1310
1911
  components: [
1311
- 'CreateFundingTransferRequest',
1312
- 'CreateFundingTransferVaultRequest',
1313
- 'CreateFundingTransferZoneRequest',
1314
- 'CreatedFundingTransfer',
1315
- 'FundingAction',
1316
- 'FundingActionCall',
1317
- 'FundingEvmCallsAction',
1318
- 'FundingTransfer',
1319
- 'FundingTransferFee',
1320
- 'FundingTransferList',
1321
- 'FundingTransferQuote',
1322
- 'FundingTransferStatusReason',
1323
- 'RegisterFundingTransferSourceTransactionRequest',
1912
+ 'CreateRoutesTransferRequest',
1913
+ 'CreateRoutesTransferVaultRequest',
1914
+ 'CreateRoutesTransferZoneRequest',
1915
+ 'CreatedRoutesTransfer',
1916
+ 'RoutesAction',
1917
+ 'RoutesActionCall',
1918
+ 'RoutesEvmCallsAction',
1919
+ 'RoutesTransfer',
1920
+ 'RoutesTransferFee',
1921
+ 'RoutesTransferList',
1922
+ 'RoutesTransferQuote',
1923
+ 'RoutesTransferStatusReason',
1924
+ 'RegisterRoutesTransferSourceTransactionRequest',
1324
1925
  ].filter((name) => spec.components.schemas[name]),
1325
1926
  formats: {
1326
- createdAt: spec.components.schemas.FundingTransfer.properties.createdAt.format,
1327
- quoteExpiresAt: spec.components.schemas.FundingTransferQuote.properties.expiresAt.format,
1328
- quoteSampledAt: spec.components.schemas.FundingTransferQuote.properties.sampledAt.format,
1329
- updatedAt: spec.components.schemas.FundingTransfer.properties.updatedAt.format,
1927
+ createdAt: spec.components.schemas.RoutesTransfer.properties.createdAt.format,
1928
+ quoteExpiresAt: spec.components.schemas.RoutesTransferQuote.properties.expiresAt.format,
1929
+ quoteSampledAt: spec.components.schemas.RoutesTransferQuote.properties.sampledAt.format,
1930
+ updatedAt: spec.components.schemas.RoutesTransfer.properties.updatedAt.format,
1330
1931
  },
1331
1932
  models: {
1332
- action: spec.components.schemas.FundingAction.oneOf,
1333
- createdAction: spec.components.schemas.CreatedFundingTransfer.properties.action,
1334
- evmCall: spec.components.schemas.FundingEvmCallsAction.properties.calls.items,
1335
- feeToken: spec.components.schemas.FundingTransferFee.properties.token,
1336
- list: spec.components.schemas.FundingTransferList.properties.data.items,
1337
- quote: spec.components.schemas.FundingTransfer.properties.quote,
1338
- statusReason: spec.components.schemas.FundingTransfer.properties.statusReason,
1933
+ action: spec.components.schemas.RoutesAction.oneOf,
1934
+ createdAction: spec.components.schemas.CreatedRoutesTransfer.properties.action,
1935
+ evmCall: spec.components.schemas.RoutesEvmCallsAction.properties.calls.items,
1936
+ feeToken: spec.components.schemas.RoutesTransferFee.properties.token,
1937
+ list: spec.components.schemas.RoutesTransferList.properties.data.items,
1938
+ quote: spec.components.schemas.RoutesTransfer.properties.quote,
1939
+ statusReason: spec.components.schemas.RoutesTransfer.properties.statusReason,
1339
1940
  },
1340
1941
  operations: {
1341
1942
  create: summarize(operations.create),
@@ -1363,25 +1964,24 @@ describe('funding transfers OpenAPI', () => {
1363
1964
  quoteNotAvailable:
1364
1965
  spec.components.schemas.QuoteNotAvailableError.properties.error.properties.code.enum,
1365
1966
  transferNotFound:
1366
- spec.components.schemas.FundingTransferNotFoundError.properties.error.properties.code
1367
- .enum,
1967
+ spec.components.schemas.RoutesTransferNotFoundError.properties.error.properties.code.enum,
1368
1968
  },
1369
1969
  }).toMatchInlineSnapshot(`
1370
1970
  {
1371
1971
  "components": [
1372
- "CreateFundingTransferRequest",
1373
- "CreateFundingTransferVaultRequest",
1374
- "CreateFundingTransferZoneRequest",
1375
- "CreatedFundingTransfer",
1376
- "FundingAction",
1377
- "FundingActionCall",
1378
- "FundingEvmCallsAction",
1379
- "FundingTransfer",
1380
- "FundingTransferFee",
1381
- "FundingTransferList",
1382
- "FundingTransferQuote",
1383
- "FundingTransferStatusReason",
1384
- "RegisterFundingTransferSourceTransactionRequest",
1972
+ "CreateRoutesTransferRequest",
1973
+ "CreateRoutesTransferVaultRequest",
1974
+ "CreateRoutesTransferZoneRequest",
1975
+ "CreatedRoutesTransfer",
1976
+ "RoutesAction",
1977
+ "RoutesActionCall",
1978
+ "RoutesEvmCallsAction",
1979
+ "RoutesTransfer",
1980
+ "RoutesTransferFee",
1981
+ "RoutesTransferList",
1982
+ "RoutesTransferQuote",
1983
+ "RoutesTransferStatusReason",
1984
+ "RegisterRoutesTransferSourceTransactionRequest",
1385
1985
  ],
1386
1986
  "formats": {
1387
1987
  "createdAt": "date-time",
@@ -1392,26 +1992,26 @@ describe('funding transfers OpenAPI', () => {
1392
1992
  "models": {
1393
1993
  "action": [
1394
1994
  {
1395
- "$ref": "#/components/schemas/FundingEvmCallsAction",
1995
+ "$ref": "#/components/schemas/RoutesEvmCallsAction",
1396
1996
  },
1397
1997
  ],
1398
1998
  "createdAction": {
1399
- "$ref": "#/components/schemas/FundingAction",
1999
+ "$ref": "#/components/schemas/RoutesAction",
1400
2000
  },
1401
2001
  "evmCall": {
1402
- "$ref": "#/components/schemas/FundingActionCall",
2002
+ "$ref": "#/components/schemas/RoutesActionCall",
1403
2003
  },
1404
2004
  "feeToken": {
1405
- "$ref": "#/components/schemas/FundingToken",
2005
+ "$ref": "#/components/schemas/RoutesToken",
1406
2006
  },
1407
2007
  "list": {
1408
- "$ref": "#/components/schemas/FundingTransfer",
2008
+ "$ref": "#/components/schemas/RoutesTransfer",
1409
2009
  },
1410
2010
  "quote": {
1411
- "$ref": "#/components/schemas/FundingTransferQuote",
2011
+ "$ref": "#/components/schemas/RoutesTransferQuote",
1412
2012
  },
1413
2013
  "statusReason": {
1414
- "$ref": "#/components/schemas/FundingTransferStatusReason",
2014
+ "$ref": "#/components/schemas/RoutesTransferStatusReason",
1415
2015
  },
1416
2016
  },
1417
2017
  "operationErrorCodes": {
@@ -1439,7 +2039,7 @@ describe('funding transfers OpenAPI', () => {
1439
2039
  "quote_not_available",
1440
2040
  ],
1441
2041
  "transferNotFound": [
1442
- "funding_transfer_not_found",
2042
+ "routes_transfer_not_found",
1443
2043
  ],
1444
2044
  },
1445
2045
  "operations": {
@@ -1452,7 +2052,7 @@ describe('funding transfers OpenAPI', () => {
1452
2052
  "$ref": "#/components/schemas/AuthenticationError",
1453
2053
  },
1454
2054
  "403": {
1455
- "$ref": "#/components/schemas/ForbiddenError",
2055
+ "$ref": "#/components/schemas/ApiKeyForbiddenOrApiKeyIpForbiddenOrBillingPastDueOrBillingRequiredOrForbiddenOrRoutesSubsidyLimitExceededOrRoutesSubsidyNotEnabledError",
1456
2056
  },
1457
2057
  "404": {
1458
2058
  "$ref": "#/components/schemas/QuoteNotAvailableError",
@@ -1462,17 +2062,23 @@ describe('funding transfers OpenAPI', () => {
1462
2062
  },
1463
2063
  "429": "#/components/responses/RateLimited",
1464
2064
  "500": "#/components/responses/InternalError",
2065
+ "501": {
2066
+ "$ref": "#/components/schemas/NotImplemented501Error",
2067
+ },
1465
2068
  "502": {
1466
2069
  "$ref": "#/components/schemas/UpstreamError",
1467
2070
  },
2071
+ "503": {
2072
+ "$ref": "#/components/schemas/SubsidyInventoryUnavailableOrSubsidySignerUnavailableError",
2073
+ },
1468
2074
  "504": "#/components/responses/RequestTimeout",
1469
2075
  },
1470
- "operationId": "createFundingTransfer",
2076
+ "operationId": "createRoutesTransfer",
1471
2077
  "request": {
1472
- "$ref": "#/components/schemas/CreateFundingTransferRequest",
2078
+ "$ref": "#/components/schemas/CreateRoutesTransferRequest",
1473
2079
  },
1474
2080
  "response": {
1475
- "$ref": "#/components/schemas/CreatedFundingTransfer",
2081
+ "$ref": "#/components/schemas/CreatedRoutesTransfer",
1476
2082
  },
1477
2083
  },
1478
2084
  "createVault": {
@@ -1493,9 +2099,9 @@ describe('funding transfers OpenAPI', () => {
1493
2099
  },
1494
2100
  "504": "#/components/responses/RequestTimeout",
1495
2101
  },
1496
- "operationId": "createFundingTransferVault",
2102
+ "operationId": "createRoutesTransferVault",
1497
2103
  "request": {
1498
- "$ref": "#/components/schemas/CreateFundingTransferVaultRequest",
2104
+ "$ref": "#/components/schemas/CreateRoutesTransferVaultRequest",
1499
2105
  },
1500
2106
  "response": undefined,
1501
2107
  },
@@ -1517,9 +2123,9 @@ describe('funding transfers OpenAPI', () => {
1517
2123
  },
1518
2124
  "504": "#/components/responses/RequestTimeout",
1519
2125
  },
1520
- "operationId": "createFundingTransferZone",
2126
+ "operationId": "createRoutesTransferZone",
1521
2127
  "request": {
1522
- "$ref": "#/components/schemas/CreateFundingTransferZoneRequest",
2128
+ "$ref": "#/components/schemas/CreateRoutesTransferZoneRequest",
1523
2129
  },
1524
2130
  "response": undefined,
1525
2131
  },
@@ -1535,7 +2141,7 @@ describe('funding transfers OpenAPI', () => {
1535
2141
  "$ref": "#/components/schemas/ForbiddenError",
1536
2142
  },
1537
2143
  "404": {
1538
- "$ref": "#/components/schemas/FundingTransferNotFoundError",
2144
+ "$ref": "#/components/schemas/RoutesTransferNotFoundError",
1539
2145
  },
1540
2146
  "429": "#/components/responses/RateLimited",
1541
2147
  "500": "#/components/responses/InternalError",
@@ -1544,10 +2150,10 @@ describe('funding transfers OpenAPI', () => {
1544
2150
  },
1545
2151
  "504": "#/components/responses/RequestTimeout",
1546
2152
  },
1547
- "operationId": "getFundingTransfer",
2153
+ "operationId": "getRoutesTransfer",
1548
2154
  "request": undefined,
1549
2155
  "response": {
1550
- "$ref": "#/components/schemas/FundingTransfer",
2156
+ "$ref": "#/components/schemas/RoutesTransfer",
1551
2157
  },
1552
2158
  },
1553
2159
  "list": {
@@ -1568,10 +2174,10 @@ describe('funding transfers OpenAPI', () => {
1568
2174
  },
1569
2175
  "504": "#/components/responses/RequestTimeout",
1570
2176
  },
1571
- "operationId": "listFundingTransfers",
2177
+ "operationId": "listRoutesTransfers",
1572
2178
  "request": undefined,
1573
2179
  "response": {
1574
- "$ref": "#/components/schemas/FundingTransferList",
2180
+ "$ref": "#/components/schemas/RoutesTransferList",
1575
2181
  },
1576
2182
  },
1577
2183
  "registerSourceTransaction": {
@@ -1586,10 +2192,10 @@ describe('funding transfers OpenAPI', () => {
1586
2192
  "$ref": "#/components/schemas/ForbiddenError",
1587
2193
  },
1588
2194
  "404": {
1589
- "$ref": "#/components/schemas/FundingTransferNotFoundError",
2195
+ "$ref": "#/components/schemas/RoutesTransferNotFoundError",
1590
2196
  },
1591
2197
  "409": {
1592
- "$ref": "#/components/schemas/FundingTransferNotAwaitingSourceOrSourceTransactionConflictOrSourceTransactionPendingError",
2198
+ "$ref": "#/components/schemas/RoutesTransferNotAwaitingSourceOrSourceTransactionConflictOrSourceTransactionPendingError",
1593
2199
  },
1594
2200
  "429": "#/components/responses/RateLimited",
1595
2201
  "500": "#/components/responses/InternalError",
@@ -1601,12 +2207,12 @@ describe('funding transfers OpenAPI', () => {
1601
2207
  },
1602
2208
  "504": "#/components/responses/RequestTimeout",
1603
2209
  },
1604
- "operationId": "registerFundingTransferSourceTransaction",
2210
+ "operationId": "registerRoutesTransferSourceTransaction",
1605
2211
  "request": {
1606
- "$ref": "#/components/schemas/RegisterFundingTransferSourceTransactionRequest",
2212
+ "$ref": "#/components/schemas/RegisterRoutesTransferSourceTransactionRequest",
1607
2213
  },
1608
2214
  "response": {
1609
- "$ref": "#/components/schemas/FundingTransfer",
2215
+ "$ref": "#/components/schemas/RoutesTransfer",
1610
2216
  },
1611
2217
  },
1612
2218
  },
@@ -1616,7 +2222,24 @@ describe('funding transfers OpenAPI', () => {
1616
2222
 
1617
2223
  test('publishes an executable account transfer example', async () => {
1618
2224
  const app = create({ auth: false })
1619
- type TransferExample = { destinationToken: { address: string } }
2225
+ type TransferExample = {
2226
+ action: RoutesAction.EvmCalls
2227
+ destinationAmountMin: { baseUnits: string }
2228
+ destinationAmountRequired: { baseUnits: string }
2229
+ destinationChain: { id: string }
2230
+ destinationToken: { address: string }
2231
+ fees: readonly {
2232
+ amount: { baseUnits: string }
2233
+ side: string
2234
+ token: { address?: string | undefined; standard: string; symbol: string }
2235
+ }[]
2236
+ recipient: string
2237
+ sender: string
2238
+ sourceAmount: { baseUnits: string }
2239
+ sourceChain: { id: string }
2240
+ sourceToken: { address: string }
2241
+ subsidize: boolean
2242
+ }
1620
2243
  type Operation = {
1621
2244
  requestBody: {
1622
2245
  content: Record<string, { schema: JsonSchema }>
@@ -1625,16 +2248,127 @@ describe('funding transfers OpenAPI', () => {
1625
2248
  }
1626
2249
  type Spec = ComponentSpec & { paths: Record<string, { post: Operation }> }
1627
2250
  const spec = (await (await app.request('/openapi.json')).json()) as Spec
1628
- const operation = spec.paths['/v1/funding/transfers']!.post
2251
+ const operation = spec.paths['/v1/routes/transfers']!.post
1629
2252
  const request = dereference(spec, operation.requestBody.content['application/json']!.schema)
2253
+ const response = operation.responses['200']!.content['application/json']!.example
1630
2254
 
1631
2255
  expect({
1632
- request: request.properties!['destinationToken']!.examples,
1633
- response:
1634
- operation.responses['200']!.content['application/json']!.example.destinationToken.address,
2256
+ request: {
2257
+ destinationChain: request.properties!['destinationChain']!.examples,
2258
+ destinationToken: request.properties!['destinationToken']!.examples,
2259
+ sourceChain: request.properties!['sourceChain']!.examples,
2260
+ sourceToken: request.properties!['sourceToken']!.examples,
2261
+ subsidyDescription: request.properties!['subsidize']!.description,
2262
+ subsidize: request.properties!['subsidize']!.examples,
2263
+ },
2264
+ response,
1635
2265
  }).toEqual({
1636
- request: ['pathusd'],
1637
- response: '0x20c0000000000000000000000000000000000000',
2266
+ request: {
2267
+ destinationChain: ['base', 'ethereum', 'tempo', 'eip155:8453'],
2268
+ destinationToken: ['usdc', 'pathusd'],
2269
+ sourceChain: ['tempo', 'base', 'ethereum', 'eip155:4217'],
2270
+ sourceToken: ['usdce', 'usdc'],
2271
+ subsidyDescription:
2272
+ 'Guarantees normalized 1:1 delivery for exact-source Tempo USD transfers to Base or Ethereum. A Tempo Admin must enable subsidies for the organization.',
2273
+ subsidize: [true],
2274
+ },
2275
+ response: expect.objectContaining({
2276
+ action: expect.objectContaining({ type: 'evm:calls' }),
2277
+ destinationAmountMin: expect.objectContaining({ baseUnits: '974398' }),
2278
+ destinationAmountRequired: expect.objectContaining({ baseUnits: '1000000' }),
2279
+ destinationChain: expect.objectContaining({ id: 'eip155:8453' }),
2280
+ destinationToken: expect.objectContaining({
2281
+ address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
2282
+ }),
2283
+ fees: [
2284
+ expect.objectContaining({
2285
+ amount: expect.objectContaining({ baseUnits: '10000' }),
2286
+ side: 'source',
2287
+ token: expect.objectContaining({
2288
+ address: '0x20c0000000000000000000000000000000000000',
2289
+ standard: 'TIP-20',
2290
+ symbol: 'pathUSD',
2291
+ }),
2292
+ }),
2293
+ ],
2294
+ sourceChain: expect.objectContaining({ id: 'eip155:4217' }),
2295
+ sourceToken: expect.objectContaining({
2296
+ address: '0x20c000000000000000000000b9537d11c60e8b50',
2297
+ }),
2298
+ subsidize: true,
2299
+ }),
2300
+ })
2301
+ const calls = response.action.calls
2302
+ expect(calls).toHaveLength(5)
2303
+ expect(calls.map((call) => ({ to: call.to, value: call.value }))).toEqual([
2304
+ { to: response.sourceToken.address, value: '0x0' },
2305
+ { to: response.fees[0]!.token.address, value: '0x0' },
2306
+ { to: '0x0cEb237E109eE22374a567c6b09F373C73FA4cBb', value: '0x0' },
2307
+ { to: '0x0cEb237E109eE22374a567c6b09F373C73FA4cBb', value: '0x0' },
2308
+ { to: '0x8c76e2f6c5ceda9aa7772e7eff30280226c44392', value: '0x0' },
2309
+ ])
2310
+ const approve = AbiFunction.from('function approve(address spender, uint256 amount)')
2311
+ const wrap = AbiFunction.from('function wrap(address token, address to, uint256 amount)')
2312
+ const send = AbiFunction.from(
2313
+ 'function send((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam, (uint256 nativeFee, uint256 lzTokenFee) fee, address refundAddress) payable returns ((bytes32 guid, uint64 nonce, (uint256 nativeFee, uint256 lzTokenFee) fee) msgReceipt, (uint256 amountSentLD, uint256 amountReceivedLD) oftReceipt)',
2314
+ )
2315
+ expect(AbiFunction.decodeData(approve, calls[0]!.data as Hex.Hex)).toEqual([
2316
+ '0x8c76e2f6c5ceda9aa7772e7eff30280226c44392',
2317
+ 1_000_000n,
2318
+ ])
2319
+ expect(AbiFunction.decodeData(approve, calls[1]!.data as Hex.Hex)).toEqual([
2320
+ '0x0ceb237e109ee22374a567c6b09f373c73fa4cbb',
2321
+ 10_000n,
2322
+ ])
2323
+ expect(AbiFunction.decodeData(wrap, calls[2]!.data as Hex.Hex)).toEqual([
2324
+ '0x20c0000000000000000000000000000000000000',
2325
+ response.sender,
2326
+ 10_000n,
2327
+ ])
2328
+ expect(AbiFunction.decodeData(approve, calls[3]!.data as Hex.Hex)).toEqual([
2329
+ '0x8c76e2f6c5ceda9aa7772e7eff30280226c44392',
2330
+ 10_000n,
2331
+ ])
2332
+ const [sendParameters, messagingFee, refundAddress] = AbiFunction.decodeData(
2333
+ send,
2334
+ calls[4]!.data as Hex.Hex,
2335
+ )
2336
+ expect(sendParameters).toMatchObject({
2337
+ amountLD: 1_000_000n,
2338
+ dstEid: 30_184,
2339
+ minAmountLD: 974_398n,
2340
+ })
2341
+ expect(messagingFee).toEqual({ lzTokenFee: 0n, nativeFee: 10_000n })
2342
+ expect(refundAddress).toBe(response.sender)
2343
+ expect(
2344
+ Stargate.validateAction({
2345
+ action: response.action,
2346
+ configuration: {
2347
+ destinationPoolAddress: '0x27a16dc786820b16e5c9028b75b99f6f604b5d26',
2348
+ poolAddress: '0x8c76e2f6c5ceda9aa7772e7eff30280226c44392',
2349
+ },
2350
+ destinationAmountMin: response.destinationAmountMin.baseUnits,
2351
+ destinationChainId: response.destinationChain.id,
2352
+ destinationTokenAddress: response.destinationToken.address,
2353
+ recipient: response.recipient,
2354
+ sender: response.sender,
2355
+ sourceAmount: response.sourceAmount.baseUnits,
2356
+ sourceChainId: response.sourceChain.id,
2357
+ sourceTokenAddress: response.sourceToken.address,
2358
+ }),
2359
+ ).toEqual({
2360
+ tokenApprovals: [
2361
+ {
2362
+ amount: '10000',
2363
+ spender: '0x0cEb237E109eE22374a567c6b09F373C73FA4cBb',
2364
+ token: '0x20C0000000000000000000000000000000000000',
2365
+ },
2366
+ {
2367
+ amount: '10000',
2368
+ spender: '0x8c76e2F6C5ceDA9AA7772e7efF30280226c44392',
2369
+ token: '0x0cEb237E109eE22374a567c6b09F373C73FA4cBb',
2370
+ },
2371
+ ],
1638
2372
  })
1639
2373
  })
1640
2374
 
@@ -1649,7 +2383,7 @@ describe('funding transfers OpenAPI', () => {
1649
2383
  const spec = (await (await app.request('/openapi.json')).json()) as Spec
1650
2384
  const properties = dereference(
1651
2385
  spec,
1652
- spec.paths['/v1/funding/deposit-addresses']!.post.requestBody.content['application/json']!
2386
+ spec.paths['/v1/routes/deposit-addresses']!.post.requestBody.content['application/json']!
1653
2387
  .schema,
1654
2388
  ).properties!
1655
2389
 
@@ -1683,19 +2417,19 @@ describe('funding transfers OpenAPI', () => {
1683
2417
  }
1684
2418
  const spec = (await (await app.request('/openapi.json')).json()) as Spec
1685
2419
  const detailPath = Object.keys(spec.paths).find(
1686
- (path) => path.includes('/v1/funding/transfers/{') && !path.endsWith('/source-transactions'),
2420
+ (path) => path.includes('/v1/routes/transfers/{') && !path.endsWith('/source-transactions'),
1687
2421
  )!
1688
2422
  const sourceTransactionPath = Object.keys(spec.paths).find((path) =>
1689
2423
  path.endsWith('/source-transactions'),
1690
2424
  )!
1691
2425
  const depositAddressPath = Object.keys(spec.paths).find(
1692
2426
  (path) =>
1693
- path.includes('/v1/funding/deposit-addresses/{') &&
2427
+ path.includes('/v1/routes/deposit-addresses/{') &&
1694
2428
  !path.endsWith('/deposits') &&
1695
2429
  !path.endsWith('/reconcile'),
1696
2430
  )!
1697
2431
  const depositPath = Object.keys(spec.paths).find((path) =>
1698
- path.includes('/v1/funding/deposits/{'),
2432
+ path.includes('/v1/routes/deposits/{'),
1699
2433
  )!
1700
2434
  const reconcilePath = Object.keys(spec.paths).find((path) => path.endsWith('/reconcile'))!
1701
2435
  const summarize = (operation: Operation) => ({
@@ -1703,23 +2437,23 @@ describe('funding transfers OpenAPI', () => {
1703
2437
  summary: operation.summary,
1704
2438
  })
1705
2439
  const operations = {
1706
- account: summarize(spec.paths['/v1/funding/transfers']!.post!),
1707
- chains: summarize(spec.paths['/v1/funding/chains']!.get!),
2440
+ account: summarize(spec.paths['/v1/routes/transfers']!.post!),
2441
+ chains: summarize(spec.paths['/v1/routes/chains']!.get!),
1708
2442
  deposit: summarize(spec.paths[depositPath]!.get!),
1709
2443
  depositAddress: summarize(spec.paths[depositAddressPath]!.get!),
1710
- depositAddressCreate: summarize(spec.paths['/v1/funding/deposit-addresses']!.post!),
1711
- depositAddressList: summarize(spec.paths['/v1/funding/deposit-addresses']!.get!),
2444
+ depositAddressCreate: summarize(spec.paths['/v1/routes/deposit-addresses']!.post!),
2445
+ depositAddressList: summarize(spec.paths['/v1/routes/deposit-addresses']!.get!),
1712
2446
  depositAddressReconcile: summarize(spec.paths[reconcilePath]!.post!),
1713
- deposits: summarize(spec.paths['/v1/funding/deposits']!.get!),
2447
+ deposits: summarize(spec.paths['/v1/routes/deposits']!.get!),
1714
2448
  detail: summarize(spec.paths[detailPath]!.get!),
1715
- list: summarize(spec.paths['/v1/funding/transfers']!.get!),
1716
- providers: summarize(spec.paths['/v1/funding/providers']!.get!),
1717
- quotes: summarize(spec.paths['/v1/funding/quotes']!.get!),
2449
+ list: summarize(spec.paths['/v1/routes/transfers']!.get!),
2450
+ providers: summarize(spec.paths['/v1/routes/providers']!.get!),
2451
+ quotes: summarize(spec.paths['/v1/routes/quotes']!.get!),
1718
2452
  sourceTransaction: summarize(spec.paths[sourceTransactionPath]!.post!),
1719
- vault: summarize(spec.paths['/v1/funding/transfers/vault']!.post!),
1720
- zone: summarize(spec.paths['/v1/funding/transfers/zone']!.post!),
2453
+ vault: summarize(spec.paths['/v1/routes/transfers/vault']!.post!),
2454
+ zone: summarize(spec.paths['/v1/routes/transfers/zone']!.post!),
1721
2455
  }
1722
- const category = spec['x-tagGroups'].find((group) => group.name === 'Funding & Bridge API')
2456
+ const category = spec['x-tagGroups'].find((group) => group.name === 'Routes API')
1723
2457
  const categoryTags = new Set(category?.tags)
1724
2458
  const sidebar = Object.values(spec.paths).flatMap((path) =>
1725
2459
  [path.get, path.post].flatMap((operation) =>
@@ -1736,10 +2470,10 @@ describe('funding transfers OpenAPI', () => {
1736
2470
  sidebar,
1737
2471
  }).toMatchInlineSnapshot(`
1738
2472
  {
1739
- "category": "Funding & Bridge API",
2473
+ "category": "Routes API",
1740
2474
  "operations": {
1741
2475
  "account": {
1742
- "description": "Creates a transfer to a Tempo account.",
2476
+ "description": "Creates a durable transfer between supported chains, including optional normalized destination subsidies.",
1743
2477
  "summary": "Create transfer",
1744
2478
  },
1745
2479
  "chains": {
@@ -1751,19 +2485,19 @@ describe('funding transfers OpenAPI', () => {
1751
2485
  "summary": "Get deposit",
1752
2486
  },
1753
2487
  "depositAddress": {
1754
- "description": "Returns one reusable funding deposit address.",
2488
+ "description": "Returns one reusable route deposit address.",
1755
2489
  "summary": "Get deposit address",
1756
2490
  },
1757
2491
  "depositAddressCreate": {
1758
- "description": "Creates or returns a reusable deposit address for funding a Tempo account.",
2492
+ "description": "Creates or returns a reusable deposit address for routes a Tempo account.",
1759
2493
  "summary": "Create deposit address",
1760
2494
  },
1761
2495
  "depositAddressList": {
1762
- "description": "Lists reusable funding deposit addresses, newest first.",
2496
+ "description": "Lists reusable route deposit addresses, newest first.",
1763
2497
  "summary": "List deposit addresses",
1764
2498
  },
1765
2499
  "depositAddressReconcile": {
1766
- "description": "Queues an immediate provider reconciliation for one reusable funding deposit address.",
2500
+ "description": "Queues an immediate provider reconciliation for one reusable route deposit address.",
1767
2501
  "summary": "Reconcile deposit address",
1768
2502
  },
1769
2503
  "deposits": {
@@ -1783,11 +2517,11 @@ describe('funding transfers OpenAPI', () => {
1783
2517
  "summary": "Get providers",
1784
2518
  },
1785
2519
  "quotes": {
1786
- "description": "Returns live quotes for transferring stablecoins to Tempo.",
2520
+ "description": "Returns live quotes for transferring stablecoins between supported chains.",
1787
2521
  "summary": "Get quotes",
1788
2522
  },
1789
2523
  "sourceTransaction": {
1790
- "description": "Registers and verifies a funding source transaction.",
2524
+ "description": "Registers verified source transactions once per organization, environment, and project.",
1791
2525
  "summary": "Register source transaction",
1792
2526
  },
1793
2527
  "vault": {
@@ -1839,7 +2573,7 @@ describe('funding transfers OpenAPI', () => {
1839
2573
  const creationSchema = (path: string) =>
1840
2574
  dereference(spec, spec.paths[path]!.post!.requestBody.content['application/json']!.schema)
1841
2575
  expect(
1842
- creationSchema('/v1/funding/deposit-addresses').required?.includes('subsidize') ?? false,
2576
+ creationSchema('/v1/routes/deposit-addresses').required?.includes('subsidize') ?? false,
1843
2577
  ).toMatchInlineSnapshot(`false`)
1844
2578
  const summarize = (path: string) => {
1845
2579
  const schema = creationSchema(path)
@@ -1850,21 +2584,22 @@ describe('funding transfers OpenAPI', () => {
1850
2584
  }
1851
2585
 
1852
2586
  expect({
1853
- account: summarize('/v1/funding/transfers'),
1854
- depositAddress: summarize('/v1/funding/deposit-addresses'),
2587
+ account: summarize('/v1/routes/transfers'),
2588
+ depositAddress: summarize('/v1/routes/deposit-addresses'),
1855
2589
  depositAddressResponses: Object.keys(
1856
- spec.paths['/v1/funding/deposit-addresses']!.post!.responses,
2590
+ spec.paths['/v1/routes/deposit-addresses']!.post!.responses,
1857
2591
  ),
1858
- vault: summarize('/v1/funding/transfers/vault'),
1859
- vaultResponses: Object.keys(spec.paths['/v1/funding/transfers/vault']!.post!.responses),
1860
- zone: summarize('/v1/funding/transfers/zone'),
1861
- zoneResponses: Object.keys(spec.paths['/v1/funding/transfers/zone']!.post!.responses),
2592
+ vault: summarize('/v1/routes/transfers/vault'),
2593
+ vaultResponses: Object.keys(spec.paths['/v1/routes/transfers/vault']!.post!.responses),
2594
+ zone: summarize('/v1/routes/transfers/zone'),
2595
+ zoneResponses: Object.keys(spec.paths['/v1/routes/transfers/zone']!.post!.responses),
1862
2596
  }).toMatchInlineSnapshot(`
1863
2597
  {
1864
2598
  "account": {
1865
2599
  "additionalProperties": false,
1866
2600
  "properties": [
1867
2601
  "amount",
2602
+ "destinationChain",
1868
2603
  "destinationToken",
1869
2604
  "mode",
1870
2605
  "provider",
@@ -1873,6 +2608,7 @@ describe('funding transfers OpenAPI', () => {
1873
2608
  "slippageBps",
1874
2609
  "sourceChain",
1875
2610
  "sourceToken",
2611
+ "subsidize",
1876
2612
  ],
1877
2613
  },
1878
2614
  "depositAddress": {
@@ -1962,13 +2698,13 @@ describe('funding transfers OpenAPI', () => {
1962
2698
  }
1963
2699
  >
1964
2700
  }
1965
- const operation = spec.paths['/v1/funding/transfers']!.get
2701
+ const operation = spec.paths['/v1/routes/transfers']!.get
1966
2702
  const fields = Object.keys(operation.responses['200']!.content['application/json']!.example.data[0]!) // prettier-ignore
1967
2703
  expect(fields).toEqual([...fields].sort())
1968
2704
  for (const route of [
1969
2705
  operation,
1970
- spec.paths['/v1/funding/deposit-addresses']!.get,
1971
- spec.paths['/v1/funding/deposits']!.get,
2706
+ spec.paths['/v1/routes/deposit-addresses']!.get,
2707
+ spec.paths['/v1/routes/deposits']!.get,
1972
2708
  ])
1973
2709
  for (const parameter of route.parameters ?? [])
1974
2710
  expect(parameter.example ?? parameter.schema?.examples, parameter.name).toBeDefined()
@@ -1990,70 +2726,70 @@ describe('funding transfers OpenAPI', () => {
1990
2726
  >
1991
2727
  }
1992
2728
  const detailPath = Object.keys(spec.paths).find(
1993
- (path) => path.includes('/v1/funding/transfers/{') && !path.endsWith('/source-transactions'),
2729
+ (path) => path.includes('/v1/routes/transfers/{') && !path.endsWith('/source-transactions'),
1994
2730
  )
1995
2731
  const sourceTransactionPath = Object.keys(spec.paths).find((path) =>
1996
2732
  path.endsWith('/source-transactions'),
1997
2733
  )
1998
2734
  const depositAddressPath = Object.keys(spec.paths).find(
1999
2735
  (path) =>
2000
- path.includes('/v1/funding/deposit-addresses/{') &&
2736
+ path.includes('/v1/routes/deposit-addresses/{') &&
2001
2737
  !path.endsWith('/deposits') &&
2002
2738
  !path.endsWith('/reconcile'),
2003
2739
  )
2004
2740
  const depositPath = Object.keys(spec.paths).find((path) =>
2005
- path.includes('/v1/funding/deposits/{'),
2741
+ path.includes('/v1/routes/deposits/{'),
2006
2742
  )
2007
2743
  const reconcilePath = Object.keys(spec.paths).find((path) => path.endsWith('/reconcile'))
2008
2744
  expect({
2009
2745
  deposit: depositPath && spec.paths[depositPath]?.get.operationId,
2010
2746
  depositAddress: depositAddressPath && spec.paths[depositAddressPath]?.get.operationId,
2011
- depositAddressCreate: spec.paths['/v1/funding/deposit-addresses']?.post?.operationId,
2747
+ depositAddressCreate: spec.paths['/v1/routes/deposit-addresses']?.post?.operationId,
2012
2748
  depositAddressCreateScopes:
2013
- spec.paths['/v1/funding/deposit-addresses']?.post?.['x-required-scopes'],
2014
- depositAddressList: spec.paths['/v1/funding/deposit-addresses']?.get.operationId,
2749
+ spec.paths['/v1/routes/deposit-addresses']?.post?.['x-required-scopes'],
2750
+ depositAddressList: spec.paths['/v1/routes/deposit-addresses']?.get.operationId,
2015
2751
  depositAddressListScopes:
2016
- spec.paths['/v1/funding/deposit-addresses']?.get['x-required-scopes'],
2752
+ spec.paths['/v1/routes/deposit-addresses']?.get['x-required-scopes'],
2017
2753
  depositAddressReconcile: reconcilePath && spec.paths[reconcilePath]?.post?.operationId,
2018
2754
  depositAddressReconcileScopes:
2019
2755
  reconcilePath && spec.paths[reconcilePath]?.post?.['x-required-scopes'],
2020
2756
  depositAddressScopes:
2021
2757
  depositAddressPath && spec.paths[depositAddressPath]?.get['x-required-scopes'],
2022
2758
  depositScopes: depositPath && spec.paths[depositPath]?.get['x-required-scopes'],
2023
- deposits: spec.paths['/v1/funding/deposits']?.get.operationId,
2024
- depositsScopes: spec.paths['/v1/funding/deposits']?.get['x-required-scopes'],
2759
+ deposits: spec.paths['/v1/routes/deposits']?.get.operationId,
2760
+ depositsScopes: spec.paths['/v1/routes/deposits']?.get['x-required-scopes'],
2025
2761
  detail: detailPath && spec.paths[detailPath]?.get.operationId,
2026
2762
  detailScopes: detailPath && spec.paths[detailPath]?.get['x-required-scopes'],
2027
- list: spec.paths['/v1/funding/transfers']?.get.operationId,
2028
- listScopes: spec.paths['/v1/funding/transfers']?.get['x-required-scopes'],
2029
- requiredCreateHeader: spec.paths['/v1/funding/transfers']?.post?.parameters.find(
2763
+ list: spec.paths['/v1/routes/transfers']?.get.operationId,
2764
+ listScopes: spec.paths['/v1/routes/transfers']?.get['x-required-scopes'],
2765
+ requiredCreateHeader: spec.paths['/v1/routes/transfers']?.post?.parameters.find(
2030
2766
  (parameter) => parameter.name === 'idempotency-key',
2031
2767
  ),
2032
2768
  sourceTransaction:
2033
2769
  sourceTransactionPath && spec.paths[sourceTransactionPath]?.post?.operationId,
2034
2770
  sourceTransactionScopes:
2035
2771
  sourceTransactionPath && spec.paths[sourceTransactionPath]?.post?.['x-required-scopes'],
2036
- vault: spec.paths['/v1/funding/transfers/vault']?.post?.operationId,
2037
- vaultScopes: spec.paths['/v1/funding/transfers/vault']?.post?.['x-required-scopes'],
2038
- zone: spec.paths['/v1/funding/transfers/zone']?.post?.operationId,
2039
- zoneScopes: spec.paths['/v1/funding/transfers/zone']?.post?.['x-required-scopes'],
2772
+ vault: spec.paths['/v1/routes/transfers/vault']?.post?.operationId,
2773
+ vaultScopes: spec.paths['/v1/routes/transfers/vault']?.post?.['x-required-scopes'],
2774
+ zone: spec.paths['/v1/routes/transfers/zone']?.post?.operationId,
2775
+ zoneScopes: spec.paths['/v1/routes/transfers/zone']?.post?.['x-required-scopes'],
2040
2776
  }).toEqual({
2041
- deposit: 'getFundingDeposit',
2042
- depositAddress: 'getFundingDepositAddress',
2043
- depositAddressCreate: 'createFundingDepositAddress',
2044
- depositAddressCreateScopes: ['funding:write'],
2045
- depositAddressList: 'listFundingDepositAddresses',
2046
- depositAddressListScopes: ['funding:read'],
2047
- depositAddressReconcile: 'reconcileFundingDepositAddress',
2048
- depositAddressReconcileScopes: ['funding:write'],
2049
- depositAddressScopes: ['funding:read'],
2050
- depositScopes: ['funding:read'],
2051
- deposits: 'listFundingDeposits',
2052
- depositsScopes: ['funding:read'],
2053
- detail: 'getFundingTransfer',
2054
- detailScopes: ['funding:read'],
2055
- list: 'listFundingTransfers',
2056
- listScopes: ['funding:read'],
2777
+ deposit: 'getRoutesDeposit',
2778
+ depositAddress: 'getRoutesDepositAddress',
2779
+ depositAddressCreate: 'createRoutesDepositAddress',
2780
+ depositAddressCreateScopes: ['routes:write'],
2781
+ depositAddressList: 'listRoutesDepositAddresses',
2782
+ depositAddressListScopes: ['routes:read'],
2783
+ depositAddressReconcile: 'reconcileRoutesDepositAddress',
2784
+ depositAddressReconcileScopes: ['routes:write'],
2785
+ depositAddressScopes: ['routes:read'],
2786
+ depositScopes: ['routes:read'],
2787
+ deposits: 'listRoutesDeposits',
2788
+ depositsScopes: ['routes:read'],
2789
+ detail: 'getRoutesTransfer',
2790
+ detailScopes: ['routes:read'],
2791
+ list: 'listRoutesTransfers',
2792
+ listScopes: ['routes:read'],
2057
2793
  requiredCreateHeader: expect.objectContaining({
2058
2794
  description: expect.stringContaining('24 hours'),
2059
2795
  in: 'header',
@@ -2061,69 +2797,80 @@ describe('funding transfers OpenAPI', () => {
2061
2797
  required: true,
2062
2798
  schema: expect.any(Object),
2063
2799
  }),
2064
- sourceTransaction: 'registerFundingTransferSourceTransaction',
2065
- sourceTransactionScopes: ['funding:write'],
2066
- vault: 'createFundingTransferVault',
2067
- vaultScopes: ['funding:write'],
2068
- zone: 'createFundingTransferZone',
2069
- zoneScopes: ['funding:write'],
2800
+ sourceTransaction: 'registerRoutesTransferSourceTransaction',
2801
+ sourceTransactionScopes: ['routes:write'],
2802
+ vault: 'createRoutesTransferVault',
2803
+ vaultScopes: ['routes:write'],
2804
+ zone: 'createRoutesTransferZone',
2805
+ zoneScopes: ['routes:write'],
2070
2806
  })
2071
2807
  })
2072
2808
  })
2073
2809
 
2074
- const fundingReader = {
2075
- id: 'key_funding',
2076
- orgId: 'org_funding',
2077
- scopes: ['funding:read'],
2078
- token: 'secret_funding_reader',
2810
+ const routesReader = {
2811
+ id: 'key_routes',
2812
+ orgId: 'org_routes',
2813
+ scopes: ['routes:read'],
2814
+ token: 'secret_routes_reader',
2079
2815
  } satisfies TestApp.kvStore.Key
2080
2816
 
2081
- const fundingProjectReader = {
2082
- id: 'key_funding_project',
2083
- orgId: 'org_funding',
2084
- projectId: 'prj_funding',
2085
- scopes: ['funding:read'],
2086
- token: 'secret_funding_project',
2817
+ const routesProjectReader = {
2818
+ id: 'key_routes_project',
2819
+ orgId: 'org_routes',
2820
+ projectId: 'prj_routes',
2821
+ scopes: ['routes:read'],
2822
+ token: 'secret_routes_project',
2087
2823
  } satisfies TestApp.kvStore.Key
2088
2824
 
2089
- const fundingForeign = {
2090
- id: 'key_funding_other',
2091
- orgId: 'org_funding_other',
2092
- scopes: ['funding:read', 'funding:write'],
2093
- token: 'secret_funding_other',
2825
+ const routesForeign = {
2826
+ id: 'key_routes_other',
2827
+ orgId: 'org_routes_other',
2828
+ scopes: ['routes:read', 'routes:write'],
2829
+ token: 'secret_routes_other',
2094
2830
  } satisfies TestApp.kvStore.Key
2095
2831
 
2096
- const fundingWriter = {
2097
- id: 'key_funding_writer',
2098
- orgId: 'org_funding',
2099
- scopes: ['funding:read', 'funding:write'],
2100
- token: 'secret_funding_writer',
2832
+ const routesWriter = {
2833
+ id: 'key_routes_writer',
2834
+ orgId: 'org_routes',
2835
+ scopes: ['routes:read', 'routes:write'],
2836
+ token: 'secret_routes_writer',
2101
2837
  } satisfies TestApp.kvStore.Key
2102
2838
 
2103
2839
  function transfersApp(options: TestApp.create.Options = {}) {
2104
2840
  return create({
2105
2841
  ...options,
2106
2842
  auth: {
2107
- keys: [TestApp.key, fundingReader, fundingProjectReader, fundingForeign, fundingWriter],
2843
+ keys: [TestApp.key, routesReader, routesProjectReader, routesForeign, routesWriter],
2108
2844
  },
2845
+ db: options.db ?? db,
2109
2846
  defaultChainId: options.defaultChainId ?? tempoMainnet.id,
2110
2847
  })
2111
2848
  }
2112
2849
 
2850
+ function transferSubsidy(maxAmount: string): RoutesTransferSubsidy.Inventory {
2851
+ return {
2852
+ account: `0x${'77'.repeat(20)}`,
2853
+ async check() {
2854
+ return { nativeAmount: '100' }
2855
+ },
2856
+ policy: { maxAmount },
2857
+ }
2858
+ }
2859
+
2113
2860
  function as(key: { token: string }) {
2114
2861
  return { headers: { authorization: `Bearer ${key.token}` } } as const
2115
2862
  }
2116
2863
 
2117
2864
  let seedSecond = 0
2118
2865
 
2119
- function seedTransfer(input: Partial<FundingTransfer.create.Input> = {}) {
2866
+ function seedTransfer(input: Partial<RoutesTransfer.create.Input> = {}) {
2120
2867
  // Distinct create times keep ids strictly ordered for paging assertions.
2121
- return FundingTransfer.create(db, {
2122
- apiKeyId: fundingReader.id,
2868
+ return RoutesTransfer.create(db, {
2869
+ apiKeyId: routesReader.id,
2123
2870
  environment: 'production',
2124
- id: FundingTransfer.generateId(new Date(Date.UTC(2026, 0, 2, 0, 0, seedSecond++))),
2125
- orgId: fundingReader.orgId,
2126
- snapshot: TestFunding.transferSnapshot(),
2871
+ id: RoutesTransfer.generateId(new Date(Date.UTC(2026, 0, 2, 0, 0, seedSecond++))),
2872
+ orgId: routesReader.orgId,
2873
+ snapshot: TestRoutes.transferSnapshot(),
2127
2874
  ...input,
2128
2875
  })
2129
2876
  }