tapimo 0.12.0 → 0.14.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 (398) hide show
  1. package/dist/App.d.ts +11333 -9563
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +39 -3
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +2 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +17 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/Webhooks.js +2 -2
  10. package/dist/Webhooks.js.map +1 -1
  11. package/dist/admin/App.d.ts +1064 -924
  12. package/dist/admin/App.d.ts.map +1 -1
  13. package/dist/admin/apps/api-keys.d.ts +80 -0
  14. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  15. package/dist/admin/apps/api-keys.js +54 -0
  16. package/dist/admin/apps/api-keys.js.map +1 -1
  17. package/dist/admin/apps/earn-vaults.d.ts +63 -15
  18. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  19. package/dist/admin/apps/earn-vaults.js +2 -1
  20. package/dist/admin/apps/earn-vaults.js.map +1 -1
  21. package/dist/admin/apps/organizations.d.ts +92 -3
  22. package/dist/admin/apps/organizations.d.ts.map +1 -1
  23. package/dist/admin/apps/organizations.js +69 -7
  24. package/dist/admin/apps/organizations.js.map +1 -1
  25. package/dist/admin/apps/verified-tokens.d.ts +7 -7
  26. package/dist/admin/ui.generated.d.ts +1 -1
  27. package/dist/admin/ui.generated.d.ts.map +1 -1
  28. package/dist/admin/ui.generated.js +151 -151
  29. package/dist/admin/ui.generated.js.map +1 -1
  30. package/dist/apps/Zones.d.ts +28 -28
  31. package/dist/apps/Zones.js +7 -7
  32. package/dist/apps/data/App.d.ts +2909 -2523
  33. package/dist/apps/data/App.d.ts.map +1 -1
  34. package/dist/apps/data/App.js +26 -21
  35. package/dist/apps/data/App.js.map +1 -1
  36. package/dist/apps/data/ZoneSelection.d.ts +8 -8
  37. package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
  38. package/dist/apps/data/ZoneSelection.js +1 -1
  39. package/dist/apps/data/ZoneSelection.js.map +1 -1
  40. package/dist/apps/data/routes/activities.d.ts +588 -584
  41. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  42. package/dist/apps/data/routes/activities.js +1561 -1239
  43. package/dist/apps/data/routes/activities.js.map +1 -1
  44. package/dist/apps/data/routes/addresses.d.ts +22 -22
  45. package/dist/apps/data/routes/balances.d.ts +26 -26
  46. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  47. package/dist/apps/data/routes/balances.js +43 -16
  48. package/dist/apps/data/routes/balances.js.map +1 -1
  49. package/dist/apps/data/routes/blocks.d.ts +44 -44
  50. package/dist/apps/data/routes/blocks.d.ts.map +1 -1
  51. package/dist/apps/data/routes/blocks.js +21 -11
  52. package/dist/apps/data/routes/blocks.js.map +1 -1
  53. package/dist/apps/data/routes/coingecko.d.ts +3 -3
  54. package/dist/apps/data/routes/earn.d.ts +201 -25
  55. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  56. package/dist/apps/data/routes/earn.js +434 -75
  57. package/dist/apps/data/routes/earn.js.map +1 -1
  58. package/dist/apps/data/routes/exchanges.d.ts +160 -145
  59. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  60. package/dist/apps/data/routes/exchanges.js +180 -114
  61. package/dist/apps/data/routes/exchanges.js.map +1 -1
  62. package/dist/apps/data/routes/fee-amm.d.ts +2 -2
  63. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  64. package/dist/apps/data/routes/fee-amm.js +18 -19
  65. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  66. package/dist/apps/data/routes/receipts.d.ts +155 -55
  67. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  68. package/dist/apps/data/routes/receipts.js +28 -10
  69. package/dist/apps/data/routes/receipts.js.map +1 -1
  70. package/dist/apps/data/routes/tokenlist.d.ts +13 -13
  71. package/dist/apps/data/routes/tokens.d.ts +5 -5
  72. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  73. package/dist/apps/data/routes/tokens.js +51 -20
  74. package/dist/apps/data/routes/tokens.js.map +1 -1
  75. package/dist/apps/data/routes/transactions.d.ts +7 -2
  76. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  77. package/dist/apps/data/routes/transactions.js +37 -17
  78. package/dist/apps/data/routes/transactions.js.map +1 -1
  79. package/dist/apps/data/routes/transfers.d.ts +13 -13
  80. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  81. package/dist/apps/data/routes/transfers.js +18 -12
  82. package/dist/apps/data/routes/transfers.js.map +1 -1
  83. package/dist/apps/data/routes/valuation.d.ts +31 -31
  84. package/dist/apps/data/routes/valuation.d.ts.map +1 -1
  85. package/dist/apps/data/routes/valuation.js +12 -7
  86. package/dist/apps/data/routes/valuation.js.map +1 -1
  87. package/dist/apps/data/routes/verified-tokens.d.ts +8 -8
  88. package/dist/apps/data/routes/webhooks.d.ts +816 -113
  89. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  90. package/dist/apps/data/routes/webhooks.js +47 -5
  91. package/dist/apps/data/routes/webhooks.js.map +1 -1
  92. package/dist/apps/data/routes/zones.d.ts +14 -14
  93. package/dist/apps/data/routes/zones.js +1 -1
  94. package/dist/apps/funding/App.d.ts +466 -44
  95. package/dist/apps/funding/App.d.ts.map +1 -1
  96. package/dist/apps/funding/App.js +1 -1
  97. package/dist/apps/funding/App.js.map +1 -1
  98. package/dist/apps/funding/routes/deposit-addresses.d.ts +17 -17
  99. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  100. package/dist/apps/funding/routes/deposit-addresses.js +17 -12
  101. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  102. package/dist/apps/funding/routes/transfers.d.ts +650 -57
  103. package/dist/apps/funding/routes/transfers.d.ts.map +1 -1
  104. package/dist/apps/funding/routes/transfers.js +238 -24
  105. package/dist/apps/funding/routes/transfers.js.map +1 -1
  106. package/dist/apps/management/App.d.ts +5120 -4152
  107. package/dist/apps/management/App.d.ts.map +1 -1
  108. package/dist/apps/management/Billing.d.ts +1 -1
  109. package/dist/apps/management/Billing.d.ts.map +1 -1
  110. package/dist/apps/management/Billing.js +17 -1
  111. package/dist/apps/management/Billing.js.map +1 -1
  112. package/dist/apps/management/routes/api-keys.d.ts +632 -81
  113. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  114. package/dist/apps/management/routes/api-keys.js +236 -4
  115. package/dist/apps/management/routes/api-keys.js.map +1 -1
  116. package/dist/apps/management/routes/billing.d.ts +76 -76
  117. package/dist/apps/management/routes/faucet.d.ts +6 -6
  118. package/dist/apps/management/routes/invitations.d.ts +24 -24
  119. package/dist/apps/management/routes/invite-links.d.ts +4 -4
  120. package/dist/apps/management/routes/members.d.ts +16 -16
  121. package/dist/apps/management/routes/orgs.d.ts +20 -20
  122. package/dist/apps/management/routes/projects.d.ts +4 -4
  123. package/dist/apps/management/routes/usage.d.ts +8 -8
  124. package/dist/apps/management/routes/verified-token-requests.d.ts +17 -17
  125. package/dist/apps/management/routes/webhooks.d.ts +675 -25
  126. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  127. package/dist/apps/management/routes/webhooks.js +21 -21
  128. package/dist/apps/management/routes/webhooks.js.map +1 -1
  129. package/dist/apps/relay/App.d.ts.map +1 -1
  130. package/dist/apps/relay/App.js +115 -23
  131. package/dist/apps/relay/App.js.map +1 -1
  132. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  133. package/dist/apps/relay/Sponsorships.js +27 -1
  134. package/dist/apps/relay/Sponsorships.js.map +1 -1
  135. package/dist/db/Db.d.ts.map +1 -1
  136. package/dist/db/Db.js +34 -1
  137. package/dist/db/Db.js.map +1 -1
  138. package/dist/db/Schema.d.ts +11 -5
  139. package/dist/db/Schema.d.ts.map +1 -1
  140. package/dist/db/Schema.js +21 -3
  141. package/dist/db/Schema.js.map +1 -1
  142. package/dist/db/tables/fundingCatalog.d.ts +4 -2
  143. package/dist/db/tables/fundingCatalog.d.ts.map +1 -1
  144. package/dist/db/tables/fundingCatalog.js +5 -1
  145. package/dist/db/tables/fundingCatalog.js.map +1 -1
  146. package/dist/db/tables/fundingTransferTransactions.d.ts +38 -0
  147. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -1
  148. package/dist/db/tables/fundingTransferTransactions.js +44 -3
  149. package/dist/db/tables/fundingTransferTransactions.js.map +1 -1
  150. package/dist/db/tables/fundingTransfers.d.ts +17 -0
  151. package/dist/db/tables/fundingTransfers.d.ts.map +1 -1
  152. package/dist/db/tables/fundingTransfers.js +21 -0
  153. package/dist/db/tables/fundingTransfers.js.map +1 -1
  154. package/dist/db/tables/organizations.d.ts +24 -0
  155. package/dist/db/tables/organizations.d.ts.map +1 -1
  156. package/dist/db/tables/organizations.js +27 -0
  157. package/dist/db/tables/organizations.js.map +1 -1
  158. package/dist/db/tables/projects.d.ts +25 -1
  159. package/dist/db/tables/projects.d.ts.map +1 -1
  160. package/dist/db/tables/projects.js +26 -0
  161. package/dist/db/tables/projects.js.map +1 -1
  162. package/dist/db/tables/sponsoredTransactions.d.ts +44 -0
  163. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  164. package/dist/db/tables/sponsoredTransactions.js +146 -13
  165. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  166. package/dist/db/tables/webhookSubscriptions.d.ts +10 -6
  167. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  168. package/dist/db/tables/webhookSubscriptions.js +9 -6
  169. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  170. package/dist/handlers/internal/utils.d.ts.map +1 -1
  171. package/dist/handlers/internal/utils.js +9 -0
  172. package/dist/handlers/internal/utils.js.map +1 -1
  173. package/dist/internal/Auth.d.ts +3 -1
  174. package/dist/internal/Auth.d.ts.map +1 -1
  175. package/dist/internal/Auth.js +6 -3
  176. package/dist/internal/Auth.js.map +1 -1
  177. package/dist/internal/EarnVaults.d.ts +3 -3
  178. package/dist/internal/EarnVaults.js +5 -5
  179. package/dist/internal/EarnVaults.js.map +1 -1
  180. package/dist/internal/Fetch.d.ts +23 -0
  181. package/dist/internal/Fetch.d.ts.map +1 -0
  182. package/dist/internal/Fetch.js +26 -0
  183. package/dist/internal/Fetch.js.map +1 -0
  184. package/dist/internal/Log.d.ts +4 -2
  185. package/dist/internal/Log.d.ts.map +1 -1
  186. package/dist/internal/Log.js +16 -3
  187. package/dist/internal/Log.js.map +1 -1
  188. package/dist/internal/OpenApi.d.ts +5 -3
  189. package/dist/internal/OpenApi.d.ts.map +1 -1
  190. package/dist/internal/OpenApi.js +13 -3
  191. package/dist/internal/OpenApi.js.map +1 -1
  192. package/dist/internal/Response.d.ts +1 -1
  193. package/dist/internal/Response.d.ts.map +1 -1
  194. package/dist/internal/Schema.d.ts +6 -3
  195. package/dist/internal/Schema.d.ts.map +1 -1
  196. package/dist/internal/Schema.js +8 -2
  197. package/dist/internal/Schema.js.map +1 -1
  198. package/dist/internal/Store.d.ts +16 -3
  199. package/dist/internal/Store.d.ts.map +1 -1
  200. package/dist/internal/Store.js +82 -16
  201. package/dist/internal/Store.js.map +1 -1
  202. package/dist/internal/Tidx.d.ts.map +1 -1
  203. package/dist/internal/Tidx.js +7 -2
  204. package/dist/internal/Tidx.js.map +1 -1
  205. package/dist/internal/Timeout.d.ts +36 -0
  206. package/dist/internal/Timeout.d.ts.map +1 -0
  207. package/dist/internal/Timeout.js +37 -0
  208. package/dist/internal/Timeout.js.map +1 -0
  209. package/dist/internal/Timing.d.ts +9 -0
  210. package/dist/internal/Timing.d.ts.map +1 -1
  211. package/dist/internal/Timing.js +7 -0
  212. package/dist/internal/Timing.js.map +1 -1
  213. package/dist/internal/Viem.d.ts +16 -1
  214. package/dist/internal/Viem.d.ts.map +1 -1
  215. package/dist/internal/Viem.js +12 -2
  216. package/dist/internal/Viem.js.map +1 -1
  217. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  218. package/dist/internal/WebhookDestination.js +3 -0
  219. package/dist/internal/WebhookDestination.js.map +1 -1
  220. package/dist/internal/funding/Action.d.ts +9 -0
  221. package/dist/internal/funding/Action.d.ts.map +1 -1
  222. package/dist/internal/funding/Action.js +16 -8
  223. package/dist/internal/funding/Action.js.map +1 -1
  224. package/dist/internal/funding/Catalog.d.ts +9 -2
  225. package/dist/internal/funding/Catalog.d.ts.map +1 -1
  226. package/dist/internal/funding/Catalog.js +19 -8
  227. package/dist/internal/funding/Catalog.js.map +1 -1
  228. package/dist/internal/funding/DepositWebhook.d.ts.map +1 -1
  229. package/dist/internal/funding/DepositWebhook.js +2 -1
  230. package/dist/internal/funding/DepositWebhook.js.map +1 -1
  231. package/dist/internal/funding/Evidence.d.ts.map +1 -1
  232. package/dist/internal/funding/Evidence.js +2 -10
  233. package/dist/internal/funding/Evidence.js.map +1 -1
  234. package/dist/internal/funding/Provider.d.ts +108 -13
  235. package/dist/internal/funding/Provider.d.ts.map +1 -1
  236. package/dist/internal/funding/Provider.js +34 -8
  237. package/dist/internal/funding/Provider.js.map +1 -1
  238. package/dist/internal/funding/SourceObservation.d.ts.map +1 -1
  239. package/dist/internal/funding/SourceObservation.js +5 -7
  240. package/dist/internal/funding/SourceObservation.js.map +1 -1
  241. package/dist/internal/funding/Transfer.d.ts +114 -18
  242. package/dist/internal/funding/Transfer.d.ts.map +1 -1
  243. package/dist/internal/funding/Transfer.js +185 -55
  244. package/dist/internal/funding/Transfer.js.map +1 -1
  245. package/dist/internal/funding/TransferReconciliation.d.ts +52 -0
  246. package/dist/internal/funding/TransferReconciliation.d.ts.map +1 -0
  247. package/dist/internal/funding/TransferReconciliation.js +192 -0
  248. package/dist/internal/funding/TransferReconciliation.js.map +1 -0
  249. package/dist/internal/funding/TransferWebhook.d.ts +21 -0
  250. package/dist/internal/funding/TransferWebhook.d.ts.map +1 -0
  251. package/dist/internal/funding/TransferWebhook.js +43 -0
  252. package/dist/internal/funding/TransferWebhook.js.map +1 -0
  253. package/dist/internal/funding/index.d.ts +1 -0
  254. package/dist/internal/funding/index.d.ts.map +1 -1
  255. package/dist/internal/funding/index.js +1 -0
  256. package/dist/internal/funding/index.js.map +1 -1
  257. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  258. package/dist/internal/funding/providers/relay.js +3 -12
  259. package/dist/internal/funding/providers/relay.js.map +1 -1
  260. package/dist/internal/funding/providers/stargate.d.ts +127 -9
  261. package/dist/internal/funding/providers/stargate.d.ts.map +1 -1
  262. package/dist/internal/funding/providers/stargate.js +548 -112
  263. package/dist/internal/funding/providers/stargate.js.map +1 -1
  264. package/dist/internal/index.d.ts +1 -0
  265. package/dist/internal/index.d.ts.map +1 -1
  266. package/dist/internal/index.js +1 -0
  267. package/dist/internal/index.js.map +1 -1
  268. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
  269. package/dist/internal/webhookDestinations/slack.js +16 -0
  270. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  271. package/package.json +2 -2
  272. package/src/App.test.ts +100 -0
  273. package/src/App.ts +43 -3
  274. package/src/Client.test-d.ts +129 -2
  275. package/src/Scope.test.ts +20 -0
  276. package/src/Scope.ts +16 -0
  277. package/src/Webhooks.ts +2 -2
  278. package/src/admin/apps/api-keys.test.ts +75 -0
  279. package/src/admin/apps/api-keys.ts +64 -0
  280. package/src/admin/apps/earn-vaults.test.ts +1 -1
  281. package/src/admin/apps/earn-vaults.ts +2 -1
  282. package/src/admin/apps/organizations.test.ts +53 -1
  283. package/src/admin/apps/organizations.ts +90 -5
  284. package/src/admin/ui/src/lib/api-keys.ts +19 -0
  285. package/src/admin/ui/src/lib/organizations.test.ts +112 -1
  286. package/src/admin/ui/src/lib/organizations.ts +55 -0
  287. package/src/admin/ui/src/routes/api-keys.tsx +89 -2
  288. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +116 -0
  289. package/src/admin/ui.generated.ts +189 -151
  290. package/src/apps/Zones.test.ts +7 -7
  291. package/src/apps/Zones.ts +7 -7
  292. package/src/apps/composition.test.ts +2 -1
  293. package/src/apps/data/App.test.ts +41 -0
  294. package/src/apps/data/App.ts +30 -21
  295. package/src/apps/data/ZoneSelection.test.ts +2 -2
  296. package/src/apps/data/ZoneSelection.ts +8 -8
  297. package/src/apps/data/routes/activities.test.ts +1526 -12
  298. package/src/apps/data/routes/activities.ts +1774 -1356
  299. package/src/apps/data/routes/balances.ts +133 -97
  300. package/src/apps/data/routes/blocks.test.ts +533 -0
  301. package/src/apps/data/routes/blocks.ts +35 -16
  302. package/src/apps/data/routes/earn.test.ts +373 -16
  303. package/src/apps/data/routes/earn.ts +548 -79
  304. package/src/apps/data/routes/earn.zone-access.test.ts +6 -6
  305. package/src/apps/data/routes/exchanges.test.ts +740 -15
  306. package/src/apps/data/routes/exchanges.ts +954 -753
  307. package/src/apps/data/routes/fee-amm.ts +17 -18
  308. package/src/apps/data/routes/receipts.ts +47 -16
  309. package/src/apps/data/routes/tokens.test.ts +578 -0
  310. package/src/apps/data/routes/tokens.ts +283 -225
  311. package/src/apps/data/routes/transactions.ts +68 -29
  312. package/src/apps/data/routes/transfers.test.ts +421 -0
  313. package/src/apps/data/routes/transfers.ts +45 -27
  314. package/src/apps/data/routes/valuation.ts +94 -78
  315. package/src/apps/data/routes/webhooks.test.ts +21 -5
  316. package/src/apps/data/routes/webhooks.ts +63 -8
  317. package/src/apps/data/routes/zones.test.ts +2 -2
  318. package/src/apps/data/routes/zones.ts +1 -1
  319. package/src/apps/funding/App.ts +12 -2
  320. package/src/apps/funding/routes/deposit-addresses.test.ts +29 -7
  321. package/src/apps/funding/routes/deposit-addresses.ts +19 -14
  322. package/src/apps/funding/routes/quotes.test.ts +5 -9
  323. package/src/apps/funding/routes/transfers.test.ts +901 -23
  324. package/src/apps/funding/routes/transfers.ts +347 -64
  325. package/src/apps/management/Billing.ts +18 -5
  326. package/src/apps/management/routes/api-keys.test.ts +220 -0
  327. package/src/apps/management/routes/api-keys.ts +280 -3
  328. package/src/apps/management/routes/webhooks.test.ts +3 -3
  329. package/src/apps/management/routes/webhooks.ts +24 -25
  330. package/src/apps/relay/App.test.ts +212 -1
  331. package/src/apps/relay/App.ts +158 -29
  332. package/src/apps/relay/Sponsorships.ts +24 -1
  333. package/src/db/Db.ts +48 -1
  334. package/src/db/Schema.ts +22 -4
  335. package/src/db/tables/earnVaults.test.ts +9 -9
  336. package/src/db/tables/fundingCatalog.test.ts +16 -2
  337. package/src/db/tables/fundingCatalog.ts +9 -3
  338. package/src/db/tables/fundingTransferTransactions.test.ts +43 -0
  339. package/src/db/tables/fundingTransferTransactions.ts +79 -3
  340. package/src/db/tables/fundingTransfers.test.ts +26 -0
  341. package/src/db/tables/fundingTransfers.ts +36 -0
  342. package/src/db/tables/organizations.test.ts +41 -0
  343. package/src/db/tables/organizations.ts +49 -0
  344. package/src/db/tables/projects.test.ts +55 -0
  345. package/src/db/tables/projects.ts +46 -1
  346. package/src/db/tables/sponsoredTransactions.test.ts +338 -0
  347. package/src/db/tables/sponsoredTransactions.ts +197 -13
  348. package/src/db/tables/webhookSubscriptions.ts +18 -11
  349. package/src/handlers/internal/utils.test.ts +53 -0
  350. package/src/handlers/internal/utils.ts +15 -0
  351. package/src/internal/Auth.ts +12 -4
  352. package/src/internal/EarnVaults.capabilities.test.ts +2 -2
  353. package/src/internal/EarnVaults.test.ts +1 -1
  354. package/src/internal/EarnVaults.ts +5 -5
  355. package/src/internal/Fetch.test.ts +46 -0
  356. package/src/internal/Fetch.ts +56 -0
  357. package/src/internal/Log.test-d.ts +2 -0
  358. package/src/internal/Log.test.ts +21 -0
  359. package/src/internal/Log.ts +22 -5
  360. package/src/internal/OpenApi.ts +14 -4
  361. package/src/internal/Response.ts +1 -1
  362. package/src/internal/Schema.test.ts +2 -0
  363. package/src/internal/Schema.ts +12 -2
  364. package/src/internal/Store.test.ts +178 -0
  365. package/src/internal/Store.ts +104 -18
  366. package/src/internal/Tidx.test.ts +2 -1
  367. package/src/internal/Tidx.ts +7 -2
  368. package/src/internal/Timeout.test.ts +35 -0
  369. package/src/internal/Timeout.ts +73 -0
  370. package/src/internal/Timing.ts +16 -0
  371. package/src/internal/Viem.ts +39 -3
  372. package/src/internal/WebhookDestination.ts +3 -0
  373. package/src/internal/Webhooks.test.ts +45 -0
  374. package/src/internal/funding/Action.test.ts +41 -1
  375. package/src/internal/funding/Action.ts +69 -41
  376. package/src/internal/funding/Catalog.test.ts +34 -0
  377. package/src/internal/funding/Catalog.ts +28 -11
  378. package/src/internal/funding/DepositWebhook.ts +2 -1
  379. package/src/internal/funding/Evidence.ts +2 -13
  380. package/src/internal/funding/Provider.test.ts +73 -0
  381. package/src/internal/funding/Provider.ts +125 -28
  382. package/src/internal/funding/SourceObservation.ts +5 -9
  383. package/src/internal/funding/Transfer.test.ts +155 -0
  384. package/src/internal/funding/Transfer.ts +387 -147
  385. package/src/internal/funding/TransferReconciliation.test.ts +147 -0
  386. package/src/internal/funding/TransferReconciliation.ts +264 -0
  387. package/src/internal/funding/TransferWebhook.test.ts +128 -0
  388. package/src/internal/funding/TransferWebhook.ts +67 -0
  389. package/src/internal/funding/index.test.ts +1 -0
  390. package/src/internal/funding/index.ts +1 -0
  391. package/src/internal/funding/providers/index.test.ts +4 -167
  392. package/src/internal/funding/providers/relay.test.ts +2 -2
  393. package/src/internal/funding/providers/relay.ts +3 -15
  394. package/src/internal/funding/providers/stargate.test.ts +328 -0
  395. package/src/internal/funding/providers/stargate.ts +807 -135
  396. package/src/internal/index.test.ts +1 -0
  397. package/src/internal/index.ts +1 -0
  398. package/src/internal/webhookDestinations/slack.ts +15 -0
@@ -2,11 +2,15 @@ import * as z from 'zod/mini'
2
2
 
3
3
  import type * as Db from '../../db/Db.js'
4
4
  import * as FundingTransferEvents from '../../db/tables/fundingTransferEvents.js'
5
+ import * as FundingTransferTransactions from '../../db/tables/fundingTransferTransactions.js'
5
6
  import * as FundingTransfers from '../../db/tables/fundingTransfers.js'
6
7
  import * as Id from '../Id.js'
8
+ import * as OpenApi from '../OpenApi.js'
7
9
  import * as Schema from '../Schema.js'
10
+ import type * as Webhooks from '../Webhooks.js'
8
11
  import * as Action from './Action.js'
9
12
  import * as Provider from './Provider.js'
13
+ import * as TransferWebhook from './TransferWebhook.js'
10
14
 
11
15
  /** Public funding transfer statuses, in the order documented by the API. */
12
16
  export const statuses = [
@@ -60,7 +64,7 @@ const transitions: Record<Status, readonly Status[]> = {
60
64
  'awaiting-source': ['action-required', 'expired', 'processing'],
61
65
  completed: [],
62
66
  expired: [],
63
- processing: ['action-required', 'completed', 'refunding'],
67
+ processing: ['action-required', 'awaiting-source', 'completed', 'refunding'],
64
68
  refunded: [],
65
69
  refunding: ['action-required', 'refunded'],
66
70
  }
@@ -91,22 +95,25 @@ export namespace schema {
91
95
  )
92
96
 
93
97
  /** Customer-safe explanation of a status that needs context. */
94
- export const StatusReason = z
95
- .object({
96
- code: z
97
- .enum(statusReasonCodes)
98
- .check(
99
- z.describe('Stable machine-readable reason code.'),
100
- z.meta({ examples: ['destination_amount_below_minimum'] }),
98
+ export const StatusReason = OpenApi.component(
99
+ z
100
+ .object({
101
+ code: z
102
+ .enum(statusReasonCodes)
103
+ .check(
104
+ z.describe('Stable machine-readable reason code.'),
105
+ z.meta({ examples: ['destination_amount_below_minimum'] }),
106
+ ),
107
+ message: z.string().check(
108
+ z.describe('Human-readable explanation of the reason.'),
109
+ z.meta({
110
+ examples: ['The verified destination amount did not satisfy the quoted minimum.'],
111
+ }),
101
112
  ),
102
- message: z.string().check(
103
- z.describe('Human-readable explanation of the reason.'),
104
- z.meta({
105
- examples: ['The verified destination amount did not satisfy the quoted minimum.'],
106
- }),
107
- ),
108
- })
109
- .check(z.describe('Why the transfer is in its current status, when context is needed.'))
113
+ })
114
+ .check(z.describe('Why the transfer is in its current status, when context is needed.')),
115
+ 'FundingTransferStatusReason',
116
+ )
110
117
 
111
118
  /** Funding method selected at creation. */
112
119
  export const Method = z
@@ -122,39 +129,45 @@ export namespace schema {
122
129
  )
123
130
 
124
131
  /** One fee applied to the transfer. */
125
- export const Fee = z
126
- .object({
127
- amount: Schema.TokenAmount,
128
- side: z
129
- .enum(['destination', 'source'])
130
- .check(
131
- z.describe('Which side of the transfer the fee is taken from.'),
132
- z.meta({ examples: ['destination'] }),
133
- ),
134
- token: Provider.schema.TokenRef,
135
- type: z
136
- .enum(['provider'])
137
- .check(z.describe('What the fee pays for.'), z.meta({ examples: ['provider'] })),
138
- })
139
- .check(z.describe('One fee applied to the transfer.'))
132
+ export const Fee = OpenApi.component(
133
+ z
134
+ .object({
135
+ amount: Schema.TokenAmount,
136
+ side: z
137
+ .enum(['destination', 'source'])
138
+ .check(
139
+ z.describe('Which side of the transfer the fee is taken from.'),
140
+ z.meta({ examples: ['destination'] }),
141
+ ),
142
+ token: Provider.schema.TokenRef,
143
+ type: z
144
+ .enum(['provider'])
145
+ .check(z.describe('What the fee pays for.'), z.meta({ examples: ['provider'] })),
146
+ })
147
+ .check(z.describe('One fee applied to the transfer.')),
148
+ 'FundingTransferFee',
149
+ )
140
150
 
141
151
  /** Freshness and validity window of the selected quote. */
142
- export const Quote = z
143
- .object({
144
- expiresAt: z
145
- .string()
146
- .check(
147
- z.describe('When the quoted terms stop being executable (ISO 8601).'),
148
- z.meta({ examples: ['2026-07-30T04:01:00.000Z'] }),
149
- ),
150
- sampledAt: z
151
- .string()
152
- .check(
153
- z.describe('When the provider produced the quote (ISO 8601).'),
154
- z.meta({ examples: ['2026-07-30T04:00:00.000Z'] }),
155
- ),
156
- })
157
- .check(z.describe('Freshness and validity window of the selected quote.'))
152
+ export const Quote = OpenApi.component(
153
+ z
154
+ .object({
155
+ expiresAt: z.iso
156
+ .datetime()
157
+ .check(
158
+ z.describe('When the quoted terms stop being executable (ISO 8601).'),
159
+ z.meta({ examples: ['2026-07-30T04:01:00.000Z'] }),
160
+ ),
161
+ sampledAt: z.iso
162
+ .datetime()
163
+ .check(
164
+ z.describe('When the provider produced the quote (ISO 8601).'),
165
+ z.meta({ examples: ['2026-07-30T04:00:00.000Z'] }),
166
+ ),
167
+ })
168
+ .check(z.describe('Freshness and validity window of the selected quote.')),
169
+ 'FundingTransferQuote',
170
+ )
158
171
 
159
172
  const hashesCheck = (description: string) => [
160
173
  z.describe(description),
@@ -219,8 +232,8 @@ export namespace schema {
219
232
  .check(z.describe('Quote terms and reconciled public fields of a funding transfer.'))
220
233
 
221
234
  const fundingTransferFields = {
222
- createdAt: z
223
- .string()
235
+ createdAt: z.iso
236
+ .datetime()
224
237
  .check(
225
238
  z.describe('When the transfer was created (ISO 8601).'),
226
239
  z.meta({ examples: ['2026-07-30T04:00:00.000Z'] }),
@@ -253,8 +266,8 @@ export namespace schema {
253
266
  sourceTransactionHashes: snapshotFields.sourceTransactionHashes,
254
267
  status: Status,
255
268
  statusReason: z.optional(StatusReason),
256
- updatedAt: z
257
- .string()
269
+ updatedAt: z.iso
270
+ .datetime()
258
271
  .check(
259
272
  z.describe('When the transfer last materially changed (ISO 8601).'),
260
273
  z.meta({ examples: ['2026-07-30T04:00:20.000Z'] }),
@@ -270,73 +283,86 @@ export namespace schema {
270
283
  }
271
284
 
272
285
  /** A funding transfer as returned by reads. Reads never include `action`. */
273
- export const FundingTransfer = z
274
- .object(fundingTransferFields)
275
- .check(z.describe('One durable funding transfer.'))
286
+ export const FundingTransfer = OpenApi.component(
287
+ z.object(fundingTransferFields).check(z.describe('One durable funding transfer.')),
288
+ 'FundingTransfer',
289
+ )
276
290
 
277
291
  /** A created funding transfer: the read shape plus the executable action. */
278
- export const CreatedFundingTransfer = z
279
- .object({ action: Action.schema.Action, ...fundingTransferFields })
280
- .check(z.describe('The created funding transfer, including its one-time executable action.'))
292
+ export const CreatedFundingTransfer = OpenApi.component(
293
+ z
294
+ .object({ action: Action.schema.Action, ...fundingTransferFields })
295
+ .check(z.describe('The created funding transfer, including its one-time executable action.')),
296
+ 'CreatedFundingTransfer',
297
+ )
281
298
 
282
299
  /** Request body accepted by funding transfer creation. */
283
- export const CreateRequest = z
284
- .strictObject({
285
- amount: z
286
- .string()
287
- .check(
288
- z.regex(/^[1-9]\d*$/),
289
- z.describe('Amount in base units of the token the mode fixes.'),
290
- z.meta({ examples: ['1000000'] }),
291
- ),
292
- destinationToken: z
293
- .string()
294
- .check(
295
- z.minLength(1),
296
- z.describe('Tempo destination token symbol, contract address, or token key.'),
297
- z.meta({ examples: ['pathusd'] }),
298
- ),
299
- mode: Mode,
300
- recipient: z
301
- .string()
302
- .check(
303
- z.regex(/^0x[0-9a-fA-F]{40}$/),
304
- z.describe('Final beneficiary of the transfer.'),
305
- z.meta({ examples: [`0x${'11'.repeat(20)}`] }),
306
- ),
307
- sender: z
308
- .string()
309
- .check(
310
- z.regex(/^0x[0-9a-fA-F]{40}$/),
311
- z.describe('Source-chain account that signs the funding action.'),
312
- z.meta({ examples: [`0x${'22'.repeat(20)}`] }),
313
- ),
314
- slippageBps: z.optional(
315
- z
316
- .number()
300
+ export const CreateRequest = OpenApi.component(
301
+ z
302
+ .strictObject({
303
+ amount: z
304
+ .string()
317
305
  .check(
318
- z.int(),
319
- z.gte(0),
320
- z.lte(10_000),
321
- z.describe('Maximum acceptable slippage in basis points.'),
322
- z.meta({ examples: [50] }),
306
+ z.regex(/^[1-9]\d*$/),
307
+ z.describe('Amount in base units of the token the mode fixes.'),
308
+ z.meta({ examples: ['1000000'] }),
323
309
  ),
324
- ),
325
- sourceChain: z
326
- .string()
327
- .check(
328
- z.describe('Source chain CAIP-2 id, slug, or alias.'),
329
- z.meta({ examples: ['base', 'eip155:8453'] }),
330
- ),
331
- sourceToken: z
332
- .string()
333
- .check(
334
- z.minLength(1),
335
- z.describe('Source token symbol, contract address, or token key.'),
336
- z.meta({ examples: ['usdc'] }),
310
+ destinationToken: z
311
+ .string()
312
+ .check(
313
+ z.minLength(1),
314
+ z.describe('Tempo destination token symbol, contract address, or token key.'),
315
+ z.meta({ examples: ['pathusd'] }),
316
+ ),
317
+ mode: Mode,
318
+ provider: z
319
+ .optional(z.string().check(z.minLength(1)))
320
+ .check(
321
+ z.describe('Funding provider ID. Omit to select the best available provider.'),
322
+ z.meta({ examples: ['relay'] }),
323
+ ),
324
+ recipient: z
325
+ .string()
326
+ .check(
327
+ z.regex(/^0x[0-9a-fA-F]{40}$/),
328
+ z.describe('Final beneficiary of the transfer.'),
329
+ z.meta({ examples: [`0x${'11'.repeat(20)}`] }),
330
+ ),
331
+ sender: z
332
+ .string()
333
+ .check(
334
+ z.regex(/^0x[0-9a-fA-F]{40}$/),
335
+ z.describe('Source-chain account that signs the funding action.'),
336
+ z.meta({ examples: [`0x${'22'.repeat(20)}`] }),
337
+ ),
338
+ slippageBps: z.optional(
339
+ z
340
+ .number()
341
+ .check(
342
+ z.int(),
343
+ z.gte(0),
344
+ z.lte(10_000),
345
+ z.describe('Maximum acceptable slippage in basis points.'),
346
+ z.meta({ examples: [50] }),
347
+ ),
337
348
  ),
338
- })
339
- .check(z.describe('Funding transfer creation request.'))
349
+ sourceChain: z
350
+ .string()
351
+ .check(
352
+ z.describe('Source chain CAIP-2 id, slug, or alias.'),
353
+ z.meta({ examples: ['base', 'eip155:8453'] }),
354
+ ),
355
+ sourceToken: z
356
+ .string()
357
+ .check(
358
+ z.minLength(1),
359
+ z.describe('Source token symbol, contract address, or token key.'),
360
+ z.meta({ examples: ['usdc'] }),
361
+ ),
362
+ })
363
+ .check(z.describe('Funding transfer creation request.')),
364
+ 'CreateFundingTransferRequest',
365
+ )
340
366
  }
341
367
 
342
368
  /** Stored public snapshot of a funding transfer (see {@link schema.Snapshot}). */
@@ -360,12 +386,22 @@ export const transactionRoles = ['destination', 'refund', 'source'] as const
360
386
  /** Role of a verified transaction reference on a transfer. */
361
387
  export type TransactionRole = (typeof transactionRoles)[number]
362
388
 
389
+ /** Bounded private provider state retained for transfer reconciliation. */
390
+ export type ProviderState = Record<string, unknown>
391
+
392
+ type Staged = {
393
+ /** Funding transfer record committed with the webhook obligations. */
394
+ record: FundingTransfers.Record
395
+ /** Webhook queue references committed with the transfer. */
396
+ references: Webhooks.QueueReference[]
397
+ }
398
+
363
399
  /**
364
400
  * Creates a durable funding transfer: the row plus its version-1 event commit
365
401
  * in one transaction, so every stored version has an immutable public record.
366
402
  */
367
403
  export async function create(db: Db.Db, input: create.Input): Promise<FundingTransfers.Record> {
368
- return db.transaction((tx) => createIn(tx, input))
404
+ return db.transaction(async (tx) => (await createIn(tx, input)).record)
369
405
  }
370
406
 
371
407
  /**
@@ -373,7 +409,7 @@ export async function create(db: Db.Db, input: create.Input): Promise<FundingTra
373
409
  * transaction, for writes that must commit atomically with sibling state
374
410
  * (idempotency completion).
375
411
  */
376
- export async function createIn(tx: Db.Db, input: create.Input): Promise<FundingTransfers.Record> {
412
+ export async function createIn(tx: Db.Db, input: create.Input): Promise<createIn.Result> {
377
413
  const now = new Date()
378
414
  const snapshot = parseSnapshot(input.snapshot)
379
415
  const record: FundingTransfers.Record = {
@@ -395,14 +431,22 @@ export async function createIn(tx: Db.Db, input: create.Input): Promise<FundingT
395
431
  version: 1,
396
432
  }
397
433
  const created = await FundingTransfers.insert(tx, record)
398
- await FundingTransferEvents.insert(tx, {
434
+ const event = await FundingTransferEvents.insert(tx, {
399
435
  createdAt: record.createdAt,
400
436
  snapshot: toPublic(created),
401
437
  status: created.status,
402
438
  transferId: created.id,
403
439
  version: created.version,
404
440
  })
405
- return created
441
+ const { references } = await TransferWebhook.stage(tx, {
442
+ changes: [{ event, record: created }],
443
+ })
444
+ return { record: created, references }
445
+ }
446
+
447
+ export declare namespace createIn {
448
+ /** Created transfer and webhook queue references committed with it. */
449
+ type Result = Staged
406
450
  }
407
451
 
408
452
  export declare namespace create {
@@ -419,12 +463,193 @@ export declare namespace create {
419
463
  /** Attributed project id, when the creating key is project-attributed. */
420
464
  projectId?: string | undefined
421
465
  /** Private provider correlation retained for reconciliation. */
422
- providerState?: Provider.prepareTransfer.Correlation | undefined
466
+ providerState?: ProviderState | undefined
423
467
  /** The public snapshot captured from the selected quote. */
424
468
  snapshot: Snapshot
425
469
  }
426
470
  }
427
471
 
472
+ /** Atomically assigns one verified source transaction and starts reconciliation. */
473
+ export async function registerSourceTransaction(
474
+ db: Db.Db,
475
+ options: registerSourceTransaction.Options,
476
+ ): Promise<registerSourceTransaction.Result> {
477
+ return db.transaction(async (tx) => {
478
+ const current = await FundingTransfers.getForUpdate(tx, options.id)
479
+ if (!current) return { type: 'not_found' }
480
+
481
+ const existing = await FundingTransferTransactions.getSource(tx, {
482
+ chainId: options.chainId,
483
+ transactionRef: options.transactionHash,
484
+ })
485
+ if (existing) {
486
+ if (existing.transferId !== current.id) return { current, type: 'conflict' }
487
+ if (current.status !== 'processing' && current.status !== 'completed')
488
+ return { current, type: 'invalid' }
489
+ return { record: current, type: 'replayed' }
490
+ }
491
+ if (current.status !== 'awaiting-source') return { current, type: 'invalid' }
492
+
493
+ const claimed = await FundingTransferTransactions.claimSource(tx, {
494
+ chainId: options.chainId,
495
+ createdAt: new Date().toISOString(),
496
+ role: 'source',
497
+ transactionRef: options.transactionHash,
498
+ transferId: options.id,
499
+ })
500
+ if (claimed.type === 'conflict') return { current, type: 'conflict' }
501
+ if (claimed.type === 'replayed') return { record: current, type: 'replayed' }
502
+
503
+ const result = await transitionIn(tx, {
504
+ current,
505
+ expectedVersion: current.version,
506
+ id: current.id,
507
+ providerState: { ...current.providerState, ...options.providerState },
508
+ snapshot: {
509
+ ...current.snapshot,
510
+ sourceTransactionHashes: [claimed.record.transactionRef],
511
+ },
512
+ status: 'processing',
513
+ })
514
+ if (result.type !== 'applied') throw new AtomicTransitionError()
515
+ return { record: result.record, references: result.references, type: 'registered' }
516
+ })
517
+ }
518
+
519
+ export declare namespace registerSourceTransaction {
520
+ /** Verified source evidence and private provider correlation. */
521
+ type Options = {
522
+ /** Source chain CAIP-2 id. */
523
+ chainId: string
524
+ /** Transfer id (`ftr_…`). */
525
+ id: string
526
+ /** Private state added for destination reconciliation. */
527
+ providerState: ProviderState
528
+ /** Verified source transaction hash. */
529
+ transactionHash: string
530
+ }
531
+
532
+ /** Source transaction registration outcome. */
533
+ type Result =
534
+ | { current: FundingTransfers.Record; type: 'conflict' }
535
+ | { current: FundingTransfers.Record; type: 'invalid' }
536
+ | (Staged & { type: 'registered' })
537
+ | { record: FundingTransfers.Record; type: 'replayed' }
538
+ | { type: 'not_found' }
539
+ }
540
+
541
+ /** Atomically removes invalidated source evidence and accepts a replacement. */
542
+ export async function reopenSourceTransaction(
543
+ db: Db.Db,
544
+ options: reopenSourceTransaction.Options,
545
+ ): Promise<reopenSourceTransaction.Result> {
546
+ return db.transaction(async (tx) => {
547
+ const current = await FundingTransfers.getForUpdate(tx, options.id)
548
+ if (!current) return { type: 'not_found' }
549
+ if (current.status !== 'processing') return { current, type: 'invalid' }
550
+
551
+ const removed = await FundingTransferTransactions.removeSource(tx, {
552
+ chainId: options.chainId,
553
+ transactionHash: options.transactionHash,
554
+ transferId: current.id,
555
+ })
556
+ if (!removed) return { current, type: 'invalid' }
557
+ const { sourceTransactionHashes: _sourceTransactionHashes, ...snapshot } = current.snapshot
558
+ const result = await transitionIn(tx, {
559
+ current,
560
+ expectedVersion: current.version,
561
+ id: current.id,
562
+ providerState: options.providerState,
563
+ snapshot,
564
+ status: 'awaiting-source',
565
+ })
566
+ if (result.type !== 'applied') throw new AtomicTransitionError()
567
+ return { record: result.record, references: result.references, type: 'reopened' }
568
+ })
569
+ }
570
+
571
+ export declare namespace reopenSourceTransaction {
572
+ /** Source evidence to remove after it disappears from the canonical chain. */
573
+ type Options = {
574
+ /** Source chain CAIP-2 id. */
575
+ chainId: string
576
+ /** Transfer id (`ftr_…`). */
577
+ id: string
578
+ /** Private provider state retained while awaiting replacement evidence. */
579
+ providerState: ProviderState
580
+ /** Invalidated source transaction hash. */
581
+ transactionHash: string
582
+ }
583
+
584
+ /** Source transaction reopening outcome. */
585
+ type Result =
586
+ | { current: FundingTransfers.Record; type: 'invalid' }
587
+ | (Staged & { type: 'reopened' })
588
+ | { type: 'not_found' }
589
+ }
590
+
591
+ /** Atomically records a verified destination transaction and completes a transfer. */
592
+ export async function complete(db: Db.Db, options: complete.Options): Promise<complete.Result> {
593
+ return db.transaction(async (tx) => {
594
+ const current = await FundingTransfers.getForUpdate(tx, options.id)
595
+ if (!current) return { type: 'not_found' }
596
+ if (current.status === 'completed') {
597
+ const transactions = await FundingTransferTransactions.listByTransfer(tx, current.id)
598
+ const existing = transactions.some(
599
+ (transaction) =>
600
+ transaction.chainId === options.chainId &&
601
+ transaction.role === 'destination' &&
602
+ transaction.transactionRef === options.transactionHash.toLowerCase(),
603
+ )
604
+ return existing ? { record: current, type: 'replayed' } : { current, type: 'invalid' }
605
+ }
606
+ if (current.status !== 'processing') return { current, type: 'invalid' }
607
+
608
+ const transaction = await FundingTransferTransactions.insert(tx, {
609
+ chainId: options.chainId,
610
+ createdAt: new Date().toISOString(),
611
+ role: 'destination',
612
+ transactionRef: options.transactionHash,
613
+ transferId: options.id,
614
+ })
615
+ if (!transaction) throw new AtomicTransitionError()
616
+ const result = await transitionIn(tx, {
617
+ current,
618
+ expectedVersion: current.version,
619
+ id: current.id,
620
+ snapshot: {
621
+ ...current.snapshot,
622
+ destinationAmount: options.destinationAmount,
623
+ destinationTransactionHashes: [transaction.transactionRef],
624
+ },
625
+ status: 'completed',
626
+ })
627
+ if (result.type !== 'applied') throw new AtomicTransitionError()
628
+ return { record: result.record, references: result.references, type: 'completed' }
629
+ })
630
+ }
631
+
632
+ export declare namespace complete {
633
+ /** Verified destination evidence for one processing transfer. */
634
+ type Options = {
635
+ /** Destination chain CAIP-2 id. */
636
+ chainId: string
637
+ /** Exact verified destination amount. */
638
+ destinationAmount: NonNullable<Snapshot['destinationAmount']>
639
+ /** Transfer id (`ftr_…`). */
640
+ id: string
641
+ /** Verified destination transaction hash. */
642
+ transactionHash: string
643
+ }
644
+
645
+ /** Transfer completion outcome. */
646
+ type Result =
647
+ | { current: FundingTransfers.Record; type: 'invalid' }
648
+ | (Staged & { type: 'completed' })
649
+ | { record: FundingTransfers.Record; type: 'replayed' }
650
+ | { type: 'not_found' }
651
+ }
652
+
428
653
  /**
429
654
  * Applies a version-guarded material change: a status transition, a snapshot
430
655
  * update, or both. The row update and its immutable event commit in one
@@ -434,39 +659,48 @@ export declare namespace create {
434
659
  export async function transition(
435
660
  db: Db.Db,
436
661
  options: transition.Options,
662
+ ): Promise<transition.Result> {
663
+ return db.transaction((tx) => transitionIn(tx, options))
664
+ }
665
+
666
+ async function transitionIn(
667
+ tx: Db.Db,
668
+ options: transition.Options & { current?: FundingTransfers.Record | undefined },
437
669
  ): Promise<transition.Result> {
438
670
  const now = new Date()
439
- return db.transaction(async (tx) => {
440
- const current = await FundingTransfers.get(tx, options.id)
441
- if (!current) return { type: 'not_found' }
442
- if (current.version !== options.expectedVersion) return { current, type: 'stale' }
443
- const status = options.status ?? current.status
444
- if (status !== current.status && !canTransition(current.status, status))
445
- return { current, type: 'invalid' }
446
-
447
- const updated = await FundingTransfers.update(tx, {
448
- expectedVersion: options.expectedVersion,
449
- id: options.id,
450
- snapshot: options.snapshot === undefined ? current.snapshot : parseSnapshot(options.snapshot),
451
- status,
452
- statusReason:
453
- options.statusReason ?? (status === current.status ? current.statusReason : null),
454
- updatedAt: now.toISOString(),
455
- version: current.version + 1,
456
- })
457
- if (!updated) {
458
- const latest = await FundingTransfers.get(tx, options.id)
459
- return latest ? { current: latest, type: 'stale' } : { type: 'not_found' }
460
- }
461
- await FundingTransferEvents.insert(tx, {
462
- createdAt: updated.updatedAt,
463
- snapshot: toPublic(updated),
464
- status: updated.status,
465
- transferId: updated.id,
466
- version: updated.version,
467
- })
468
- return { record: updated, type: 'applied' }
671
+ const current = options.current ?? (await FundingTransfers.get(tx, options.id))
672
+ if (!current) return { type: 'not_found' }
673
+ if (current.version !== options.expectedVersion) return { current, type: 'stale' }
674
+ const status = options.status ?? current.status
675
+ if (status !== current.status && !canTransition(current.status, status))
676
+ return { current, type: 'invalid' }
677
+
678
+ const updated = await FundingTransfers.update(tx, {
679
+ expectedVersion: options.expectedVersion,
680
+ id: options.id,
681
+ providerState:
682
+ options.providerState === undefined ? current.providerState : options.providerState,
683
+ snapshot: options.snapshot === undefined ? current.snapshot : parseSnapshot(options.snapshot),
684
+ status,
685
+ statusReason: options.statusReason ?? (status === current.status ? current.statusReason : null),
686
+ updatedAt: now.toISOString(),
687
+ version: current.version + 1,
688
+ })
689
+ if (!updated) {
690
+ const latest = await FundingTransfers.get(tx, options.id)
691
+ return latest ? { current: latest, type: 'stale' } : { type: 'not_found' }
692
+ }
693
+ const event = await FundingTransferEvents.insert(tx, {
694
+ createdAt: updated.updatedAt,
695
+ snapshot: toPublic(updated),
696
+ status: updated.status,
697
+ transferId: updated.id,
698
+ version: updated.version,
699
+ })
700
+ const { references } = await TransferWebhook.stage(tx, {
701
+ changes: [{ event, record: updated }],
469
702
  })
703
+ return { record: updated, references, type: 'applied' }
470
704
  }
471
705
 
472
706
  export declare namespace transition {
@@ -476,6 +710,8 @@ export declare namespace transition {
476
710
  expectedVersion: number
477
711
  /** Transfer id (`ftr_…`). */
478
712
  id: string
713
+ /** Replacement private provider state; omitted keeps the stored value. */
714
+ providerState?: ProviderState | null | undefined
479
715
  /** Replacement public snapshot; omitted keeps the stored snapshot. */
480
716
  snapshot?: Snapshot | undefined
481
717
  /** New status; omitted keeps the current status (snapshot-only change). */
@@ -486,7 +722,7 @@ export declare namespace transition {
486
722
 
487
723
  /** Outcome of a guarded transition. */
488
724
  type Result =
489
- | { record: FundingTransfers.Record; type: 'applied' }
725
+ | (Staged & { type: 'applied' })
490
726
  | { current: FundingTransfers.Record; type: 'invalid' }
491
727
  | { current: FundingTransfers.Record; type: 'stale' }
492
728
  | { type: 'not_found' }
@@ -520,3 +756,7 @@ function parseSnapshot(snapshot: Snapshot): Snapshot {
520
756
  },
521
757
  }
522
758
  }
759
+
760
+ class AtomicTransitionError extends Error {
761
+ override name = 'Funding.Transfer.AtomicTransitionError'
762
+ }