tapimo 0.7.1 → 0.8.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 (786) hide show
  1. package/README.md +10 -7
  2. package/dist/ApiKey.d.ts +3 -0
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +10 -0
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +29 -8
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +24 -5
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +20680 -15487
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +250 -129
  13. package/dist/App.js.map +1 -1
  14. package/dist/Assets.d.ts +10 -0
  15. package/dist/Assets.d.ts.map +1 -1
  16. package/dist/Assets.js +17 -4
  17. package/dist/Assets.js.map +1 -1
  18. package/dist/Scope.d.ts +19 -8
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +25 -9
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +1 -1
  23. package/dist/admin/App.d.ts +667 -76
  24. package/dist/admin/App.d.ts.map +1 -1
  25. package/dist/admin/App.js +12 -2
  26. package/dist/admin/App.js.map +1 -1
  27. package/dist/admin/apps/api-keys.d.ts +107 -11
  28. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  29. package/dist/admin/apps/api-keys.js +145 -32
  30. package/dist/admin/apps/api-keys.js.map +1 -1
  31. package/dist/admin/apps/earn-vaults.d.ts +1069 -0
  32. package/dist/admin/apps/earn-vaults.d.ts.map +1 -0
  33. package/dist/admin/apps/earn-vaults.js +409 -0
  34. package/dist/admin/apps/earn-vaults.js.map +1 -0
  35. package/dist/admin/apps/organizations.d.ts +2 -0
  36. package/dist/admin/apps/organizations.d.ts.map +1 -1
  37. package/dist/admin/apps/verified-tokens.d.ts +74 -74
  38. package/dist/admin/ui.generated.d.ts +1 -1
  39. package/dist/admin/ui.generated.d.ts.map +1 -1
  40. package/dist/admin/ui.generated.js +99 -43
  41. package/dist/admin/ui.generated.js.map +1 -1
  42. package/dist/analytics/Analytics.d.ts +4 -0
  43. package/dist/analytics/Analytics.d.ts.map +1 -1
  44. package/dist/analytics/Analytics.js +21 -2
  45. package/dist/analytics/Analytics.js.map +1 -1
  46. package/dist/analytics/tables/requestEvents.d.ts +22 -1
  47. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  48. package/dist/analytics/tables/requestEvents.js +59 -39
  49. package/dist/analytics/tables/requestEvents.js.map +1 -1
  50. package/dist/apps/Zones.d.ts +8335 -0
  51. package/dist/apps/Zones.d.ts.map +1 -0
  52. package/dist/apps/Zones.js +38 -0
  53. package/dist/apps/Zones.js.map +1 -0
  54. package/dist/apps/data/App.d.ts +5666 -3934
  55. package/dist/apps/data/App.d.ts.map +1 -1
  56. package/dist/apps/data/App.js +90 -22
  57. package/dist/apps/data/App.js.map +1 -1
  58. package/dist/apps/data/FxOracle.d.ts +1 -1
  59. package/dist/apps/data/FxOracle.d.ts.map +1 -1
  60. package/dist/apps/data/index.d.ts +0 -1
  61. package/dist/apps/data/index.d.ts.map +1 -1
  62. package/dist/apps/data/index.js +0 -1
  63. package/dist/apps/data/index.js.map +1 -1
  64. package/dist/apps/data/routes/activities.d.ts +56 -56
  65. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  66. package/dist/apps/data/routes/activities.js +130 -65
  67. package/dist/apps/data/routes/activities.js.map +1 -1
  68. package/dist/apps/data/routes/addresses.d.ts +65 -65
  69. package/dist/apps/data/routes/balances.d.ts +14 -14
  70. package/dist/apps/data/routes/balances.js +5 -5
  71. package/dist/apps/data/routes/balances.js.map +1 -1
  72. package/dist/apps/data/routes/blocks.d.ts +39 -39
  73. package/dist/apps/data/routes/coingecko.d.ts +54 -54
  74. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  75. package/dist/apps/data/routes/coingecko.js +62 -35
  76. package/dist/apps/data/routes/coingecko.js.map +1 -1
  77. package/dist/apps/data/routes/earn.d.ts +2574 -0
  78. package/dist/apps/data/routes/earn.d.ts.map +1 -0
  79. package/dist/apps/data/routes/earn.js +1574 -0
  80. package/dist/apps/data/routes/earn.js.map +1 -0
  81. package/dist/apps/data/routes/exchanges.d.ts +1626 -719
  82. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  83. package/dist/apps/data/routes/exchanges.js +656 -193
  84. package/dist/apps/data/routes/exchanges.js.map +1 -1
  85. package/dist/apps/data/routes/fee-amm.d.ts +108 -108
  86. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  87. package/dist/apps/data/routes/fee-amm.js +59 -8
  88. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  89. package/dist/apps/data/routes/indexer.d.ts +35 -35
  90. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  91. package/dist/apps/data/routes/indexer.js +23 -3
  92. package/dist/apps/data/routes/indexer.js.map +1 -1
  93. package/dist/apps/data/routes/receipts.d.ts +33 -33
  94. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  95. package/dist/apps/data/routes/receipts.js +11 -11
  96. package/dist/apps/data/routes/receipts.js.map +1 -1
  97. package/dist/apps/data/routes/rpc.d.ts +6 -6
  98. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  99. package/dist/apps/data/routes/rpc.js +214 -22
  100. package/dist/apps/data/routes/rpc.js.map +1 -1
  101. package/dist/apps/data/routes/tokenlist.d.ts +1 -1
  102. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  103. package/dist/apps/data/routes/tokenlist.js +39 -8
  104. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  105. package/dist/apps/data/routes/tokens.d.ts +115 -87
  106. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  107. package/dist/apps/data/routes/tokens.js +194 -91
  108. package/dist/apps/data/routes/tokens.js.map +1 -1
  109. package/dist/apps/data/routes/transactions.d.ts +54 -54
  110. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  111. package/dist/apps/data/routes/transactions.js +78 -39
  112. package/dist/apps/data/routes/transactions.js.map +1 -1
  113. package/dist/apps/data/routes/transfers.d.ts +76 -27
  114. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  115. package/dist/apps/data/routes/transfers.js +165 -67
  116. package/dist/apps/data/routes/transfers.js.map +1 -1
  117. package/dist/apps/data/routes/valuation.d.ts +42 -42
  118. package/dist/apps/data/routes/valuation.js +2 -2
  119. package/dist/apps/data/routes/valuation.js.map +1 -1
  120. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  121. package/dist/apps/data/routes/webhooks.d.ts +370 -368
  122. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  123. package/dist/apps/data/routes/webhooks.js +161 -59
  124. package/dist/apps/data/routes/webhooks.js.map +1 -1
  125. package/dist/apps/data/routes/zones.d.ts +281 -0
  126. package/dist/apps/data/routes/zones.d.ts.map +1 -0
  127. package/dist/apps/data/routes/zones.js +252 -0
  128. package/dist/apps/data/routes/zones.js.map +1 -0
  129. package/dist/apps/docs/reference.js +6 -5
  130. package/dist/apps/docs/reference.js.map +1 -1
  131. package/dist/apps/funding/Access.d.ts +16 -0
  132. package/dist/apps/funding/Access.d.ts.map +1 -0
  133. package/dist/apps/funding/Access.js +22 -0
  134. package/dist/apps/funding/Access.js.map +1 -0
  135. package/dist/apps/funding/App.d.ts +3183 -0
  136. package/dist/apps/funding/App.d.ts.map +1 -0
  137. package/dist/apps/funding/App.js +46 -0
  138. package/dist/apps/funding/App.js.map +1 -0
  139. package/dist/apps/funding/Inventory.d.ts +5 -0
  140. package/dist/apps/funding/Inventory.d.ts.map +1 -0
  141. package/dist/apps/funding/Inventory.js +25 -0
  142. package/dist/apps/funding/Inventory.js.map +1 -0
  143. package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
  144. package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
  145. package/dist/apps/funding/ProviderAttempts.js +24 -0
  146. package/dist/apps/funding/ProviderAttempts.js.map +1 -0
  147. package/dist/apps/funding/index.d.ts +2 -0
  148. package/dist/apps/funding/index.d.ts.map +1 -0
  149. package/dist/apps/funding/index.js +2 -0
  150. package/dist/apps/funding/index.js.map +1 -0
  151. package/dist/apps/funding/routes/chains.d.ts +169 -0
  152. package/dist/apps/funding/routes/chains.d.ts.map +1 -0
  153. package/dist/apps/funding/routes/chains.js +80 -0
  154. package/dist/apps/funding/routes/chains.js.map +1 -0
  155. package/dist/apps/funding/routes/deposit-addresses.d.ts +2388 -0
  156. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
  157. package/dist/apps/funding/routes/deposit-addresses.js +860 -0
  158. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
  159. package/dist/apps/funding/routes/providers.d.ts +91 -0
  160. package/dist/apps/funding/routes/providers.d.ts.map +1 -0
  161. package/dist/apps/funding/routes/providers.js +56 -0
  162. package/dist/apps/funding/routes/providers.js.map +1 -0
  163. package/dist/apps/{data/routes/funding.d.ts → funding/routes/quotes.d.ts} +26 -396
  164. package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
  165. package/dist/apps/funding/routes/quotes.js +188 -0
  166. package/dist/apps/funding/routes/quotes.js.map +1 -0
  167. package/dist/apps/funding/routes/transfers.d.ts +1999 -0
  168. package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
  169. package/dist/apps/funding/routes/transfers.js +614 -0
  170. package/dist/apps/funding/routes/transfers.js.map +1 -0
  171. package/dist/apps/index.d.ts +3 -1
  172. package/dist/apps/index.d.ts.map +1 -1
  173. package/dist/apps/index.js +3 -1
  174. package/dist/apps/index.js.map +1 -1
  175. package/dist/apps/management/App.d.ts +839 -574
  176. package/dist/apps/management/App.d.ts.map +1 -1
  177. package/dist/apps/management/Billing.d.ts +8 -0
  178. package/dist/apps/management/Billing.d.ts.map +1 -1
  179. package/dist/apps/management/Billing.js +12 -0
  180. package/dist/apps/management/Billing.js.map +1 -1
  181. package/dist/apps/management/Error.d.ts +12 -0
  182. package/dist/apps/management/Error.d.ts.map +1 -0
  183. package/dist/apps/management/Error.js +10 -0
  184. package/dist/apps/management/Error.js.map +1 -0
  185. package/dist/apps/management/routes/api-keys.d.ts +296 -19
  186. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  187. package/dist/apps/management/routes/api-keys.js +77 -9
  188. package/dist/apps/management/routes/api-keys.js.map +1 -1
  189. package/dist/apps/management/routes/billing.d.ts +119 -119
  190. package/dist/apps/management/routes/faucet.d.ts +35 -35
  191. package/dist/apps/management/routes/invitations.d.ts +17 -17
  192. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  193. package/dist/apps/management/routes/invitations.js +4 -10
  194. package/dist/apps/management/routes/invitations.js.map +1 -1
  195. package/dist/apps/management/routes/invite-links.d.ts +38 -38
  196. package/dist/apps/management/routes/me.d.ts +6 -6
  197. package/dist/apps/management/routes/members.d.ts +69 -69
  198. package/dist/apps/management/routes/orgs.d.ts +16 -16
  199. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  200. package/dist/apps/management/routes/orgs.js +2 -8
  201. package/dist/apps/management/routes/orgs.js.map +1 -1
  202. package/dist/apps/management/routes/projects.d.ts +19 -19
  203. package/dist/apps/management/routes/scopes.d.ts +1 -1
  204. package/dist/apps/management/routes/scopes.js +2 -2
  205. package/dist/apps/management/routes/scopes.js.map +1 -1
  206. package/dist/apps/management/routes/usage.d.ts +72 -72
  207. package/dist/apps/management/routes/verified-token-requests.d.ts +18 -18
  208. package/dist/apps/management/routes/webhooks.d.ts +122 -122
  209. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  210. package/dist/apps/management/routes/webhooks.js +15 -3
  211. package/dist/apps/management/routes/webhooks.js.map +1 -1
  212. package/dist/apps/mcp.d.ts +2 -3
  213. package/dist/apps/mcp.d.ts.map +1 -1
  214. package/dist/apps/mcp.js +16 -14
  215. package/dist/apps/mcp.js.map +1 -1
  216. package/dist/apps/metadata.d.ts +4 -0
  217. package/dist/apps/metadata.d.ts.map +1 -1
  218. package/dist/apps/metadata.js +10 -0
  219. package/dist/apps/metadata.js.map +1 -1
  220. package/dist/apps/mpp/App.d.ts +57 -58
  221. package/dist/apps/mpp/App.d.ts.map +1 -1
  222. package/dist/apps/mpp/App.js +192 -27
  223. package/dist/apps/mpp/App.js.map +1 -1
  224. package/dist/apps/mpp/Mpp.d.ts +0 -20
  225. package/dist/apps/mpp/Mpp.d.ts.map +1 -1
  226. package/dist/apps/mpp/Mpp.js +0 -24
  227. package/dist/apps/mpp/Mpp.js.map +1 -1
  228. package/dist/apps/mpp/internal/Adapter.d.ts +40 -0
  229. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -0
  230. package/dist/apps/mpp/internal/Adapter.js +66 -0
  231. package/dist/apps/mpp/internal/Adapter.js.map +1 -0
  232. package/dist/apps/mpp/internal/Error.d.ts +4 -0
  233. package/dist/apps/mpp/internal/Error.d.ts.map +1 -0
  234. package/dist/apps/mpp/internal/Error.js +41 -0
  235. package/dist/apps/mpp/internal/Error.js.map +1 -0
  236. package/dist/apps/mpp/internal/Idempotency.d.ts +33 -0
  237. package/dist/apps/mpp/internal/Idempotency.d.ts.map +1 -0
  238. package/dist/apps/mpp/internal/Idempotency.js +28 -0
  239. package/dist/apps/mpp/internal/Idempotency.js.map +1 -0
  240. package/dist/apps/mpp/internal/Policy.d.ts +46 -0
  241. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -0
  242. package/dist/apps/mpp/internal/Policy.js +57 -0
  243. package/dist/apps/mpp/internal/Policy.js.map +1 -0
  244. package/dist/apps/mpp/internal/ScreenManager.d.ts +27 -0
  245. package/dist/apps/mpp/internal/ScreenManager.d.ts.map +1 -0
  246. package/dist/apps/mpp/internal/ScreenManager.js +18 -0
  247. package/dist/apps/mpp/internal/ScreenManager.js.map +1 -0
  248. package/dist/apps/mpp/internal/Screening.d.ts +35 -0
  249. package/dist/apps/mpp/internal/Screening.d.ts.map +1 -0
  250. package/dist/apps/mpp/internal/Screening.js +20 -0
  251. package/dist/apps/mpp/internal/Screening.js.map +1 -0
  252. package/dist/apps/mpp/internal/Sponsorship.d.ts +36 -0
  253. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -0
  254. package/dist/apps/mpp/internal/Sponsorship.js +103 -0
  255. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -0
  256. package/dist/apps/mpp/internal/Utils.d.ts +7 -0
  257. package/dist/apps/mpp/internal/Utils.d.ts.map +1 -0
  258. package/dist/apps/mpp/internal/Utils.js +24 -0
  259. package/dist/apps/mpp/internal/Utils.js.map +1 -0
  260. package/dist/apps/relay/App.js +183 -23
  261. package/dist/apps/relay/App.js.map +1 -1
  262. package/dist/apps/relay/Sponsorships.d.ts +4 -0
  263. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  264. package/dist/apps/relay/Sponsorships.js +15 -6
  265. package/dist/apps/relay/Sponsorships.js.map +1 -1
  266. package/dist/cloudflare.d.ts +50 -0
  267. package/dist/cloudflare.d.ts.map +1 -1
  268. package/dist/cloudflare.js +178 -0
  269. package/dist/cloudflare.js.map +1 -1
  270. package/dist/db/Db.d.ts +22 -0
  271. package/dist/db/Db.d.ts.map +1 -1
  272. package/dist/db/Db.js +379 -0
  273. package/dist/db/Db.js.map +1 -1
  274. package/dist/db/Schema.d.ts +933 -1
  275. package/dist/db/Schema.d.ts.map +1 -1
  276. package/dist/db/Schema.js +289 -1
  277. package/dist/db/Schema.js.map +1 -1
  278. package/dist/db/tables/earnVaults.d.ts +68 -0
  279. package/dist/db/tables/earnVaults.d.ts.map +1 -0
  280. package/dist/db/tables/earnVaults.js +72 -0
  281. package/dist/db/tables/earnVaults.js.map +1 -0
  282. package/dist/db/tables/fundingCatalog.d.ts +56 -0
  283. package/dist/db/tables/fundingCatalog.d.ts.map +1 -0
  284. package/dist/db/tables/fundingCatalog.js +85 -0
  285. package/dist/db/tables/fundingCatalog.js.map +1 -0
  286. package/dist/db/tables/fundingDepositAddresses.d.ts +172 -0
  287. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -0
  288. package/dist/db/tables/fundingDepositAddresses.js +218 -0
  289. package/dist/db/tables/fundingDepositAddresses.js.map +1 -0
  290. package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
  291. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
  292. package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
  293. package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
  294. package/dist/db/tables/fundingDeposits.d.ts +171 -0
  295. package/dist/db/tables/fundingDeposits.d.ts.map +1 -0
  296. package/dist/db/tables/fundingDeposits.js +182 -0
  297. package/dist/db/tables/fundingDeposits.js.map +1 -0
  298. package/dist/db/tables/fundingIdempotency.d.ts +91 -0
  299. package/dist/db/tables/fundingIdempotency.d.ts.map +1 -0
  300. package/dist/db/tables/fundingIdempotency.js +103 -0
  301. package/dist/db/tables/fundingIdempotency.js.map +1 -0
  302. package/dist/db/tables/fundingTransferEvents.d.ts +12 -0
  303. package/dist/db/tables/fundingTransferEvents.d.ts.map +1 -0
  304. package/dist/db/tables/fundingTransferEvents.js +18 -0
  305. package/dist/db/tables/fundingTransferEvents.js.map +1 -0
  306. package/dist/db/tables/fundingTransferTransactions.d.ts +15 -0
  307. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -0
  308. package/dist/db/tables/fundingTransferTransactions.js +27 -0
  309. package/dist/db/tables/fundingTransferTransactions.js.map +1 -0
  310. package/dist/db/tables/fundingTransfers.d.ts +82 -0
  311. package/dist/db/tables/fundingTransfers.d.ts.map +1 -0
  312. package/dist/db/tables/fundingTransfers.js +89 -0
  313. package/dist/db/tables/fundingTransfers.js.map +1 -0
  314. package/dist/db/tables/organizations.d.ts +8 -0
  315. package/dist/db/tables/organizations.d.ts.map +1 -1
  316. package/dist/db/tables/organizations.js +16 -0
  317. package/dist/db/tables/organizations.js.map +1 -1
  318. package/dist/db/tables/projects.d.ts +8 -0
  319. package/dist/db/tables/projects.d.ts.map +1 -1
  320. package/dist/db/tables/projects.js +16 -0
  321. package/dist/db/tables/projects.js.map +1 -1
  322. package/dist/db/tables/requestUsage.d.ts +13 -0
  323. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  324. package/dist/db/tables/requestUsage.js +16 -0
  325. package/dist/db/tables/requestUsage.js.map +1 -1
  326. package/dist/db/tables/sponsoredTransactions.d.ts +2 -2
  327. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  328. package/dist/db/tables/sponsoredTransactions.js +1 -1
  329. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  330. package/dist/db/tables/users.d.ts +8 -0
  331. package/dist/db/tables/users.d.ts.map +1 -1
  332. package/dist/db/tables/users.js +16 -0
  333. package/dist/db/tables/users.js.map +1 -1
  334. package/dist/db/tables/verifiedTokens.d.ts +14 -7
  335. package/dist/db/tables/verifiedTokens.d.ts.map +1 -1
  336. package/dist/db/tables/verifiedTokens.js +9 -7
  337. package/dist/db/tables/verifiedTokens.js.map +1 -1
  338. package/dist/db/tables/webhookSubscriptions.d.ts +31 -1
  339. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  340. package/dist/db/tables/webhookSubscriptions.js +24 -3
  341. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  342. package/dist/handlers/relay.js +108 -108
  343. package/dist/handlers/relay.js.map +1 -1
  344. package/dist/internal/Auth.d.ts +37 -11
  345. package/dist/internal/Auth.d.ts.map +1 -1
  346. package/dist/internal/Auth.js +75 -16
  347. package/dist/internal/Auth.js.map +1 -1
  348. package/dist/internal/Cache.d.ts.map +1 -1
  349. package/dist/internal/Cache.js +17 -11
  350. package/dist/internal/Cache.js.map +1 -1
  351. package/dist/internal/Cursor.js +1 -1
  352. package/dist/internal/Cursor.js.map +1 -1
  353. package/dist/internal/EarnRates.d.ts +89 -0
  354. package/dist/internal/EarnRates.d.ts.map +1 -0
  355. package/dist/internal/EarnRates.js +266 -0
  356. package/dist/internal/EarnRates.js.map +1 -0
  357. package/dist/internal/EarnVaults.d.ts +171 -0
  358. package/dist/internal/EarnVaults.d.ts.map +1 -0
  359. package/dist/internal/EarnVaults.js +547 -0
  360. package/dist/internal/EarnVaults.js.map +1 -0
  361. package/dist/internal/EdgeCache.d.ts +10 -3
  362. package/dist/internal/EdgeCache.d.ts.map +1 -1
  363. package/dist/internal/EdgeCache.js +43 -11
  364. package/dist/internal/EdgeCache.js.map +1 -1
  365. package/dist/internal/Id.d.ts +11 -0
  366. package/dist/internal/Id.d.ts.map +1 -1
  367. package/dist/internal/Id.js +13 -0
  368. package/dist/internal/Id.js.map +1 -1
  369. package/dist/internal/Idempotency.d.ts +83 -0
  370. package/dist/internal/Idempotency.d.ts.map +1 -0
  371. package/dist/internal/Idempotency.js +113 -0
  372. package/dist/internal/Idempotency.js.map +1 -0
  373. package/dist/internal/IpAllowlist.d.ts +15 -0
  374. package/dist/internal/IpAllowlist.d.ts.map +1 -0
  375. package/dist/internal/IpAllowlist.js +27 -0
  376. package/dist/internal/IpAllowlist.js.map +1 -0
  377. package/dist/internal/Log.d.ts +64 -9
  378. package/dist/internal/Log.d.ts.map +1 -1
  379. package/dist/internal/Log.js +29 -7
  380. package/dist/internal/Log.js.map +1 -1
  381. package/dist/internal/MetricSink.d.ts.map +1 -1
  382. package/dist/internal/MetricSink.js +47 -1
  383. package/dist/internal/MetricSink.js.map +1 -1
  384. package/dist/internal/OpenApi.d.ts +2 -2
  385. package/dist/internal/OpenApi.d.ts.map +1 -1
  386. package/dist/internal/OpenApi.js +4 -1
  387. package/dist/internal/OpenApi.js.map +1 -1
  388. package/dist/internal/Provider.d.ts +14 -0
  389. package/dist/internal/Provider.d.ts.map +1 -0
  390. package/dist/internal/Provider.js +5 -0
  391. package/dist/internal/Provider.js.map +1 -0
  392. package/dist/internal/Schema.d.ts +6 -1
  393. package/dist/internal/Schema.d.ts.map +1 -1
  394. package/dist/internal/Schema.js +20 -11
  395. package/dist/internal/Schema.js.map +1 -1
  396. package/dist/internal/Store.d.ts +6 -0
  397. package/dist/internal/Store.d.ts.map +1 -1
  398. package/dist/internal/Store.js +35 -0
  399. package/dist/internal/Store.js.map +1 -1
  400. package/dist/internal/Tidx.d.ts +49 -24
  401. package/dist/internal/Tidx.d.ts.map +1 -1
  402. package/dist/internal/Tidx.js +90 -12
  403. package/dist/internal/Tidx.js.map +1 -1
  404. package/dist/internal/Viem.d.ts +90 -65
  405. package/dist/internal/Viem.d.ts.map +1 -1
  406. package/dist/internal/Viem.js +90 -12
  407. package/dist/internal/Viem.js.map +1 -1
  408. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  409. package/dist/internal/WebhookDestination.js +34 -28
  410. package/dist/internal/WebhookDestination.js.map +1 -1
  411. package/dist/internal/Webhooks.d.ts +9 -0
  412. package/dist/internal/Webhooks.d.ts.map +1 -1
  413. package/dist/internal/Webhooks.js +15 -10
  414. package/dist/internal/Webhooks.js.map +1 -1
  415. package/dist/internal/exchange/Provider.d.ts +219 -0
  416. package/dist/internal/exchange/Provider.d.ts.map +1 -0
  417. package/dist/internal/exchange/Provider.js +117 -0
  418. package/dist/internal/exchange/Provider.js.map +1 -0
  419. package/dist/internal/exchange/index.d.ts +2 -0
  420. package/dist/internal/exchange/index.d.ts.map +1 -0
  421. package/dist/internal/exchange/index.js +2 -0
  422. package/dist/internal/exchange/index.js.map +1 -0
  423. package/dist/internal/exchange/providers/uniswap.d.ts +57 -0
  424. package/dist/internal/exchange/providers/uniswap.d.ts.map +1 -0
  425. package/dist/internal/exchange/providers/uniswap.js +553 -0
  426. package/dist/internal/exchange/providers/uniswap.js.map +1 -0
  427. package/dist/internal/funding/Action.d.ts +55 -0
  428. package/dist/internal/funding/Action.d.ts.map +1 -0
  429. package/dist/internal/funding/Action.js +74 -0
  430. package/dist/internal/funding/Action.js.map +1 -0
  431. package/dist/internal/funding/Catalog.d.ts +107 -0
  432. package/dist/internal/funding/Catalog.d.ts.map +1 -0
  433. package/dist/internal/funding/Catalog.js +270 -0
  434. package/dist/internal/funding/Catalog.js.map +1 -0
  435. package/dist/internal/funding/Chain.d.ts +76 -0
  436. package/dist/internal/funding/Chain.d.ts.map +1 -0
  437. package/dist/internal/funding/Chain.js +38 -0
  438. package/dist/internal/funding/Chain.js.map +1 -0
  439. package/dist/internal/funding/Deposit.d.ts +408 -0
  440. package/dist/internal/funding/Deposit.d.ts.map +1 -0
  441. package/dist/internal/funding/Deposit.js +318 -0
  442. package/dist/internal/funding/Deposit.js.map +1 -0
  443. package/dist/internal/funding/DepositAddress.d.ts +345 -0
  444. package/dist/internal/funding/DepositAddress.d.ts.map +1 -0
  445. package/dist/internal/funding/DepositAddress.js +189 -0
  446. package/dist/internal/funding/DepositAddress.js.map +1 -0
  447. package/dist/internal/funding/Evidence.d.ts +48 -0
  448. package/dist/internal/funding/Evidence.d.ts.map +1 -0
  449. package/dist/internal/funding/Evidence.js +315 -0
  450. package/dist/internal/funding/Evidence.js.map +1 -0
  451. package/dist/internal/funding/Provider.d.ts +1288 -0
  452. package/dist/internal/funding/Provider.d.ts.map +1 -0
  453. package/dist/internal/funding/Provider.js +1086 -0
  454. package/dist/internal/funding/Provider.js.map +1 -0
  455. package/dist/internal/funding/Reconciliation.d.ts +127 -0
  456. package/dist/internal/funding/Reconciliation.d.ts.map +1 -0
  457. package/dist/internal/funding/Reconciliation.js +885 -0
  458. package/dist/internal/funding/Reconciliation.js.map +1 -0
  459. package/dist/internal/funding/Route.d.ts +21 -0
  460. package/dist/internal/funding/Route.d.ts.map +1 -0
  461. package/dist/internal/funding/Route.js +5 -0
  462. package/dist/internal/funding/Route.js.map +1 -0
  463. package/dist/internal/funding/SourceObservation.d.ts +108 -0
  464. package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
  465. package/dist/internal/funding/SourceObservation.js +143 -0
  466. package/dist/internal/funding/SourceObservation.js.map +1 -0
  467. package/dist/internal/funding/Subsidy.d.ts +113 -0
  468. package/dist/internal/funding/Subsidy.d.ts.map +1 -0
  469. package/dist/internal/funding/Subsidy.js +138 -0
  470. package/dist/internal/funding/Subsidy.js.map +1 -0
  471. package/dist/internal/funding/Token.d.ts +41 -0
  472. package/dist/internal/funding/Token.d.ts.map +1 -0
  473. package/dist/internal/funding/Token.js +22 -0
  474. package/dist/internal/funding/Token.js.map +1 -0
  475. package/dist/internal/funding/Transfer.d.ts +643 -0
  476. package/dist/internal/funding/Transfer.d.ts.map +1 -0
  477. package/dist/internal/funding/Transfer.js +332 -0
  478. package/dist/internal/funding/Transfer.js.map +1 -0
  479. package/dist/internal/funding/index.d.ts +14 -0
  480. package/dist/internal/funding/index.d.ts.map +1 -0
  481. package/dist/internal/funding/index.js +14 -0
  482. package/dist/internal/funding/index.js.map +1 -0
  483. package/dist/internal/funding/providers/across.d.ts +21 -0
  484. package/dist/internal/funding/providers/across.d.ts.map +1 -0
  485. package/dist/internal/funding/providers/across.js +117 -0
  486. package/dist/internal/funding/providers/across.js.map +1 -0
  487. package/dist/internal/funding/providers/relay.d.ts +88 -0
  488. package/dist/internal/funding/providers/relay.d.ts.map +1 -0
  489. package/dist/internal/funding/providers/relay.js +871 -0
  490. package/dist/internal/funding/providers/relay.js.map +1 -0
  491. package/dist/internal/funding/providers/rhino.d.ts +15 -0
  492. package/dist/internal/funding/providers/rhino.d.ts.map +1 -0
  493. package/dist/internal/funding/providers/rhino.js +116 -0
  494. package/dist/internal/funding/providers/rhino.js.map +1 -0
  495. package/dist/internal/funding/providers/squid.d.ts +19 -0
  496. package/dist/internal/funding/providers/squid.d.ts.map +1 -0
  497. package/dist/internal/funding/providers/squid.js +120 -0
  498. package/dist/internal/funding/providers/squid.js.map +1 -0
  499. package/dist/internal/funding/providers/symbiosis.d.ts +20 -0
  500. package/dist/internal/funding/providers/symbiosis.d.ts.map +1 -0
  501. package/dist/internal/funding/providers/symbiosis.js +176 -0
  502. package/dist/internal/funding/providers/symbiosis.js.map +1 -0
  503. package/dist/internal/index.d.ts +5 -1
  504. package/dist/internal/index.d.ts.map +1 -1
  505. package/dist/internal/index.js +5 -1
  506. package/dist/internal/index.js.map +1 -1
  507. package/dist/internal/webhookDestinations/url.js +61 -57
  508. package/dist/internal/webhookDestinations/url.js.map +1 -1
  509. package/dist/providers/index.d.ts +43 -0
  510. package/dist/providers/index.d.ts.map +1 -0
  511. package/dist/providers/index.js +19 -0
  512. package/dist/providers/index.js.map +1 -0
  513. package/package.json +10 -5
  514. package/src/ApiKey.test.ts +35 -0
  515. package/src/ApiKey.ts +27 -0
  516. package/src/ApiKeys.test.ts +40 -7
  517. package/src/ApiKeys.ts +46 -10
  518. package/src/App.test.ts +108 -15
  519. package/src/App.ts +296 -138
  520. package/src/Assets.ts +29 -4
  521. package/src/Client.test-d.ts +809 -37
  522. package/src/Scope.test.ts +19 -0
  523. package/src/Scope.ts +28 -11
  524. package/src/Webhooks.test.ts +25 -0
  525. package/src/Webhooks.ts +1 -1
  526. package/src/admin/App.test.ts +9 -2
  527. package/src/admin/App.ts +32 -9
  528. package/src/admin/apps/api-keys.test.ts +190 -22
  529. package/src/admin/apps/api-keys.ts +170 -28
  530. package/src/admin/apps/earn-vaults.test.ts +338 -0
  531. package/src/admin/apps/earn-vaults.ts +572 -0
  532. package/src/admin/apps/scopes.test.ts +16 -1
  533. package/src/admin/ui/plugin.ts +2 -2
  534. package/src/admin/ui/src/components/EarnVaultForm.tsx +137 -0
  535. package/src/admin/ui/src/components/MobileProfile.tsx +2 -2
  536. package/src/admin/ui/src/components/Page.tsx +1 -1
  537. package/src/admin/ui/src/components/ThemeToggle.tsx +1 -1
  538. package/src/admin/ui/src/lib/api-keys.ts +37 -7
  539. package/src/admin/ui/src/lib/client.ts +1 -1
  540. package/src/admin/ui/src/lib/config.ts +1 -1
  541. package/src/admin/ui/src/lib/early-access.ts +8 -9
  542. package/src/admin/ui/src/lib/earn-vaults.ts +207 -0
  543. package/src/admin/ui/src/lib/format.ts +13 -0
  544. package/src/admin/ui/src/lib/organizations.test.ts +14 -4
  545. package/src/admin/ui/src/lib/organizations.ts +7 -1
  546. package/src/admin/ui/src/lib/response.ts +22 -0
  547. package/src/admin/ui/src/lib/scopes.test.ts +19 -11
  548. package/src/admin/ui/src/lib/scopes.ts +28 -7
  549. package/src/admin/ui/src/lib/verified-tokens.ts +10 -9
  550. package/src/admin/ui/src/routeTree.gen.ts +70 -7
  551. package/src/admin/ui/src/router.tsx +2 -2
  552. package/src/admin/ui/src/routes/__root.tsx +9 -8
  553. package/src/admin/ui/src/routes/api-keys.tsx +475 -64
  554. package/src/admin/ui/src/routes/early-access.tsx +5 -12
  555. package/src/admin/ui/src/routes/earn.vaults.tsx +348 -0
  556. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +154 -0
  557. package/src/admin/ui/src/routes/earn.vaults_.new.tsx +190 -0
  558. package/src/admin/ui/src/routes/organizations.tsx +4 -7
  559. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +56 -54
  560. package/src/admin/ui/src/routes/verified-tokens.tsx +4 -4
  561. package/src/admin/ui/vite.config.ts +4 -1
  562. package/src/admin/ui.generated.ts +113 -43
  563. package/src/analytics/Analytics.test.ts +69 -10
  564. package/src/analytics/Analytics.ts +24 -1
  565. package/src/analytics/tables/requestEvents.ts +79 -37
  566. package/src/apps/Zones.test.ts +33 -0
  567. package/src/apps/Zones.ts +40 -0
  568. package/src/apps/composition.test.ts +184 -3
  569. package/src/apps/data/App.test.ts +28 -23
  570. package/src/apps/data/App.ts +95 -36
  571. package/src/apps/data/FxOracle.ts +1 -1
  572. package/src/apps/data/index.ts +0 -1
  573. package/src/apps/data/routes/activities.test.ts +82 -6
  574. package/src/apps/data/routes/activities.ts +131 -71
  575. package/src/apps/data/routes/balances.test.ts +50 -37
  576. package/src/apps/data/routes/balances.ts +5 -5
  577. package/src/apps/data/routes/blocks.test.ts +2 -0
  578. package/src/apps/data/routes/coingecko.test.ts +22 -0
  579. package/src/apps/data/routes/coingecko.ts +84 -48
  580. package/src/apps/data/routes/earn.test.ts +1391 -0
  581. package/src/apps/data/routes/earn.ts +2188 -0
  582. package/src/apps/data/routes/exchanges.test.ts +82 -33
  583. package/src/apps/data/routes/exchanges.ts +842 -227
  584. package/src/apps/data/routes/fee-amm.test.ts +4 -6
  585. package/src/apps/data/routes/fee-amm.ts +69 -12
  586. package/src/apps/data/routes/indexer.test.ts +43 -34
  587. package/src/apps/data/routes/indexer.ts +21 -3
  588. package/src/apps/data/routes/receipts.test.ts +5 -3
  589. package/src/apps/data/routes/receipts.ts +13 -11
  590. package/src/apps/data/routes/rpc.test.ts +446 -28
  591. package/src/apps/data/routes/rpc.ts +245 -25
  592. package/src/apps/data/routes/tokenlist.ts +72 -19
  593. package/src/apps/data/routes/tokens.test.ts +6 -8
  594. package/src/apps/data/routes/tokens.ts +258 -86
  595. package/src/apps/data/routes/transactions.test.ts +2 -0
  596. package/src/apps/data/routes/transactions.ts +77 -42
  597. package/src/apps/data/routes/transfers.test.ts +166 -4
  598. package/src/apps/data/routes/transfers.ts +216 -78
  599. package/src/apps/data/routes/valuation.test.ts +62 -9
  600. package/src/apps/data/routes/valuation.ts +2 -2
  601. package/src/apps/data/routes/webhooks.test.ts +386 -43
  602. package/src/apps/data/routes/webhooks.ts +172 -71
  603. package/src/apps/data/routes/zones.test.ts +473 -0
  604. package/src/apps/data/routes/zones.ts +338 -0
  605. package/src/apps/docs/reference.ts +8 -6
  606. package/src/apps/funding/Access.ts +26 -0
  607. package/src/apps/funding/App.test.ts +78 -0
  608. package/src/apps/funding/App.ts +63 -0
  609. package/src/apps/funding/Inventory.ts +30 -0
  610. package/src/apps/funding/ProviderAttempts.ts +26 -0
  611. package/src/apps/funding/index.ts +1 -0
  612. package/src/apps/funding/routes/chains.test.ts +78 -0
  613. package/src/apps/funding/routes/chains.ts +101 -0
  614. package/src/apps/funding/routes/deposit-addresses.test.ts +1628 -0
  615. package/src/apps/funding/routes/deposit-addresses.ts +1015 -0
  616. package/src/apps/funding/routes/providers.test.ts +165 -0
  617. package/src/apps/funding/routes/providers.ts +73 -0
  618. package/src/apps/funding/routes/quotes.test.ts +511 -0
  619. package/src/apps/funding/routes/quotes.ts +205 -0
  620. package/src/apps/funding/routes/transfers.test.ts +1255 -0
  621. package/src/apps/funding/routes/transfers.ts +704 -0
  622. package/src/apps/index.ts +3 -1
  623. package/src/apps/management/Billing.test.ts +29 -0
  624. package/src/apps/management/Billing.ts +23 -0
  625. package/src/apps/management/Error.ts +12 -0
  626. package/src/apps/management/routes/api-keys.test.ts +103 -3
  627. package/src/apps/management/routes/api-keys.ts +98 -10
  628. package/src/apps/management/routes/invitations.ts +4 -11
  629. package/src/apps/management/routes/orgs.ts +2 -9
  630. package/src/apps/management/routes/scopes.test.ts +7 -2
  631. package/src/apps/management/routes/scopes.ts +2 -2
  632. package/src/apps/management/routes/webhooks.test.ts +6 -1
  633. package/src/apps/management/routes/webhooks.ts +15 -3
  634. package/src/apps/mcp.test.ts +17 -2
  635. package/src/apps/mcp.ts +18 -13
  636. package/src/apps/metadata.ts +12 -0
  637. package/src/apps/mpp/App.ts +276 -29
  638. package/src/apps/mpp/Mpp.test.ts +631 -26
  639. package/src/apps/mpp/Mpp.ts +0 -42
  640. package/src/apps/mpp/internal/Adapter.ts +108 -3
  641. package/src/apps/mpp/internal/Error.test.ts +33 -0
  642. package/src/apps/mpp/internal/Error.ts +44 -6
  643. package/src/apps/mpp/internal/Idempotency.test.ts +213 -0
  644. package/src/apps/mpp/internal/Idempotency.ts +59 -5
  645. package/src/apps/mpp/internal/Policy.test.ts +128 -0
  646. package/src/apps/mpp/internal/Policy.ts +98 -0
  647. package/src/apps/mpp/internal/ScreenManager.test.ts +68 -0
  648. package/src/apps/mpp/internal/ScreenManager.ts +41 -0
  649. package/src/apps/mpp/internal/Screening.ts +40 -3
  650. package/src/apps/mpp/internal/Sponsorship.ts +144 -0
  651. package/src/apps/mpp/internal/Utils.ts +30 -0
  652. package/src/apps/relay/App.test.ts +493 -62
  653. package/src/apps/relay/App.ts +233 -23
  654. package/src/apps/relay/Sponsorships.test.ts +49 -0
  655. package/src/apps/relay/Sponsorships.ts +18 -6
  656. package/src/cloudflare.ts +279 -0
  657. package/src/db/Db.ts +501 -0
  658. package/src/db/Schema.ts +349 -2
  659. package/src/db/tables/earnVaults.test.ts +185 -0
  660. package/src/db/tables/earnVaults.ts +151 -0
  661. package/src/db/tables/fundingCatalog.test.ts +108 -0
  662. package/src/db/tables/fundingCatalog.ts +157 -0
  663. package/src/db/tables/fundingDepositAddresses.test.ts +580 -0
  664. package/src/db/tables/fundingDepositAddresses.ts +404 -0
  665. package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
  666. package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
  667. package/src/db/tables/fundingDeposits.test.ts +399 -0
  668. package/src/db/tables/fundingDeposits.ts +373 -0
  669. package/src/db/tables/fundingIdempotency.test.ts +182 -0
  670. package/src/db/tables/fundingIdempotency.ts +180 -0
  671. package/src/db/tables/fundingTransferEvents.ts +28 -0
  672. package/src/db/tables/fundingTransferTransactions.test.ts +69 -0
  673. package/src/db/tables/fundingTransferTransactions.ts +37 -0
  674. package/src/db/tables/fundingTransfers.test.ts +89 -0
  675. package/src/db/tables/fundingTransfers.ts +153 -0
  676. package/src/db/tables/organizations.ts +16 -0
  677. package/src/db/tables/projects.ts +16 -0
  678. package/src/db/tables/requestUsage.ts +23 -0
  679. package/src/db/tables/sponsoredTransactions.test.ts +11 -0
  680. package/src/db/tables/sponsoredTransactions.ts +3 -3
  681. package/src/db/tables/users.ts +16 -0
  682. package/src/db/tables/verifiedTokens.ts +19 -14
  683. package/src/db/tables/webhookSubscriptions.ts +58 -3
  684. package/src/handlers/relay.test.ts +2 -0
  685. package/src/handlers/relay.ts +95 -99
  686. package/src/internal/Auth.test.ts +225 -5
  687. package/src/internal/Auth.ts +109 -20
  688. package/src/internal/Cache.test.ts +37 -3
  689. package/src/internal/Cache.ts +14 -10
  690. package/src/internal/Cursor.test.ts +2 -2
  691. package/src/internal/Cursor.ts +1 -1
  692. package/src/internal/EarnRates.test.ts +125 -0
  693. package/src/internal/EarnRates.ts +409 -0
  694. package/src/internal/EarnVaults.test.ts +195 -0
  695. package/src/internal/EarnVaults.ts +876 -0
  696. package/src/internal/EdgeCache.test.ts +26 -7
  697. package/src/internal/EdgeCache.ts +48 -9
  698. package/src/internal/Id.ts +14 -0
  699. package/src/internal/Idempotency.test.ts +109 -0
  700. package/src/internal/Idempotency.ts +237 -0
  701. package/src/internal/IpAllowlist.test.ts +52 -0
  702. package/src/internal/IpAllowlist.ts +39 -0
  703. package/src/internal/Log.test-d.ts +41 -8
  704. package/src/internal/Log.test.ts +196 -9
  705. package/src/internal/Log.ts +124 -17
  706. package/src/internal/MetricSink.test.ts +244 -4
  707. package/src/internal/MetricSink.ts +51 -1
  708. package/src/internal/OpenApi.ts +4 -1
  709. package/src/internal/Provider.test.ts +17 -0
  710. package/src/internal/Provider.ts +17 -0
  711. package/src/internal/Schema.ts +27 -17
  712. package/src/internal/Store.test.ts +25 -0
  713. package/src/internal/Store.ts +46 -0
  714. package/src/internal/Tidx.local.test.ts +78 -3
  715. package/src/internal/Tidx.test.ts +240 -18
  716. package/src/internal/Tidx.ts +129 -33
  717. package/src/internal/VerifiedTokens.test.ts +15 -0
  718. package/src/internal/Viem.test.ts +198 -17
  719. package/src/internal/Viem.ts +127 -28
  720. package/src/internal/WebhookDestination.ts +25 -23
  721. package/src/internal/Webhooks.test.ts +59 -0
  722. package/src/internal/Webhooks.ts +34 -10
  723. package/src/internal/exchange/Provider.test.ts +216 -0
  724. package/src/internal/exchange/Provider.ts +326 -0
  725. package/src/internal/exchange/index.test.ts +6 -0
  726. package/src/internal/exchange/index.ts +1 -0
  727. package/src/internal/exchange/providers/uniswap.test.ts +122 -0
  728. package/src/internal/exchange/providers/uniswap.ts +682 -0
  729. package/src/internal/funding/Action.test.ts +138 -0
  730. package/src/internal/funding/Action.ts +117 -0
  731. package/src/internal/funding/Catalog.test.ts +311 -0
  732. package/src/internal/funding/Catalog.ts +347 -0
  733. package/src/internal/funding/Chain.test.ts +79 -0
  734. package/src/internal/funding/Chain.ts +120 -0
  735. package/src/internal/funding/Deposit.test.ts +26 -0
  736. package/src/internal/funding/Deposit.ts +522 -0
  737. package/src/internal/funding/DepositAddress.test.ts +18 -0
  738. package/src/internal/funding/DepositAddress.ts +312 -0
  739. package/src/internal/funding/Evidence.test.ts +297 -0
  740. package/src/internal/funding/Evidence.ts +425 -0
  741. package/src/internal/funding/Provider.test.ts +1328 -0
  742. package/src/internal/funding/Provider.ts +1911 -0
  743. package/src/internal/funding/Reconciliation.local.test.ts +511 -0
  744. package/src/internal/funding/Reconciliation.test.ts +3037 -0
  745. package/src/internal/funding/Reconciliation.ts +1202 -0
  746. package/src/internal/funding/Route.test.ts +66 -0
  747. package/src/internal/funding/Route.ts +29 -0
  748. package/src/internal/funding/SourceObservation.test.ts +301 -0
  749. package/src/internal/funding/SourceObservation.ts +267 -0
  750. package/src/internal/funding/Subsidy.local.test.ts +152 -0
  751. package/src/internal/funding/Subsidy.test.ts +75 -0
  752. package/src/internal/funding/Subsidy.ts +260 -0
  753. package/src/internal/funding/Token.test.ts +47 -0
  754. package/src/internal/funding/Token.ts +59 -0
  755. package/src/internal/funding/Transfer.test.ts +369 -0
  756. package/src/internal/funding/Transfer.ts +522 -0
  757. package/src/internal/funding/index.test.ts +21 -0
  758. package/src/internal/funding/index.ts +13 -0
  759. package/src/internal/funding/providers/across.ts +149 -0
  760. package/src/{apps/data/FundingQuoteProviders.test.ts → internal/funding/providers/index.test.ts} +178 -157
  761. package/src/internal/funding/providers/relay.test.ts +564 -0
  762. package/src/internal/funding/providers/relay.ts +1060 -0
  763. package/src/internal/funding/providers/rhino.ts +139 -0
  764. package/src/internal/funding/providers/squid.ts +151 -0
  765. package/src/internal/funding/providers/symbiosis.ts +221 -0
  766. package/src/internal/index.test.ts +5 -1
  767. package/src/internal/index.ts +5 -1
  768. package/src/internal/webhookDestinations/url.ts +49 -45
  769. package/src/providers/index.test.ts +14 -0
  770. package/src/providers/index.ts +54 -0
  771. package/dist/apps/data/FundingQuoteProviders.d.ts +0 -174
  772. package/dist/apps/data/FundingQuoteProviders.d.ts.map +0 -1
  773. package/dist/apps/data/FundingQuoteProviders.js +0 -775
  774. package/dist/apps/data/FundingQuoteProviders.js.map +0 -1
  775. package/dist/apps/data/routes/funding.d.ts.map +0 -1
  776. package/dist/apps/data/routes/funding.js +0 -410
  777. package/dist/apps/data/routes/funding.js.map +0 -1
  778. package/dist/internal/FundingQuotes.d.ts +0 -864
  779. package/dist/internal/FundingQuotes.d.ts.map +0 -1
  780. package/dist/internal/FundingQuotes.js +0 -672
  781. package/dist/internal/FundingQuotes.js.map +0 -1
  782. package/src/apps/data/FundingQuoteProviders.ts +0 -1043
  783. package/src/apps/data/routes/funding.test.ts +0 -503
  784. package/src/apps/data/routes/funding.ts +0 -604
  785. package/src/internal/FundingQuotes.test.ts +0 -456
  786. package/src/internal/FundingQuotes.ts +0 -944
@@ -0,0 +1,1628 @@
1
+ import { Hash, Hex } from 'ox'
2
+
3
+ import { tempoMainnet } from 'viem/tempo/chains'
4
+
5
+ import * as FundingDepositAddresses from '../../../db/tables/fundingDepositAddresses.js'
6
+
7
+ import * as FundingIdempotencyTable from '../../../db/tables/fundingIdempotency.js'
8
+
9
+ import * as Cache from '../../../internal/Cache.js'
10
+
11
+ import * as Cursor from '../../../internal/Cursor.js'
12
+
13
+ import * as FundingDeposit from '../../../internal/funding/Deposit.js'
14
+
15
+ import * as FundingDepositAddress from '../../../internal/funding/DepositAddress.js'
16
+
17
+ import * as FundingProvider from '../../../internal/funding/Provider.js'
18
+
19
+ import * as Idempotency from '../../../internal/Idempotency.js'
20
+
21
+ import * as Across from '../../../internal/funding/providers/across.js'
22
+
23
+ import * as Relay from '../../../internal/funding/providers/relay.js'
24
+
25
+ import * as Rhino from '../../../internal/funding/providers/rhino.js'
26
+
27
+ import * as Squid from '../../../internal/funding/providers/squid.js'
28
+
29
+ import * as Symbiosis from '../../../internal/funding/providers/symbiosis.js'
30
+
31
+ import type * as Log from '../../../internal/Log.js'
32
+
33
+ import * as TestApp from '../../../../test/App.js'
34
+
35
+ import * as TestFunding from '../../../../test/Funding.js'
36
+
37
+ import * as Funding from './deposit-addresses.js'
38
+
39
+ const db = TestApp.database()
40
+ const relayApiKey = process.env.RELAY_API_KEY
41
+
42
+ const providers = [
43
+ Across.across({
44
+ apiKey: 'across-api-key',
45
+ integratorId: '0xdead',
46
+ userAddress: '0x1111111111111111111111111111111111111111',
47
+ }),
48
+ Relay.relay({ userAddress: '0x1111111111111111111111111111111111111111' }),
49
+ Rhino.rhino(),
50
+ Squid.squid({
51
+ integratorId: 'squid-integrator-id',
52
+ userAddress: '0x1111111111111111111111111111111111111111',
53
+ }),
54
+ Symbiosis.symbiosis({ userAddress: '0x1111111111111111111111111111111111111111' }),
55
+ ] as const
56
+
57
+ beforeAll(() => TestFunding.publish(db))
58
+
59
+ function create(options: TestApp.create.Options = {}) {
60
+ return TestApp.create({ ...options, db })
61
+ }
62
+
63
+ describe('POST /funding/deposit-addresses', () => {
64
+ test('requires funding:write and an idempotency key', async () => {
65
+ const app = transfersApp({ providers: [depositAddressProvider()] })
66
+ expect((await postDepositAddress(app, { headers: {} })).status).toBe(401)
67
+ expect((await postDepositAddress(app, { key: fundingReader })).status).toBe(403)
68
+ const response = await postDepositAddress(app, {
69
+ headers: { authorization: `Bearer ${fundingWriter.token}` },
70
+ })
71
+ expect(response.status).toBe(400)
72
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
73
+ 'idempotency_key_required',
74
+ )
75
+ })
76
+
77
+ test('requires a source amount', async () => {
78
+ const { amount: _amount, ...body } = depositAddressBody
79
+ const response = await postDepositAddress(
80
+ transfersApp({ providers: [depositAddressProvider()] }),
81
+ { body },
82
+ )
83
+
84
+ expect({ body: await response.json(), status: response.status }).toMatchInlineSnapshot(
85
+ {
86
+ body: { requestId: expect.any(String) },
87
+ },
88
+ `
89
+ {
90
+ "body": {
91
+ "error": {
92
+ "code": "body_invalid",
93
+ "details": [
94
+ {
95
+ "message": "Invalid input: expected string, received undefined",
96
+ "path": [
97
+ "amount",
98
+ ],
99
+ },
100
+ ],
101
+ "message": "Check the request body and try again.",
102
+ },
103
+ "requestId": Any<String>,
104
+ },
105
+ "status": 400,
106
+ }
107
+ `,
108
+ )
109
+ })
110
+
111
+ test('creates a reusable address and persists private provider correlation', async () => {
112
+ let seen: FundingProvider.createDepositAddress.Parameters | undefined
113
+ const registered: FundingDepositAddress.Public[] = []
114
+ const app = transfersApp({
115
+ funding: {
116
+ registerDepositAddress: async (address) => {
117
+ registered.push(address)
118
+ },
119
+ },
120
+ providers: [
121
+ depositAddressProvider('TBXSw8fM4jpQkGc6zZjsVABFpVN7UvXPdV', (input) => {
122
+ seen = input
123
+ }),
124
+ ],
125
+ })
126
+ const response = await postDepositAddress(app, {
127
+ body: { ...depositAddressBody, destinationToken: 'usdt0', sourceToken: 'usdt' },
128
+ })
129
+ expect(response.status).toBe(200)
130
+ expect(response.headers.get('cache-control')).toBe('no-store')
131
+ const body = await TestApp.json(response, Funding.schema.createFundingDepositAddress.Response)
132
+ expect(body).toMatchObject({
133
+ address: 'TBXSw8fM4jpQkGc6zZjsVABFpVN7UvXPdV',
134
+ destinationToken: { symbol: 'USDT0' },
135
+ provider: { id: 'relay', name: 'Relay' },
136
+ recipient: depositAddressBody.recipient,
137
+ refundAddress: depositAddressBody.refundAddress,
138
+ sourceChain: { id: 'tron:0x2b6653dc' },
139
+ sourceToken: { symbol: 'USDT' },
140
+ status: 'active',
141
+ })
142
+ expect(seen).toMatchObject({
143
+ recipient: depositAddressBody.recipient,
144
+ refundAddress: depositAddressBody.refundAddress,
145
+ })
146
+ const record = await FundingDepositAddresses.get(db, body.id)
147
+ expect(record).toMatchObject({
148
+ deliveryStrategy: 'provider',
149
+ providerOutputToken: body.destinationToken,
150
+ providerRequestIds: ['relay_request_1'],
151
+ providerState: { checkEndpoint: '/intents/status/v3?requestId=relay_request_1' },
152
+ })
153
+ expect(registered).toMatchInlineSnapshot(
154
+ [
155
+ {
156
+ createdAt: expect.any(String),
157
+ id: expect.any(String),
158
+ updatedAt: expect.any(String),
159
+ },
160
+ ],
161
+ `
162
+ [
163
+ {
164
+ "address": "TBXSw8fM4jpQkGc6zZjsVABFpVN7UvXPdV",
165
+ "createdAt": Any<String>,
166
+ "destinationChain": {
167
+ "addressFormat": "hex",
168
+ "id": "eip155:4217",
169
+ "kind": "evm",
170
+ "name": "Tempo",
171
+ },
172
+ "destinationToken": {
173
+ "address": "0x20c00000000000000000000014f22ca97301eb73",
174
+ "currency": "USD",
175
+ "decimals": 6,
176
+ "name": "USDT0",
177
+ "standard": "TIP-20",
178
+ "symbol": "USDT0",
179
+ "tokenKey": "eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73",
180
+ "verified": true,
181
+ },
182
+ "id": Any<String>,
183
+ "provider": {
184
+ "id": "relay",
185
+ "name": "Relay",
186
+ },
187
+ "recipient": "0x1111111111111111111111111111111111111111",
188
+ "refundAddress": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
189
+ "sourceChain": {
190
+ "addressFormat": "base58check",
191
+ "id": "tron:0x2b6653dc",
192
+ "kind": "tron",
193
+ "name": "Tron",
194
+ },
195
+ "sourceToken": {
196
+ "address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
197
+ "currency": "USD",
198
+ "decimals": 6,
199
+ "name": "Tether USD",
200
+ "standard": "TRC-20",
201
+ "symbol": "USDT",
202
+ "tokenKey": "tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
203
+ "verified": true,
204
+ },
205
+ "status": "active",
206
+ "subsidize": false,
207
+ "updatedAt": Any<String>,
208
+ },
209
+ ]
210
+ `,
211
+ )
212
+ })
213
+
214
+ test('resolves token identifiers through the composed route', async () => {
215
+ const app = transfersApp({
216
+ providers: [depositAddressProvider('T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb')],
217
+ })
218
+ const identifiers = [
219
+ { destinationToken: 'usdt0', name: 'alias', sourceToken: 'usdt' },
220
+ {
221
+ destinationToken: depositAddressBody.destinationToken,
222
+ name: 'address',
223
+ sourceToken: depositAddressBody.sourceToken,
224
+ },
225
+ {
226
+ destinationToken: 'eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73',
227
+ name: 'token key',
228
+ sourceToken: 'tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
229
+ },
230
+ ] as const
231
+ const results = []
232
+ for (const identifier of identifiers) {
233
+ const response = await postDepositAddress(app, {
234
+ body: {
235
+ ...depositAddressBody,
236
+ destinationToken: identifier.destinationToken,
237
+ recipient: `0x${'55'.repeat(20)}`,
238
+ refundAddress: 'TD5gsCwxykWsLN9aPrq2TAfNjByuZKYp4E',
239
+ sourceToken: identifier.sourceToken,
240
+ },
241
+ })
242
+ const body = await TestApp.json(response, Funding.schema.createFundingDepositAddress.Response)
243
+ results.push({
244
+ destinationToken: body.destinationToken.symbol,
245
+ name: identifier.name,
246
+ sourceToken: body.sourceToken.symbol,
247
+ status: response.status,
248
+ })
249
+ }
250
+
251
+ expect(results).toMatchInlineSnapshot(`
252
+ [
253
+ {
254
+ "destinationToken": "USDT0",
255
+ "name": "alias",
256
+ "sourceToken": "USDT",
257
+ "status": 200,
258
+ },
259
+ {
260
+ "destinationToken": "USDT0",
261
+ "name": "address",
262
+ "sourceToken": "USDT",
263
+ "status": 200,
264
+ },
265
+ {
266
+ "destinationToken": "USDT0",
267
+ "name": "token key",
268
+ "sourceToken": "USDT",
269
+ "status": 200,
270
+ },
271
+ ]
272
+ `)
273
+ })
274
+
275
+ test('omits newly quoted terms from a matching reusable address', async () => {
276
+ let calls = 0
277
+ const body = { ...depositAddressBody, refundAddress: 'TD5gsCwxykWsLN9aPrq2TAfNjByuZKYp4E' }
278
+ const app = transfersApp({
279
+ providers: [
280
+ depositAddressProvider('TEdvoHEatmDKvTh3o9vBRB9Vdtbhn4QFhy', () => {
281
+ calls += 1
282
+ }),
283
+ ],
284
+ })
285
+ const first = await TestApp.json(
286
+ await postDepositAddress(app, { body, idempotencyKey: nextDepositAddressKey() }),
287
+ Funding.schema.createFundingDepositAddress.Response,
288
+ )
289
+ const second = await TestApp.json(
290
+ await postDepositAddress(app, {
291
+ body: { ...body, amount: '200000000' },
292
+ idempotencyKey: nextDepositAddressKey(),
293
+ }),
294
+ Funding.schema.createFundingDepositAddress.Response,
295
+ )
296
+ if (!('sourceAmount' in first)) throw new Error('Expected newly provisioned terms.')
297
+
298
+ expect({
299
+ calls,
300
+ firstHasQuote: 'quote' in first,
301
+ firstId: first.id,
302
+ firstSourceAmount: first.sourceAmount.baseUnits,
303
+ secondHasQuote: 'quote' in second,
304
+ secondId: second.id,
305
+ }).toMatchInlineSnapshot(
306
+ {
307
+ firstId: expect.any(String),
308
+ secondId: expect.any(String),
309
+ },
310
+ `
311
+ {
312
+ "calls": 2,
313
+ "firstHasQuote": true,
314
+ "firstId": Any<String>,
315
+ "firstSourceAmount": "100000000",
316
+ "secondHasQuote": false,
317
+ "secondId": Any<String>,
318
+ }
319
+ `,
320
+ )
321
+ expect(second.id).toBe(first.id)
322
+ })
323
+
324
+ test('stores subsidized addresses as a separate reusable identity', async () => {
325
+ const inputs: FundingProvider.createDepositAddress.Parameters[] = []
326
+ const app = transfersApp({
327
+ providers: [
328
+ withCreateDepositAddress('relay', async (input) => {
329
+ inputs.push(input)
330
+ return {
331
+ address: inputs.length === 1 ? `0x${'31'.repeat(20)}` : `0x${'32'.repeat(20)}`,
332
+ correlation: { requestId: `relay_request_${inputs.length}` },
333
+ destinationAmount: '1000000',
334
+ destinationAmountMin: '1000000',
335
+ fees: [],
336
+ sampledAt: '2026-08-04T00:00:00.000Z',
337
+ }
338
+ }),
339
+ ],
340
+ })
341
+ const body = {
342
+ ...depositAddressBody,
343
+ refundAddress: `0x${'33'.repeat(20)}`,
344
+ sourceChain: 'base',
345
+ sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
346
+ }
347
+ const standard = await TestApp.json(
348
+ await postDepositAddress(app, { body }),
349
+ Funding.schema.createFundingDepositAddress.Response,
350
+ )
351
+ const subsidized = await TestApp.json(
352
+ await postDepositAddress(app, { body: { ...body, subsidize: true } }),
353
+ Funding.schema.createFundingDepositAddress.Response,
354
+ )
355
+
356
+ expect({
357
+ distinct: standard.id !== subsidized.id,
358
+ inputs: inputs.map(({ subsidize }) => subsidize),
359
+ standard: standard.subsidize,
360
+ subsidized: subsidized.subsidize,
361
+ }).toMatchInlineSnapshot(`
362
+ {
363
+ "distinct": true,
364
+ "inputs": [
365
+ false,
366
+ true,
367
+ ],
368
+ "standard": false,
369
+ "subsidized": true,
370
+ }
371
+ `)
372
+ })
373
+
374
+ test('normalizes EVM accounts before provisioning and matching', async () => {
375
+ const inputs: FundingProvider.createDepositAddress.Parameters[] = []
376
+ const recipient = '0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD'
377
+ const refundAddress = '0x1234567890AbcdEF1234567890aBcdef12345678'
378
+ const body = {
379
+ ...depositAddressBody,
380
+ recipient,
381
+ refundAddress,
382
+ sourceChain: 'base',
383
+ sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
384
+ }
385
+ const app = transfersApp({
386
+ providers: [
387
+ depositAddressProvider(`0x${'99'.repeat(20)}`, (input) => {
388
+ inputs.push(input)
389
+ }),
390
+ ],
391
+ })
392
+ const first = await TestApp.json(
393
+ await postDepositAddress(app, { body, idempotencyKey: nextDepositAddressKey() }),
394
+ Funding.schema.createFundingDepositAddress.Response,
395
+ )
396
+ const second = await TestApp.json(
397
+ await postDepositAddress(app, {
398
+ body: {
399
+ ...body,
400
+ recipient: recipient.toLowerCase(),
401
+ refundAddress: refundAddress.toLowerCase(),
402
+ },
403
+ idempotencyKey: nextDepositAddressKey(),
404
+ }),
405
+ Funding.schema.createFundingDepositAddress.Response,
406
+ )
407
+
408
+ expect({
409
+ first: { recipient: first.recipient, refundAddress: first.refundAddress },
410
+ inputs: inputs.map(({ recipient, refundAddress }) => ({ recipient, refundAddress })),
411
+ reused: first.id === second.id,
412
+ second: { recipient: second.recipient, refundAddress: second.refundAddress },
413
+ }).toMatchInlineSnapshot(`
414
+ {
415
+ "first": {
416
+ "recipient": "0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD",
417
+ "refundAddress": "0x1234567890AbcdEF1234567890aBcdef12345678",
418
+ },
419
+ "inputs": [
420
+ {
421
+ "recipient": "0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD",
422
+ "refundAddress": "0x1234567890AbcdEF1234567890aBcdef12345678",
423
+ },
424
+ {
425
+ "recipient": "0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD",
426
+ "refundAddress": "0x1234567890AbcdEF1234567890aBcdef12345678",
427
+ },
428
+ ],
429
+ "reused": true,
430
+ "second": {
431
+ "recipient": "0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD",
432
+ "refundAddress": "0x1234567890AbcdEF1234567890aBcdef12345678",
433
+ },
434
+ }
435
+ `)
436
+ })
437
+
438
+ test('keeps the stored reusable address when the provider rotates addresses', async () => {
439
+ const body = { ...depositAddressBody, recipient: `0x${'22'.repeat(20)}` }
440
+ const first = await TestApp.json(
441
+ await postDepositAddress(
442
+ transfersApp({
443
+ providers: [depositAddressProvider('TGCAjMXComunWZEXCT1LPBdcYbDVuyexBv')],
444
+ }),
445
+ { body, idempotencyKey: nextDepositAddressKey() },
446
+ ),
447
+ Funding.schema.createFundingDepositAddress.Response,
448
+ )
449
+
450
+ let calls = 0
451
+ const app = transfersApp({
452
+ providers: [
453
+ depositAddressProvider('THkQfRopincF6emzbk6VMC7jTHqJ8MP8g7', () => {
454
+ calls += 1
455
+ }),
456
+ ],
457
+ })
458
+ const idempotencyKey = nextDepositAddressKey()
459
+ const response = await postDepositAddress(app, {
460
+ body: { ...body, amount: '200000000' },
461
+ idempotencyKey,
462
+ })
463
+ const second = await TestApp.json(response, Funding.schema.createFundingDepositAddress.Response)
464
+
465
+ expect({
466
+ address: second.address,
467
+ calls,
468
+ hasQuote: 'quote' in second,
469
+ reused: second.id === first.id,
470
+ status: response.status,
471
+ }).toMatchInlineSnapshot(
472
+ {
473
+ address: expect.any(String),
474
+ },
475
+ `
476
+ {
477
+ "address": Any<String>,
478
+ "calls": 1,
479
+ "hasQuote": false,
480
+ "reused": true,
481
+ "status": 200,
482
+ }
483
+ `,
484
+ )
485
+ expect(second.address).toBe(first.address)
486
+ expect(
487
+ await TestApp.json(
488
+ await postDepositAddress(app, {
489
+ body: { ...body, amount: '200000000' },
490
+ idempotencyKey,
491
+ }),
492
+ Funding.schema.createFundingDepositAddress.Response,
493
+ ),
494
+ ).toEqual(second)
495
+ expect(calls).toBe(1)
496
+ })
497
+
498
+ test('rejects a refund address from another chain', async () => {
499
+ const response = await postDepositAddress(
500
+ transfersApp({ providers: [depositAddressProvider()] }),
501
+ { body: { ...depositAddressBody, refundAddress: `0x${'11'.repeat(20)}` } },
502
+ )
503
+
504
+ expect({ body: await response.json(), status: response.status }).toMatchInlineSnapshot(
505
+ {
506
+ body: { requestId: expect.any(String) },
507
+ },
508
+ `
509
+ {
510
+ "body": {
511
+ "error": {
512
+ "code": "body_invalid",
513
+ "message": "Invalid \`refundAddress\`.",
514
+ },
515
+ "requestId": Any<String>,
516
+ },
517
+ "status": 400,
518
+ }
519
+ `,
520
+ )
521
+ })
522
+
523
+ test('replays matching idempotent requests and rejects changed input', async () => {
524
+ const body = { ...depositAddressBody, refundAddress: 'TKJebW6SdoJhgkKU13BeKCbrMzT6HeQhJ6' }
525
+ const app = transfersApp({
526
+ providers: [depositAddressProvider('TLrtXaP4Yp1AGqrwQLGoHD5yGh4tTAvPbe')],
527
+ })
528
+ const idempotencyKey = nextDepositAddressKey()
529
+ const first = await postDepositAddress(app, { body, idempotencyKey })
530
+ const replay = await postDepositAddress(app, { body, idempotencyKey })
531
+ expect(replay.status).toBe(200)
532
+ expect(await replay.json()).toEqual(await first.json())
533
+
534
+ const conflict = await postDepositAddress(app, {
535
+ body: { ...depositAddressBody, refundAddress: 'TNR8TefgTphcrwQQodMxFDa6BPggc2CZeE' },
536
+ idempotencyKey,
537
+ })
538
+ expect(conflict.status).toBe(409)
539
+ expect(((await conflict.json()) as { error: { code: string } }).error.code).toBe(
540
+ 'idempotency_conflict',
541
+ )
542
+ })
543
+
544
+ test('defaults subsidize for legacy idempotency replays', async () => {
545
+ const app = transfersApp({
546
+ providers: [depositAddressProvider(`0x${'45'.repeat(20)}`)],
547
+ })
548
+ const requestBody = {
549
+ ...depositAddressBody,
550
+ refundAddress: `0x${'44'.repeat(20)}`,
551
+ sourceChain: 'base',
552
+ sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
553
+ }
554
+ const idempotencyKey = nextDepositAddressKey()
555
+ const first = await postDepositAddress(app, { body: requestBody, idempotencyKey })
556
+ const response = await TestApp.json(first, Funding.schema.createFundingDepositAddress.Response)
557
+ const { subsidize: _, ...legacy } = response
558
+ await db.kysely
559
+ .updateTable('funding_idempotency_requests')
560
+ .set({ response: JSON.stringify(legacy) })
561
+ .where('apiKeyId', '=', fundingWriter.id)
562
+ .where('keyHash', '=', Idempotency.inputHash(idempotencyKey))
563
+ .returningAll()
564
+ .executeTakeFirstOrThrow()
565
+
566
+ const replay = await postDepositAddress(app, { body: requestBody, idempotencyKey })
567
+ const response_replay = await TestApp.json(
568
+ replay,
569
+ Funding.schema.createFundingDepositAddress.Response,
570
+ )
571
+
572
+ expect({ status: replay.status, subsidize: response_replay.subsidize }).toMatchInlineSnapshot(`
573
+ {
574
+ "status": 200,
575
+ "subsidize": false,
576
+ }
577
+ `)
578
+ })
579
+
580
+ test('preserves the idempotency fingerprint when subsidize is omitted', async () => {
581
+ const idempotencyKey = nextDepositAddressKey()
582
+ await FundingIdempotencyTable.claim(db, {
583
+ apiKeyId: fundingWriter.id,
584
+ keyHash: Idempotency.inputHash(idempotencyKey),
585
+ requestHash: Idempotency.inputHash({
586
+ body: depositAddressBody,
587
+ endpoint: 'POST /v1/funding/deposit-addresses',
588
+ }),
589
+ ttlMs: 60_000,
590
+ })
591
+
592
+ const response = await postDepositAddress(transfersApp(), { idempotencyKey })
593
+
594
+ expect({ body: await response.json(), status: response.status }).toMatchInlineSnapshot(
595
+ {
596
+ body: { requestId: expect.any(String) },
597
+ },
598
+ `
599
+ {
600
+ "body": {
601
+ "error": {
602
+ "code": "idempotency_in_progress",
603
+ "message": "An equivalent funding deposit address creation is still running.",
604
+ },
605
+ "requestId": Any<String>,
606
+ },
607
+ "status": 409,
608
+ }
609
+ `,
610
+ )
611
+ })
612
+
613
+ test('includes the creation endpoint in the idempotency fingerprint', async () => {
614
+ const idempotencyKey = nextDepositAddressKey()
615
+ await FundingIdempotencyTable.claim(db, {
616
+ apiKeyId: fundingWriter.id,
617
+ keyHash: Idempotency.inputHash(idempotencyKey),
618
+ requestHash: Idempotency.inputHash(depositAddressBody),
619
+ ttlMs: 60_000,
620
+ })
621
+
622
+ const response = await postDepositAddress(
623
+ transfersApp({ providers: [depositAddressProvider()] }),
624
+ { idempotencyKey },
625
+ )
626
+ expect(response.status).toBe(409)
627
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
628
+ 'idempotency_conflict',
629
+ )
630
+ })
631
+
632
+ test('returns not available without a registered provider', async () => {
633
+ const response = await postDepositAddress(transfersApp({ providers: [] }))
634
+ expect(response.status).toBe(404)
635
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
636
+ 'deposit_address_not_available',
637
+ )
638
+ })
639
+
640
+ test('reports unexpected provider failures', async () => {
641
+ const entries: Log.Entry[] = []
642
+ const app = transfersApp({
643
+ logger: (entry) => {
644
+ entries.push(entry)
645
+ },
646
+ providers: [
647
+ withCreateDepositAddress('relay', async () => {
648
+ throw new Error('upstream failed')
649
+ }),
650
+ ],
651
+ })
652
+ const response = await postDepositAddress(app, {
653
+ body: { ...depositAddressBody, refundAddress: 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb' },
654
+ })
655
+ expect(response.status).toBe(502)
656
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
657
+ 'upstream_error',
658
+ )
659
+ expect(entries.at(-1)).toMatchObject({
660
+ provider: {
661
+ failure: 'unknown',
662
+ id: 'relay',
663
+ operation: 'createDepositAddress',
664
+ },
665
+ status: 502,
666
+ })
667
+ })
668
+ })
669
+
670
+ describe('POST /webhooks/funding/relay', () => {
671
+ test('authenticates and enqueues a bounded reconciliation hint', async () => {
672
+ const secret = 'relay-webhook-secret'
673
+ const address = await seedDepositAddress({
674
+ snapshot: TestFunding.depositAddressSnapshot({
675
+ provider: { id: 'relay', name: 'Relay' },
676
+ recipient: `0x${'99'.repeat(20)}`,
677
+ }),
678
+ })
679
+ const dispatched: FundingProvider.Webhook.receive.Dispatchable[] = []
680
+ const app = transfersApp({
681
+ providers: [
682
+ Relay.relay({
683
+ apiKey: secret,
684
+ webhook: {
685
+ db,
686
+ dispatch: async (message) => {
687
+ dispatched.push(message)
688
+ },
689
+ },
690
+ }),
691
+ ],
692
+ })
693
+ const timestamp = String(Date.now())
694
+ const body = JSON.stringify({
695
+ data: {
696
+ depositAddress: { address: address.address },
697
+ requestId: 'relay-child-request',
698
+ status: 'pending',
699
+ updatedAt: Number(timestamp) - 1_000,
700
+ },
701
+ event: 'request.status.updated',
702
+ timestamp: Number(timestamp),
703
+ })
704
+ const signature = Hash.hmac256(
705
+ Hex.fromString(secret),
706
+ Hex.fromString(`${timestamp}.${body}`),
707
+ ).slice(2)
708
+ const response = await app.request('/webhooks/funding/relay', {
709
+ body,
710
+ headers: {
711
+ 'x-signature-sha256': signature,
712
+ 'x-signature-timestamp': timestamp,
713
+ },
714
+ method: 'POST',
715
+ })
716
+
717
+ expect({ dispatched, status: response.status }).toMatchInlineSnapshot(
718
+ {
719
+ dispatched: [
720
+ {
721
+ webhookTiming: {
722
+ enqueuedAt: expect.any(String),
723
+ providerUpdatedAt: expect.any(String),
724
+ receivedAt: expect.any(String),
725
+ sentAt: expect.any(String),
726
+ },
727
+ },
728
+ ],
729
+ },
730
+ `
731
+ {
732
+ "dispatched": [
733
+ {
734
+ "addressId": "${address.id}",
735
+ "requestId": "relay-child-request",
736
+ "trigger": "webhook",
737
+ "type": "funding:deposit-address:reconcile",
738
+ "webhookTiming": {
739
+ "enqueuedAt": Any<String>,
740
+ "providerStatus": "pending",
741
+ "providerUpdatedAt": Any<String>,
742
+ "receivedAt": Any<String>,
743
+ "sentAt": Any<String>,
744
+ },
745
+ },
746
+ ],
747
+ "status": 204,
748
+ }
749
+ `,
750
+ )
751
+ })
752
+
753
+ test('rejects unauthenticated events', async () => {
754
+ const app = transfersApp({
755
+ providers: [
756
+ Relay.relay({
757
+ apiKey: 'relay-webhook-secret',
758
+ webhook: { db, dispatch: async () => {} },
759
+ }),
760
+ ],
761
+ })
762
+ const response = await app.request('/webhooks/funding/relay', {
763
+ body: '{}',
764
+ method: 'POST',
765
+ })
766
+
767
+ expect(response.status).toMatchInlineSnapshot('401')
768
+ })
769
+ })
770
+
771
+ describe.skipIf(!relayApiKey)('POST /funding/deposit-addresses (relay)', () => {
772
+ test('provisions and persists a reusable address through Relay', async () => {
773
+ const app = transfersApp({
774
+ providers: [Relay.relay({ apiKey: relayApiKey })],
775
+ })
776
+ const response = await postDepositAddress(app, {
777
+ body: { ...depositAddressBody, recipient: `0x${'77'.repeat(20)}` },
778
+ })
779
+
780
+ expect(response.status).toBe(200)
781
+ const body = await TestApp.json(response, Funding.schema.createFundingDepositAddress.Response)
782
+ expect(body).toMatchInlineSnapshot(
783
+ {
784
+ address: expect.any(String),
785
+ createdAt: expect.any(String),
786
+ destinationAmount: {
787
+ baseUnits: expect.any(String),
788
+ formatted: expect.any(String),
789
+ },
790
+ destinationAmountMin: {
791
+ baseUnits: expect.any(String),
792
+ formatted: expect.any(String),
793
+ },
794
+ fees: expect.any(Array),
795
+ id: expect.any(String),
796
+ quote: { sampledAt: expect.any(String) },
797
+ updatedAt: expect.any(String),
798
+ },
799
+ `
800
+ {
801
+ "address": Any<String>,
802
+ "createdAt": Any<String>,
803
+ "destinationAmount": {
804
+ "baseUnits": Any<String>,
805
+ "currency": "USD",
806
+ "decimals": 6,
807
+ "formatted": Any<String>,
808
+ },
809
+ "destinationAmountMin": {
810
+ "baseUnits": Any<String>,
811
+ "currency": "USD",
812
+ "decimals": 6,
813
+ "formatted": Any<String>,
814
+ },
815
+ "destinationChain": {
816
+ "addressFormat": "hex",
817
+ "id": "eip155:4217",
818
+ "kind": "evm",
819
+ "name": "Tempo",
820
+ },
821
+ "destinationToken": {
822
+ "address": "0x20c00000000000000000000014f22ca97301eb73",
823
+ "currency": "USD",
824
+ "decimals": 6,
825
+ "name": "USDT0",
826
+ "standard": "TIP-20",
827
+ "symbol": "USDT0",
828
+ "tokenKey": "eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73",
829
+ "verified": true,
830
+ },
831
+ "fees": Any<Array>,
832
+ "id": Any<String>,
833
+ "provider": {
834
+ "id": "relay",
835
+ "name": "Relay",
836
+ },
837
+ "quote": {
838
+ "sampledAt": Any<String>,
839
+ },
840
+ "recipient": "0x7777777777777777777777777777777777777777",
841
+ "refundAddress": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
842
+ "sourceAmount": {
843
+ "baseUnits": "100000000",
844
+ "currency": "USD",
845
+ "decimals": 6,
846
+ "formatted": "100",
847
+ },
848
+ "sourceChain": {
849
+ "addressFormat": "base58check",
850
+ "id": "tron:0x2b6653dc",
851
+ "kind": "tron",
852
+ "name": "Tron",
853
+ },
854
+ "sourceToken": {
855
+ "address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
856
+ "currency": "USD",
857
+ "decimals": 6,
858
+ "name": "Tether USD",
859
+ "standard": "TRC-20",
860
+ "symbol": "USDT",
861
+ "tokenKey": "tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
862
+ "verified": true,
863
+ },
864
+ "status": "active",
865
+ "subsidize": false,
866
+ "updatedAt": Any<String>,
867
+ }
868
+ `,
869
+ )
870
+ const record = await FundingDepositAddresses.get(db, body.id)
871
+ expect(record).toMatchInlineSnapshot(
872
+ {
873
+ address: expect.any(String),
874
+ createdAt: expect.any(String),
875
+ id: expect.any(String),
876
+ nextPollAt: expect.any(String),
877
+ providerRequestIds: [expect.any(String)],
878
+ providerState: expect.any(Object),
879
+ snapshot: { address: expect.any(String) },
880
+ updatedAt: expect.any(String),
881
+ },
882
+ `
883
+ {
884
+ "address": Any<String>,
885
+ "apiKeyId": "key_funding_writer",
886
+ "createdAt": Any<String>,
887
+ "deliveryStrategy": "provider",
888
+ "destinationTokenKey": "eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73",
889
+ "environment": "production",
890
+ "id": Any<String>,
891
+ "lastPolledAt": null,
892
+ "nextPollAt": Any<String>,
893
+ "orgId": "org_funding",
894
+ "pollFailureCount": 0,
895
+ "pollLeaseUntil": null,
896
+ "pollLeaseVersion": 0,
897
+ "projectId": null,
898
+ "providerId": "relay",
899
+ "providerOutputToken": {
900
+ "address": "0x20c00000000000000000000014f22ca97301eb73",
901
+ "currency": "USD",
902
+ "decimals": 6,
903
+ "name": "USDT0",
904
+ "standard": "TIP-20",
905
+ "symbol": "USDT0",
906
+ "tokenKey": "eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73",
907
+ "verified": true,
908
+ },
909
+ "providerRequestIds": [
910
+ Any<String>,
911
+ ],
912
+ "providerState": Any<Object>,
913
+ "recipient": "0x7777777777777777777777777777777777777777",
914
+ "refundAddress": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
915
+ "snapshot": {
916
+ "address": Any<String>,
917
+ "destinationChain": {
918
+ "addressFormat": "hex",
919
+ "id": "eip155:4217",
920
+ "kind": "evm",
921
+ "name": "Tempo",
922
+ },
923
+ "destinationToken": {
924
+ "address": "0x20c00000000000000000000014f22ca97301eb73",
925
+ "currency": "USD",
926
+ "decimals": 6,
927
+ "name": "USDT0",
928
+ "standard": "TIP-20",
929
+ "symbol": "USDT0",
930
+ "tokenKey": "eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73",
931
+ "verified": true,
932
+ },
933
+ "provider": {
934
+ "id": "relay",
935
+ "name": "Relay",
936
+ },
937
+ "recipient": "0x7777777777777777777777777777777777777777",
938
+ "refundAddress": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
939
+ "sourceChain": {
940
+ "addressFormat": "base58check",
941
+ "id": "tron:0x2b6653dc",
942
+ "kind": "tron",
943
+ "name": "Tron",
944
+ },
945
+ "sourceToken": {
946
+ "address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
947
+ "currency": "USD",
948
+ "decimals": 6,
949
+ "name": "Tether USD",
950
+ "standard": "TRC-20",
951
+ "symbol": "USDT",
952
+ "tokenKey": "tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
953
+ "verified": true,
954
+ },
955
+ "subsidize": false,
956
+ },
957
+ "sourceChainId": "tron:0x2b6653dc",
958
+ "sourceTokenKey": "tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
959
+ "status": "active",
960
+ "subsidize": false,
961
+ "updatedAt": Any<String>,
962
+ "version": 1,
963
+ }
964
+ `,
965
+ )
966
+ })
967
+ })
968
+
969
+ describe('GET /funding/deposit-addresses/:id', () => {
970
+ test('requires funding:read and returns the owned address', async () => {
971
+ const app = transfersApp()
972
+ const record = await seedDepositAddress()
973
+ const path = `/v1/funding/deposit-addresses/${record.id}`
974
+ expect((await app.request(path)).status).toBe(401)
975
+ expect((await app.request(path, as(TestApp.key))).status).toBe(403)
976
+
977
+ const response = await app.request(path, as(fundingReader))
978
+ expect(response.status).toBe(200)
979
+ expect({
980
+ cacheControl: response.headers.get('cache-control'),
981
+ etag: response.headers.get('etag'),
982
+ }).toMatchInlineSnapshot(`
983
+ {
984
+ "cacheControl": "no-store",
985
+ "etag": null,
986
+ }
987
+ `)
988
+ expect(response.headers.get('vary')).toBe(
989
+ 'Accept-Encoding, Authorization, Tempo-API-Key, X-API-Key',
990
+ )
991
+ const body = await TestApp.json(response, Funding.schema.getFundingDepositAddress.Response)
992
+ expect(body).toEqual(FundingDepositAddress.toPublic(record))
993
+ expect(body).not.toHaveProperty('providerOutputToken')
994
+ expect(body).not.toHaveProperty('providerRequestIds')
995
+ })
996
+
997
+ test('hides foreign, project-inaccessible, and absent addresses', async () => {
998
+ const app = transfersApp()
999
+ const record = await seedDepositAddress()
1000
+ const path = `/v1/funding/deposit-addresses/${record.id}`
1001
+
1002
+ for (const key of [fundingForeign, fundingProjectReader]) {
1003
+ const response = await app.request(path, as(key))
1004
+ expect(response.status).toBe(404)
1005
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
1006
+ 'funding_deposit_address_not_found',
1007
+ )
1008
+ }
1009
+
1010
+ const absent = await app.request(
1011
+ '/v1/funding/deposit-addresses/fda_000000000000000_000000000000000000000000',
1012
+ as(fundingReader),
1013
+ )
1014
+ expect(absent.status).toBe(404)
1015
+ })
1016
+ })
1017
+
1018
+ describe('POST /funding/deposit-addresses/:id/reconcile', () => {
1019
+ test('requires funding:write and queues the owned active address', async () => {
1020
+ const dispatched: FundingProvider.Webhook.receive.Dispatchable[] = []
1021
+ const app = transfersApp({
1022
+ funding: {
1023
+ dispatchReconciliation: async (message) => {
1024
+ dispatched.push(message)
1025
+ },
1026
+ },
1027
+ })
1028
+ const record = await seedDepositAddress()
1029
+ const path = `/v1/funding/deposit-addresses/${record.id}/reconcile`
1030
+ expect((await app.request(path, { method: 'POST' })).status).toBe(401)
1031
+ expect((await app.request(path, { ...as(fundingReader), method: 'POST' })).status).toBe(403)
1032
+
1033
+ const response = await app.request(path, { ...as(fundingWriter), method: 'POST' })
1034
+ expect({
1035
+ body: await response.text(),
1036
+ cacheControl: response.headers.get('cache-control'),
1037
+ dispatched,
1038
+ rateLimit: {
1039
+ limit: response.headers.get('ratelimit-limit'),
1040
+ remaining: response.headers.get('ratelimit-remaining'),
1041
+ scope: response.headers.get('ratelimit-scope'),
1042
+ },
1043
+ status: response.status,
1044
+ vary: response.headers.get('vary'),
1045
+ }).toMatchInlineSnapshot(`
1046
+ {
1047
+ "body": "",
1048
+ "cacheControl": "no-store",
1049
+ "dispatched": [
1050
+ {
1051
+ "addressId": "${record.id}",
1052
+ "trigger": "manual",
1053
+ "type": "funding:deposit-address:reconcile",
1054
+ },
1055
+ ],
1056
+ "rateLimit": {
1057
+ "limit": "10",
1058
+ "remaining": "9",
1059
+ "scope": "funding:write",
1060
+ },
1061
+ "status": 202,
1062
+ "vary": "Accept-Encoding, Authorization, Tempo-API-Key",
1063
+ }
1064
+ `)
1065
+ expect(await FundingDepositAddresses.get(db, record.id)).toEqual(record)
1066
+ })
1067
+
1068
+ test('hides foreign, project-inaccessible, and absent addresses', async () => {
1069
+ const dispatched: FundingProvider.Webhook.receive.Dispatchable[] = []
1070
+ const app = transfersApp({
1071
+ funding: {
1072
+ dispatchReconciliation: async (message) => {
1073
+ dispatched.push(message)
1074
+ },
1075
+ },
1076
+ })
1077
+ const record = await seedDepositAddress()
1078
+ const path = `/v1/funding/deposit-addresses/${record.id}/reconcile`
1079
+
1080
+ for (const key of [fundingForeignWriter, fundingProjectWriter]) {
1081
+ const response = await app.request(path, { ...as(key), method: 'POST' })
1082
+ expect(response.status).toBe(404)
1083
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
1084
+ 'funding_deposit_address_not_found',
1085
+ )
1086
+ }
1087
+
1088
+ const absent = await app.request(
1089
+ '/v1/funding/deposit-addresses/fda_000000000000000_000000000000000000000000/reconcile',
1090
+ { ...as(fundingWriter), method: 'POST' },
1091
+ )
1092
+ expect(absent.status).toBe(404)
1093
+ expect(dispatched).toEqual([])
1094
+ })
1095
+
1096
+ test('rejects inactive addresses and unavailable reconciliation', async () => {
1097
+ const dispatched: FundingProvider.Webhook.receive.Dispatchable[] = []
1098
+ const configured = transfersApp({
1099
+ funding: {
1100
+ dispatchReconciliation: async (message) => {
1101
+ dispatched.push(message)
1102
+ },
1103
+ },
1104
+ })
1105
+ const record = await seedDepositAddress()
1106
+ const transition = await FundingDepositAddress.transition(db, {
1107
+ expectedVersion: record.version,
1108
+ id: record.id,
1109
+ status: 'action-required',
1110
+ })
1111
+ expect(transition.type).toBe('applied')
1112
+
1113
+ const inactive = await configured.request(
1114
+ `/v1/funding/deposit-addresses/${record.id}/reconcile`,
1115
+ { ...as(fundingWriter), method: 'POST' },
1116
+ )
1117
+ expect({
1118
+ code: ((await inactive.json()) as { error: { code: string } }).error.code,
1119
+ status: inactive.status,
1120
+ }).toEqual({ code: 'funding_deposit_address_not_active', status: 409 })
1121
+
1122
+ const active = await seedDepositAddress()
1123
+ const unavailable = await transfersApp().request(
1124
+ `/v1/funding/deposit-addresses/${active.id}/reconcile`,
1125
+ { ...as(fundingWriter), method: 'POST' },
1126
+ )
1127
+ expect({
1128
+ code: ((await unavailable.json()) as { error: { code: string } }).error.code,
1129
+ status: unavailable.status,
1130
+ }).toEqual({ code: 'not_implemented', status: 501 })
1131
+ expect(dispatched).toEqual([])
1132
+ })
1133
+
1134
+ test('returns an upstream error when the queue rejects the message', async () => {
1135
+ const app = transfersApp({
1136
+ funding: {
1137
+ dispatchReconciliation: async () => {
1138
+ throw new Error('queue unavailable')
1139
+ },
1140
+ },
1141
+ })
1142
+ const record = await seedDepositAddress()
1143
+ const response = await app.request(`/v1/funding/deposit-addresses/${record.id}/reconcile`, {
1144
+ ...as(fundingWriter),
1145
+ method: 'POST',
1146
+ })
1147
+
1148
+ expect({
1149
+ code: ((await response.json()) as { error: { code: string } }).error.code,
1150
+ status: response.status,
1151
+ }).toEqual({ code: 'upstream_error', status: 502 })
1152
+ })
1153
+ })
1154
+
1155
+ describe('GET /funding/deposit-addresses/:id/deposits', () => {
1156
+ test('pages deposits newest-first under an owned address', async () => {
1157
+ const app = transfersApp()
1158
+ const address = await seedDepositAddress()
1159
+ const first = await seedDeposit(address)
1160
+ const second = await seedDeposit(address)
1161
+ const third = await seedDeposit(address)
1162
+ const fourth = await seedDeposit(address)
1163
+ const fifth = await seedDeposit(address)
1164
+ const sixth = await seedDeposit(address)
1165
+
1166
+ const response = await app.request(
1167
+ `/v1/funding/deposit-addresses/${address.id}/deposits?limit=5`,
1168
+ as(fundingReader),
1169
+ )
1170
+ expect(response.status).toBe(200)
1171
+ expect({
1172
+ cacheControl: response.headers.get('cache-control'),
1173
+ etag: response.headers.get('etag'),
1174
+ }).toMatchInlineSnapshot(`
1175
+ {
1176
+ "cacheControl": "no-store",
1177
+ "etag": null,
1178
+ }
1179
+ `)
1180
+ const body = await TestApp.json(
1181
+ response,
1182
+ Funding.schema.listFundingDepositAddressDeposits.Response,
1183
+ )
1184
+ expect(body.data.map((deposit) => deposit.id)).toEqual([
1185
+ sixth.id,
1186
+ fifth.id,
1187
+ fourth.id,
1188
+ third.id,
1189
+ second.id,
1190
+ ])
1191
+ expect(body.nextCursor).toBe(Cursor.encode([second.createdAt, second.id]))
1192
+
1193
+ const next = await TestApp.json(
1194
+ await app.request(
1195
+ `/v1/funding/deposit-addresses/${address.id}/deposits?limit=5&cursor=${body.nextCursor}`,
1196
+ as(fundingReader),
1197
+ ),
1198
+ Funding.schema.listFundingDepositAddressDeposits.Response,
1199
+ )
1200
+ expect(next.data.map((deposit) => deposit.id)).toEqual([first.id])
1201
+ expect(next.nextCursor).toBeNull()
1202
+ })
1203
+
1204
+ test('returns a provider-observed deposit before source verification', async () => {
1205
+ const app = transfersApp()
1206
+ const address = await seedDepositAddress({
1207
+ snapshot: TestFunding.depositAddressSnapshot({
1208
+ provider: { id: 'relay_observed', name: 'Relay' },
1209
+ recipient: `0x${'31'.repeat(20)}`,
1210
+ }),
1211
+ })
1212
+ const sourceTransactionHash = `0x${'ab'.repeat(32)}`
1213
+ await FundingDeposit.create(db, {
1214
+ now: new Date('2026-01-04T00:00:00.000Z'),
1215
+ providerRequestId: 'relay-observed-request',
1216
+ snapshot: {
1217
+ depositAddressId: address.id,
1218
+ destinationChain: address.snapshot.destinationChain,
1219
+ destinationToken: address.snapshot.destinationToken,
1220
+ provider: address.snapshot.provider,
1221
+ recipient: address.recipient,
1222
+ refundAddress: address.refundAddress,
1223
+ sourceChain: address.snapshot.sourceChain,
1224
+ sourceToken: address.snapshot.sourceToken,
1225
+ sourceTransactionHashes: [sourceTransactionHash],
1226
+ },
1227
+ sourceTransactionHash,
1228
+ })
1229
+
1230
+ const response = await app.request(
1231
+ `/v1/funding/deposit-addresses/${address.id}/deposits`,
1232
+ as(fundingReader),
1233
+ )
1234
+ expect(response.status).toBe(200)
1235
+ const body = await TestApp.json(
1236
+ response,
1237
+ Funding.schema.listFundingDepositAddressDeposits.Response,
1238
+ )
1239
+ expect(body).toMatchInlineSnapshot(
1240
+ {
1241
+ data: [
1242
+ {
1243
+ createdAt: expect.any(String),
1244
+ depositAddressId: expect.any(String),
1245
+ id: expect.any(String),
1246
+ updatedAt: expect.any(String),
1247
+ },
1248
+ ],
1249
+ },
1250
+ `
1251
+ {
1252
+ "data": [
1253
+ {
1254
+ "createdAt": Any<String>,
1255
+ "depositAddressId": Any<String>,
1256
+ "destinationChain": {
1257
+ "addressFormat": "hex",
1258
+ "id": "eip155:4217",
1259
+ "kind": "evm",
1260
+ "name": "Tempo",
1261
+ },
1262
+ "destinationToken": {
1263
+ "address": "0x20c00000000000000000000014f22ca97301eb73",
1264
+ "currency": "USD",
1265
+ "decimals": 6,
1266
+ "name": "USDT0",
1267
+ "standard": "TIP-20",
1268
+ "symbol": "USDT0",
1269
+ "tokenKey": "eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73",
1270
+ "verified": true,
1271
+ },
1272
+ "id": Any<String>,
1273
+ "provider": {
1274
+ "id": "relay_observed",
1275
+ "name": "Relay",
1276
+ },
1277
+ "recipient": "0x3131313131313131313131313131313131313131",
1278
+ "refundAddress": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
1279
+ "sourceChain": {
1280
+ "addressFormat": "base58check",
1281
+ "id": "tron:0x2b6653dc",
1282
+ "kind": "tron",
1283
+ "name": "Tron",
1284
+ },
1285
+ "sourceToken": {
1286
+ "address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
1287
+ "currency": "USD",
1288
+ "decimals": 6,
1289
+ "name": "Tether USD",
1290
+ "standard": "TRC-20",
1291
+ "symbol": "USDT",
1292
+ "tokenKey": "tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
1293
+ "verified": true,
1294
+ },
1295
+ "sourceTransactionHashes": [
1296
+ "0xabababababababababababababababababababababababababababababababab",
1297
+ ],
1298
+ "status": "detected",
1299
+ "updatedAt": Any<String>,
1300
+ },
1301
+ ],
1302
+ "nextCursor": null,
1303
+ }
1304
+ `,
1305
+ )
1306
+ })
1307
+
1308
+ test('returns the parent not-found error before listing deposits', async () => {
1309
+ const app = transfersApp()
1310
+ const address = await seedDepositAddress({ orgId: fundingForeign.orgId })
1311
+ const response = await app.request(
1312
+ `/v1/funding/deposit-addresses/${address.id}/deposits`,
1313
+ as(fundingReader),
1314
+ )
1315
+ expect(response.status).toBe(404)
1316
+ expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
1317
+ 'funding_deposit_address_not_found',
1318
+ )
1319
+ })
1320
+ })
1321
+
1322
+ describe('GET /funding/deposits', () => {
1323
+ test('requires funding:read and pages recipient deposits across addresses', async () => {
1324
+ const app = transfersApp()
1325
+ const recipient = '0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD'
1326
+ const path = `/v1/funding/deposits?recipient=${recipient.toLowerCase()}`
1327
+ expect((await app.request(path)).status).toBe(401)
1328
+ expect((await app.request(path, as(TestApp.key))).status).toBe(403)
1329
+ expect((await app.request('/v1/funding/deposits', as(fundingReader))).status).toBe(400)
1330
+
1331
+ const firstAddress = await seedDepositAddress({
1332
+ snapshot: TestFunding.depositAddressSnapshot({
1333
+ provider: { id: 'relay_account_first', name: 'Relay' },
1334
+ recipient,
1335
+ }),
1336
+ })
1337
+ const secondAddress = await seedDepositAddress({
1338
+ snapshot: TestFunding.depositAddressSnapshot({
1339
+ provider: { id: 'relay_account_second', name: 'Relay' },
1340
+ recipient,
1341
+ subsidize: true,
1342
+ }),
1343
+ })
1344
+ const first = await seedDeposit(firstAddress)
1345
+ const second = await seedDeposit(secondAddress)
1346
+ const third = await seedDeposit(firstAddress)
1347
+ const fourth = await seedDeposit(secondAddress)
1348
+ const fifth = await seedDeposit(firstAddress)
1349
+ const sixth = await seedDeposit(secondAddress)
1350
+ const unrelatedAddress = await seedDepositAddress()
1351
+ await seedDeposit(unrelatedAddress)
1352
+ const foreignAddress = await seedDepositAddress({
1353
+ orgId: fundingForeign.orgId,
1354
+ snapshot: TestFunding.depositAddressSnapshot({
1355
+ provider: { id: 'relay_account_foreign', name: 'Relay' },
1356
+ recipient,
1357
+ }),
1358
+ })
1359
+ await seedDeposit(foreignAddress)
1360
+
1361
+ const response = await app.request(`${path}&limit=5`, as(fundingReader))
1362
+ expect(response.status).toBe(200)
1363
+ expect({
1364
+ cacheControl: response.headers.get('cache-control'),
1365
+ etag: response.headers.get('etag'),
1366
+ vary: response.headers.get('vary'),
1367
+ }).toMatchInlineSnapshot(`
1368
+ {
1369
+ "cacheControl": "no-store",
1370
+ "etag": null,
1371
+ "vary": "Accept-Encoding, Authorization, Tempo-API-Key",
1372
+ }
1373
+ `)
1374
+ const body = await TestApp.json(response, Funding.schema.listFundingDeposits.Response)
1375
+ expect(body.data.map((deposit) => deposit.id)).toEqual([
1376
+ sixth.id,
1377
+ fifth.id,
1378
+ fourth.id,
1379
+ third.id,
1380
+ second.id,
1381
+ ])
1382
+ expect(body.nextCursor).toBe(Cursor.encode([second.createdAt, second.id]))
1383
+
1384
+ const next = await TestApp.json(
1385
+ await app.request(`${path}&limit=5&cursor=${body.nextCursor}`, as(fundingReader)),
1386
+ Funding.schema.listFundingDeposits.Response,
1387
+ )
1388
+ expect(next.data.map((deposit) => deposit.id)).toEqual([first.id])
1389
+ expect(next.nextCursor).toBeNull()
1390
+ })
1391
+
1392
+ test('narrows project-attributed keys to their project', async () => {
1393
+ const app = transfersApp()
1394
+ const recipient = `0x${'ab'.repeat(20)}`
1395
+ const projectAddress = await seedDepositAddress({
1396
+ projectId: fundingProjectReader.projectId,
1397
+ snapshot: TestFunding.depositAddressSnapshot({
1398
+ provider: { id: 'relay_account_project', name: 'Relay' },
1399
+ recipient,
1400
+ }),
1401
+ })
1402
+ const siblingAddress = await seedDepositAddress({
1403
+ projectId: 'prj_funding_other',
1404
+ snapshot: TestFunding.depositAddressSnapshot({
1405
+ provider: { id: 'relay_account_sibling', name: 'Relay' },
1406
+ recipient,
1407
+ }),
1408
+ })
1409
+ const projectDeposit = await seedDeposit(projectAddress)
1410
+ await seedDeposit(siblingAddress)
1411
+
1412
+ const body = await TestApp.json(
1413
+ await app.request(`/v1/funding/deposits?recipient=${recipient}`, as(fundingProjectReader)),
1414
+ Funding.schema.listFundingDeposits.Response,
1415
+ )
1416
+ expect(body.data.map((deposit) => deposit.id)).toEqual([projectDeposit.id])
1417
+ })
1418
+ })
1419
+
1420
+ describe('GET /funding/deposits/:id', () => {
1421
+ test('returns the owned deposit without private reconciliation state', async () => {
1422
+ const app = transfersApp()
1423
+ const address = await seedDepositAddress()
1424
+ const record = await seedDeposit(address, {
1425
+ providerRequestIds: ['relay-private'],
1426
+ providerState: { request: 'private' },
1427
+ })
1428
+ const response = await app.request(`/v1/funding/deposits/${record.id}`, as(fundingReader))
1429
+ expect(response.status).toBe(200)
1430
+ expect(response.headers.get('cache-control')).toBe(Cache.policies.state)
1431
+ const body = await TestApp.json(response, Funding.schema.getFundingDeposit.Response)
1432
+ expect(body).toEqual(FundingDeposit.toPublic(record))
1433
+ expect(body).not.toHaveProperty('providerRequestIds')
1434
+ expect(body).not.toHaveProperty('providerState')
1435
+ })
1436
+
1437
+ test('hides foreign and absent deposits', async () => {
1438
+ const app = transfersApp()
1439
+ const address = await seedDepositAddress()
1440
+ const record = await seedDeposit(address)
1441
+ expect(
1442
+ (await app.request(`/v1/funding/deposits/${record.id}`, as(fundingForeign))).status,
1443
+ ).toBe(404)
1444
+ expect(
1445
+ (
1446
+ await app.request(
1447
+ '/v1/funding/deposits/fdp_000000000000000_000000000000000000000000',
1448
+ as(fundingReader),
1449
+ )
1450
+ ).status,
1451
+ ).toBe(404)
1452
+ })
1453
+ })
1454
+
1455
+ const depositAddressBody = {
1456
+ amount: '100000000',
1457
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
1458
+ recipient: `0x${'11'.repeat(20)}`,
1459
+ refundAddress: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
1460
+ sourceChain: 'tron',
1461
+ sourceToken: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
1462
+ }
1463
+
1464
+ let depositAddressIdempotencySequence = 0
1465
+
1466
+ function nextDepositAddressKey() {
1467
+ return `funding_deposit_address_${depositAddressIdempotencySequence++}`
1468
+ }
1469
+
1470
+ function postDepositAddress(
1471
+ app: ReturnType<typeof create>,
1472
+ options: {
1473
+ body?: unknown
1474
+ headers?: Record<string, string> | undefined
1475
+ idempotencyKey?: string | undefined
1476
+ key?: { token: string } | undefined
1477
+ } = {},
1478
+ ) {
1479
+ return app.request('/v1/funding/deposit-addresses', {
1480
+ body: JSON.stringify(options.body ?? depositAddressBody),
1481
+ headers: options.headers ?? {
1482
+ authorization: `Bearer ${(options.key ?? fundingWriter).token}`,
1483
+ 'content-type': 'application/json',
1484
+ 'idempotency-key': options.idempotencyKey ?? nextDepositAddressKey(),
1485
+ },
1486
+ method: 'POST',
1487
+ })
1488
+ }
1489
+
1490
+ function withCreateDepositAddress(
1491
+ id: FundingProvider.ProviderId,
1492
+ createDepositAddress: NonNullable<FundingProvider.Provider['createDepositAddress']>,
1493
+ ) {
1494
+ const provider = providers.find((entry) => entry.id === id)
1495
+ if (!provider) throw new Error(`Missing ${id} provider`)
1496
+ return FundingProvider.from({ ...provider, createDepositAddress })
1497
+ }
1498
+
1499
+ function depositAddressProvider(
1500
+ address = 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
1501
+ onCreate: (input: FundingProvider.createDepositAddress.Parameters) => void = () => {},
1502
+ ) {
1503
+ return withCreateDepositAddress('relay', async (input) => {
1504
+ onCreate(input)
1505
+ return {
1506
+ address,
1507
+ correlation: {
1508
+ checkEndpoint: '/intents/status/v3?requestId=relay_request_1',
1509
+ requestId: 'relay_request_1',
1510
+ },
1511
+ destinationAmount: '1000000',
1512
+ destinationAmountMin: '1000000',
1513
+ fees: [],
1514
+ sampledAt: '2026-08-04T00:00:00.000Z',
1515
+ }
1516
+ })
1517
+ }
1518
+
1519
+ const fundingReader = {
1520
+ id: 'key_funding',
1521
+ orgId: 'org_funding',
1522
+ scopes: ['funding:read'],
1523
+ token: 'secret_funding_reader',
1524
+ } satisfies TestApp.kvStore.Key
1525
+
1526
+ const fundingProjectReader = {
1527
+ id: 'key_funding_project',
1528
+ orgId: 'org_funding',
1529
+ projectId: 'prj_funding',
1530
+ scopes: ['funding:read'],
1531
+ token: 'secret_funding_project',
1532
+ } satisfies TestApp.kvStore.Key
1533
+
1534
+ const fundingForeign = {
1535
+ id: 'key_funding_other',
1536
+ orgId: 'org_funding_other',
1537
+ scopes: ['funding:read'],
1538
+ token: 'secret_funding_other',
1539
+ } satisfies TestApp.kvStore.Key
1540
+
1541
+ /** Key with funding write access in a different organization. */
1542
+ const fundingForeignWriter = {
1543
+ id: 'key_funding_other_writer',
1544
+ orgId: fundingForeign.orgId,
1545
+ scopes: ['funding:write'],
1546
+ token: 'secret_funding_other_writer',
1547
+ } satisfies TestApp.kvStore.Key
1548
+
1549
+ /** Project-attributed funding writer in the owning organization. */
1550
+ const fundingProjectWriter = {
1551
+ id: 'key_funding_project_writer',
1552
+ orgId: fundingReader.orgId,
1553
+ projectId: fundingProjectReader.projectId,
1554
+ scopes: ['funding:write'],
1555
+ token: 'secret_funding_project_writer',
1556
+ } satisfies TestApp.kvStore.Key
1557
+
1558
+ const fundingWriter = {
1559
+ id: 'key_funding_writer',
1560
+ orgId: 'org_funding',
1561
+ scopes: ['funding:read', 'funding:write'],
1562
+ token: 'secret_funding_writer',
1563
+ } satisfies TestApp.kvStore.Key
1564
+
1565
+ function transfersApp(options: TestApp.create.Options = {}) {
1566
+ return create({
1567
+ ...options,
1568
+ auth: {
1569
+ keys: [
1570
+ TestApp.key,
1571
+ fundingForeign,
1572
+ fundingForeignWriter,
1573
+ fundingProjectReader,
1574
+ fundingProjectWriter,
1575
+ fundingReader,
1576
+ fundingWriter,
1577
+ ],
1578
+ },
1579
+ defaultChainId: options.defaultChainId ?? tempoMainnet.id,
1580
+ })
1581
+ }
1582
+
1583
+ function as(key: { token: string }) {
1584
+ return { headers: { authorization: `Bearer ${key.token}` } } as const
1585
+ }
1586
+
1587
+ let seedDepositAddressSecond = 0
1588
+
1589
+ function seedDepositAddress(input: Partial<FundingDepositAddress.create.Input> = {}) {
1590
+ const index = seedDepositAddressSecond++
1591
+ const now = new Date(Date.UTC(2026, 0, 3, 0, 0, index))
1592
+ return FundingDepositAddress.create(db, {
1593
+ apiKeyId: fundingReader.id,
1594
+ deliveryStrategy: 'provider',
1595
+ environment: 'production',
1596
+ id: FundingDepositAddress.generateId(now),
1597
+ now,
1598
+ orgId: fundingReader.orgId,
1599
+ providerOutputToken: TestFunding.providerOutputToken(),
1600
+ snapshot: TestFunding.depositAddressSnapshot({
1601
+ provider: { id: `relay_route_${index}`, name: 'Relay' },
1602
+ recipient: `0x${index.toString(16).padStart(40, '0')}`,
1603
+ }),
1604
+ ...input,
1605
+ })
1606
+ }
1607
+
1608
+ let seedDepositSecond = 0
1609
+
1610
+ function seedDeposit(
1611
+ address: Awaited<ReturnType<typeof seedDepositAddress>>,
1612
+ input: Partial<FundingDeposit.create.Input> = {},
1613
+ ) {
1614
+ const index = seedDepositSecond++
1615
+ const now = new Date(Date.UTC(2026, 0, 4, 0, 0, index))
1616
+ const sourceTransactionHash = index.toString(16).padStart(64, '0')
1617
+ return FundingDeposit.create(db, {
1618
+ now,
1619
+ providerRequestId: `relay_request_${index}`,
1620
+ snapshot: TestFunding.depositSnapshot(address.id, {
1621
+ provider: address.snapshot.provider,
1622
+ sourceTransactionHashes: [sourceTransactionHash],
1623
+ }),
1624
+ sourceTransactionHash,
1625
+ sourceTransferIndex: 0,
1626
+ ...input,
1627
+ })
1628
+ }