tapimo 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (689) hide show
  1. package/dist/ApiKeys.d.ts +24 -7
  2. package/dist/ApiKeys.d.ts.map +1 -1
  3. package/dist/ApiKeys.js +17 -5
  4. package/dist/ApiKeys.js.map +1 -1
  5. package/dist/App.d.ts +12104 -7861
  6. package/dist/App.d.ts.map +1 -1
  7. package/dist/App.js +191 -128
  8. package/dist/App.js.map +1 -1
  9. package/dist/Assets.d.ts +10 -0
  10. package/dist/Assets.d.ts.map +1 -1
  11. package/dist/Assets.js +17 -4
  12. package/dist/Assets.js.map +1 -1
  13. package/dist/Scope.d.ts +19 -8
  14. package/dist/Scope.d.ts.map +1 -1
  15. package/dist/Scope.js +25 -9
  16. package/dist/Scope.js.map +1 -1
  17. package/dist/Webhooks.d.ts +1 -1
  18. package/dist/admin/App.d.ts +601 -20
  19. package/dist/admin/App.d.ts.map +1 -1
  20. package/dist/admin/App.js +12 -2
  21. package/dist/admin/App.js.map +1 -1
  22. package/dist/admin/apps/api-keys.d.ts +94 -11
  23. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.js +141 -32
  25. package/dist/admin/apps/api-keys.js.map +1 -1
  26. package/dist/admin/apps/earn-vaults.d.ts +1069 -0
  27. package/dist/admin/apps/earn-vaults.d.ts.map +1 -0
  28. package/dist/admin/apps/earn-vaults.js +409 -0
  29. package/dist/admin/apps/earn-vaults.js.map +1 -0
  30. package/dist/admin/apps/verified-tokens.d.ts +166 -166
  31. package/dist/admin/ui.generated.d.ts +1 -1
  32. package/dist/admin/ui.generated.d.ts.map +1 -1
  33. package/dist/admin/ui.generated.js +151 -95
  34. package/dist/admin/ui.generated.js.map +1 -1
  35. package/dist/analytics/Analytics.d.ts +2 -0
  36. package/dist/analytics/Analytics.d.ts.map +1 -1
  37. package/dist/analytics/Analytics.js +18 -2
  38. package/dist/analytics/Analytics.js.map +1 -1
  39. package/dist/analytics/tables/requestEvents.d.ts +22 -1
  40. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  41. package/dist/analytics/tables/requestEvents.js +59 -39
  42. package/dist/analytics/tables/requestEvents.js.map +1 -1
  43. package/dist/apps/Zones.d.ts +8335 -0
  44. package/dist/apps/Zones.d.ts.map +1 -0
  45. package/dist/apps/Zones.js +38 -0
  46. package/dist/apps/Zones.js.map +1 -0
  47. package/dist/apps/data/App.d.ts +14377 -10165
  48. package/dist/apps/data/App.d.ts.map +1 -1
  49. package/dist/apps/data/App.js +91 -19
  50. package/dist/apps/data/App.js.map +1 -1
  51. package/dist/apps/data/FxOracle.d.ts +1 -1
  52. package/dist/apps/data/FxOracle.d.ts.map +1 -1
  53. package/dist/apps/data/index.d.ts +0 -1
  54. package/dist/apps/data/index.d.ts.map +1 -1
  55. package/dist/apps/data/index.js +0 -1
  56. package/dist/apps/data/index.js.map +1 -1
  57. package/dist/apps/data/routes/activities.d.ts +32 -32
  58. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  59. package/dist/apps/data/routes/activities.js +130 -65
  60. package/dist/apps/data/routes/activities.js.map +1 -1
  61. package/dist/apps/data/routes/addresses.d.ts +312 -312
  62. package/dist/apps/data/routes/balances.d.ts +18 -18
  63. package/dist/apps/data/routes/balances.js +5 -5
  64. package/dist/apps/data/routes/balances.js.map +1 -1
  65. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  66. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  67. package/dist/apps/data/routes/coingecko.js +62 -35
  68. package/dist/apps/data/routes/coingecko.js.map +1 -1
  69. package/dist/apps/data/routes/earn.d.ts +2144 -0
  70. package/dist/apps/data/routes/earn.d.ts.map +1 -0
  71. package/dist/apps/data/routes/earn.js +947 -0
  72. package/dist/apps/data/routes/earn.js.map +1 -0
  73. package/dist/apps/data/routes/exchanges.d.ts +1231 -324
  74. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  75. package/dist/apps/data/routes/exchanges.js +656 -193
  76. package/dist/apps/data/routes/exchanges.js.map +1 -1
  77. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  78. package/dist/apps/data/routes/fee-amm.js +59 -8
  79. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  80. package/dist/apps/data/routes/funding.d.ts +4092 -387
  81. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  82. package/dist/apps/data/routes/funding.js +1403 -256
  83. package/dist/apps/data/routes/funding.js.map +1 -1
  84. package/dist/apps/data/routes/indexer.d.ts +5 -5
  85. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  86. package/dist/apps/data/routes/indexer.js +19 -2
  87. package/dist/apps/data/routes/indexer.js.map +1 -1
  88. package/dist/apps/data/routes/receipts.d.ts +265 -265
  89. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  90. package/dist/apps/data/routes/receipts.js +11 -11
  91. package/dist/apps/data/routes/receipts.js.map +1 -1
  92. package/dist/apps/data/routes/rpc.d.ts +5 -5
  93. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  94. package/dist/apps/data/routes/rpc.js +211 -22
  95. package/dist/apps/data/routes/rpc.js.map +1 -1
  96. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  97. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  98. package/dist/apps/data/routes/tokenlist.js +49 -9
  99. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  100. package/dist/apps/data/routes/tokens.d.ts +116 -88
  101. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  102. package/dist/apps/data/routes/tokens.js +198 -91
  103. package/dist/apps/data/routes/tokens.js.map +1 -1
  104. package/dist/apps/data/routes/transactions.d.ts +55 -55
  105. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  106. package/dist/apps/data/routes/transactions.js +78 -39
  107. package/dist/apps/data/routes/transactions.js.map +1 -1
  108. package/dist/apps/data/routes/transfers.d.ts +78 -29
  109. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  110. package/dist/apps/data/routes/transfers.js +165 -67
  111. package/dist/apps/data/routes/transfers.js.map +1 -1
  112. package/dist/apps/data/routes/valuation.d.ts +10 -10
  113. package/dist/apps/data/routes/valuation.js +2 -2
  114. package/dist/apps/data/routes/valuation.js.map +1 -1
  115. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  116. package/dist/apps/data/routes/verified-tokens.js +34 -9
  117. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  118. package/dist/apps/data/routes/webhooks.d.ts +5 -3
  119. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  120. package/dist/apps/data/routes/webhooks.js +175 -65
  121. package/dist/apps/data/routes/webhooks.js.map +1 -1
  122. package/dist/apps/data/routes/zones.d.ts +281 -0
  123. package/dist/apps/data/routes/zones.d.ts.map +1 -0
  124. package/dist/apps/data/routes/zones.js +252 -0
  125. package/dist/apps/data/routes/zones.js.map +1 -0
  126. package/dist/apps/docs/reference.js +6 -5
  127. package/dist/apps/docs/reference.js.map +1 -1
  128. package/dist/apps/index.d.ts +2 -1
  129. package/dist/apps/index.d.ts.map +1 -1
  130. package/dist/apps/index.js +2 -1
  131. package/dist/apps/index.js.map +1 -1
  132. package/dist/apps/management/App.d.ts +670 -670
  133. package/dist/apps/management/Billing.d.ts +8 -0
  134. package/dist/apps/management/Billing.d.ts.map +1 -1
  135. package/dist/apps/management/Billing.js +12 -0
  136. package/dist/apps/management/Billing.js.map +1 -1
  137. package/dist/apps/management/Error.d.ts +12 -0
  138. package/dist/apps/management/Error.d.ts.map +1 -0
  139. package/dist/apps/management/Error.js +10 -0
  140. package/dist/apps/management/Error.js.map +1 -0
  141. package/dist/apps/management/routes/api-keys.d.ts +5 -5
  142. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  143. package/dist/apps/management/routes/api-keys.js +2 -8
  144. package/dist/apps/management/routes/api-keys.js.map +1 -1
  145. package/dist/apps/management/routes/billing.d.ts +119 -119
  146. package/dist/apps/management/routes/faucet.d.ts +35 -35
  147. package/dist/apps/management/routes/invitations.d.ts +62 -62
  148. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  149. package/dist/apps/management/routes/invitations.js +4 -10
  150. package/dist/apps/management/routes/invitations.js.map +1 -1
  151. package/dist/apps/management/routes/invite-links.d.ts +30 -30
  152. package/dist/apps/management/routes/me.d.ts +6 -6
  153. package/dist/apps/management/routes/members.d.ts +78 -78
  154. package/dist/apps/management/routes/orgs.d.ts +48 -48
  155. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  156. package/dist/apps/management/routes/orgs.js +2 -8
  157. package/dist/apps/management/routes/orgs.js.map +1 -1
  158. package/dist/apps/management/routes/projects.d.ts +9 -9
  159. package/dist/apps/management/routes/scopes.js +2 -2
  160. package/dist/apps/management/routes/scopes.js.map +1 -1
  161. package/dist/apps/management/routes/usage.d.ts +80 -80
  162. package/dist/apps/management/routes/verified-token-requests.d.ts +36 -36
  163. package/dist/apps/management/routes/webhooks.d.ts +50 -50
  164. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  165. package/dist/apps/management/routes/webhooks.js +15 -3
  166. package/dist/apps/management/routes/webhooks.js.map +1 -1
  167. package/dist/apps/mpp/App.d.ts +55 -56
  168. package/dist/apps/mpp/App.d.ts.map +1 -1
  169. package/dist/apps/mpp/App.js +192 -27
  170. package/dist/apps/mpp/App.js.map +1 -1
  171. package/dist/apps/mpp/Mpp.d.ts +0 -20
  172. package/dist/apps/mpp/Mpp.d.ts.map +1 -1
  173. package/dist/apps/mpp/Mpp.js +0 -24
  174. package/dist/apps/mpp/Mpp.js.map +1 -1
  175. package/dist/apps/mpp/internal/Adapter.d.ts +40 -0
  176. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -0
  177. package/dist/apps/mpp/internal/Adapter.js +66 -0
  178. package/dist/apps/mpp/internal/Adapter.js.map +1 -0
  179. package/dist/apps/mpp/internal/Error.d.ts +4 -0
  180. package/dist/apps/mpp/internal/Error.d.ts.map +1 -0
  181. package/dist/apps/mpp/internal/Error.js +41 -0
  182. package/dist/apps/mpp/internal/Error.js.map +1 -0
  183. package/dist/apps/mpp/internal/Idempotency.d.ts +33 -0
  184. package/dist/apps/mpp/internal/Idempotency.d.ts.map +1 -0
  185. package/dist/apps/mpp/internal/Idempotency.js +28 -0
  186. package/dist/apps/mpp/internal/Idempotency.js.map +1 -0
  187. package/dist/apps/mpp/internal/Policy.d.ts +46 -0
  188. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -0
  189. package/dist/apps/mpp/internal/Policy.js +57 -0
  190. package/dist/apps/mpp/internal/Policy.js.map +1 -0
  191. package/dist/apps/mpp/internal/ScreenManager.d.ts +27 -0
  192. package/dist/apps/mpp/internal/ScreenManager.d.ts.map +1 -0
  193. package/dist/apps/mpp/internal/ScreenManager.js +18 -0
  194. package/dist/apps/mpp/internal/ScreenManager.js.map +1 -0
  195. package/dist/apps/mpp/internal/Screening.d.ts +35 -0
  196. package/dist/apps/mpp/internal/Screening.d.ts.map +1 -0
  197. package/dist/apps/mpp/internal/Screening.js +20 -0
  198. package/dist/apps/mpp/internal/Screening.js.map +1 -0
  199. package/dist/apps/mpp/internal/Sponsorship.d.ts +36 -0
  200. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -0
  201. package/dist/apps/mpp/internal/Sponsorship.js +103 -0
  202. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -0
  203. package/dist/apps/mpp/internal/Utils.d.ts +7 -0
  204. package/dist/apps/mpp/internal/Utils.d.ts.map +1 -0
  205. package/dist/apps/mpp/internal/Utils.js +24 -0
  206. package/dist/apps/mpp/internal/Utils.js.map +1 -0
  207. package/dist/apps/relay/App.js +184 -24
  208. package/dist/apps/relay/App.js.map +1 -1
  209. package/dist/apps/relay/Sponsorships.d.ts +4 -0
  210. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  211. package/dist/apps/relay/Sponsorships.js +16 -7
  212. package/dist/apps/relay/Sponsorships.js.map +1 -1
  213. package/dist/db/Db.d.ts +20 -0
  214. package/dist/db/Db.d.ts.map +1 -1
  215. package/dist/db/Db.js +342 -0
  216. package/dist/db/Db.js.map +1 -1
  217. package/dist/db/Schema.d.ts +921 -1
  218. package/dist/db/Schema.d.ts.map +1 -1
  219. package/dist/db/Schema.js +269 -1
  220. package/dist/db/Schema.js.map +1 -1
  221. package/dist/db/tables/earnVaults.d.ts +68 -0
  222. package/dist/db/tables/earnVaults.d.ts.map +1 -0
  223. package/dist/db/tables/earnVaults.js +72 -0
  224. package/dist/db/tables/earnVaults.js.map +1 -0
  225. package/dist/db/tables/fundingCatalog.d.ts +56 -0
  226. package/dist/db/tables/fundingCatalog.d.ts.map +1 -0
  227. package/dist/db/tables/fundingCatalog.js +85 -0
  228. package/dist/db/tables/fundingCatalog.js.map +1 -0
  229. package/dist/db/tables/fundingDepositAddresses.d.ts +159 -0
  230. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -0
  231. package/dist/db/tables/fundingDepositAddresses.js +206 -0
  232. package/dist/db/tables/fundingDepositAddresses.js.map +1 -0
  233. package/dist/db/tables/fundingDeposits.d.ts +115 -0
  234. package/dist/db/tables/fundingDeposits.d.ts.map +1 -0
  235. package/dist/db/tables/fundingDeposits.js +100 -0
  236. package/dist/db/tables/fundingDeposits.js.map +1 -0
  237. package/dist/db/tables/fundingIdempotency.d.ts +91 -0
  238. package/dist/db/tables/fundingIdempotency.d.ts.map +1 -0
  239. package/dist/db/tables/fundingIdempotency.js +103 -0
  240. package/dist/db/tables/fundingIdempotency.js.map +1 -0
  241. package/dist/db/tables/fundingTransferEvents.d.ts +12 -0
  242. package/dist/db/tables/fundingTransferEvents.d.ts.map +1 -0
  243. package/dist/db/tables/fundingTransferEvents.js +18 -0
  244. package/dist/db/tables/fundingTransferEvents.js.map +1 -0
  245. package/dist/db/tables/fundingTransferTransactions.d.ts +15 -0
  246. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -0
  247. package/dist/db/tables/fundingTransferTransactions.js +27 -0
  248. package/dist/db/tables/fundingTransferTransactions.js.map +1 -0
  249. package/dist/db/tables/fundingTransfers.d.ts +82 -0
  250. package/dist/db/tables/fundingTransfers.d.ts.map +1 -0
  251. package/dist/db/tables/fundingTransfers.js +89 -0
  252. package/dist/db/tables/fundingTransfers.js.map +1 -0
  253. package/dist/db/tables/organizations.d.ts +8 -0
  254. package/dist/db/tables/organizations.d.ts.map +1 -1
  255. package/dist/db/tables/organizations.js +35 -0
  256. package/dist/db/tables/organizations.js.map +1 -1
  257. package/dist/db/tables/projects.d.ts +8 -0
  258. package/dist/db/tables/projects.d.ts.map +1 -1
  259. package/dist/db/tables/projects.js +16 -0
  260. package/dist/db/tables/projects.js.map +1 -1
  261. package/dist/db/tables/requestUsage.d.ts +13 -0
  262. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  263. package/dist/db/tables/requestUsage.js +16 -0
  264. package/dist/db/tables/requestUsage.js.map +1 -1
  265. package/dist/db/tables/sponsoredTransactions.d.ts +4 -2
  266. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  267. package/dist/db/tables/sponsoredTransactions.js +14 -4
  268. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  269. package/dist/db/tables/users.d.ts +8 -0
  270. package/dist/db/tables/users.d.ts.map +1 -1
  271. package/dist/db/tables/users.js +16 -0
  272. package/dist/db/tables/users.js.map +1 -1
  273. package/dist/db/tables/verifiedTokens.d.ts +14 -7
  274. package/dist/db/tables/verifiedTokens.d.ts.map +1 -1
  275. package/dist/db/tables/verifiedTokens.js +9 -7
  276. package/dist/db/tables/verifiedTokens.js.map +1 -1
  277. package/dist/db/tables/webhookSubscriptions.d.ts +31 -1
  278. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  279. package/dist/db/tables/webhookSubscriptions.js +24 -3
  280. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  281. package/dist/handlers/relay.js +108 -108
  282. package/dist/handlers/relay.js.map +1 -1
  283. package/dist/internal/Auth.d.ts +26 -0
  284. package/dist/internal/Auth.d.ts.map +1 -1
  285. package/dist/internal/Auth.js +35 -2
  286. package/dist/internal/Auth.js.map +1 -1
  287. package/dist/internal/Cache.d.ts.map +1 -1
  288. package/dist/internal/Cache.js +16 -10
  289. package/dist/internal/Cache.js.map +1 -1
  290. package/dist/internal/Cursor.js +1 -1
  291. package/dist/internal/Cursor.js.map +1 -1
  292. package/dist/internal/EarnRates.d.ts +89 -0
  293. package/dist/internal/EarnRates.d.ts.map +1 -0
  294. package/dist/internal/EarnRates.js +266 -0
  295. package/dist/internal/EarnRates.js.map +1 -0
  296. package/dist/internal/EarnVaults.d.ts +171 -0
  297. package/dist/internal/EarnVaults.d.ts.map +1 -0
  298. package/dist/internal/EarnVaults.js +547 -0
  299. package/dist/internal/EarnVaults.js.map +1 -0
  300. package/dist/internal/EdgeCache.d.ts +9 -2
  301. package/dist/internal/EdgeCache.d.ts.map +1 -1
  302. package/dist/internal/EdgeCache.js +36 -9
  303. package/dist/internal/EdgeCache.js.map +1 -1
  304. package/dist/internal/Id.d.ts +11 -0
  305. package/dist/internal/Id.d.ts.map +1 -1
  306. package/dist/internal/Id.js +13 -0
  307. package/dist/internal/Id.js.map +1 -1
  308. package/dist/internal/Idempotency.d.ts +83 -0
  309. package/dist/internal/Idempotency.d.ts.map +1 -0
  310. package/dist/internal/Idempotency.js +113 -0
  311. package/dist/internal/Idempotency.js.map +1 -0
  312. package/dist/internal/Log.d.ts +64 -9
  313. package/dist/internal/Log.d.ts.map +1 -1
  314. package/dist/internal/Log.js +29 -7
  315. package/dist/internal/Log.js.map +1 -1
  316. package/dist/internal/MetricSink.d.ts.map +1 -1
  317. package/dist/internal/MetricSink.js +47 -1
  318. package/dist/internal/MetricSink.js.map +1 -1
  319. package/dist/internal/OpenApi.d.ts +1 -1
  320. package/dist/internal/OpenApi.d.ts.map +1 -1
  321. package/dist/internal/OpenApi.js +3 -0
  322. package/dist/internal/OpenApi.js.map +1 -1
  323. package/dist/internal/Provider.d.ts +14 -0
  324. package/dist/internal/Provider.d.ts.map +1 -0
  325. package/dist/internal/Provider.js +5 -0
  326. package/dist/internal/Provider.js.map +1 -0
  327. package/dist/internal/Schema.d.ts +8 -3
  328. package/dist/internal/Schema.d.ts.map +1 -1
  329. package/dist/internal/Schema.js +24 -13
  330. package/dist/internal/Schema.js.map +1 -1
  331. package/dist/internal/Store.d.ts +6 -0
  332. package/dist/internal/Store.d.ts.map +1 -1
  333. package/dist/internal/Store.js +35 -0
  334. package/dist/internal/Store.js.map +1 -1
  335. package/dist/internal/Tidx.d.ts +43 -24
  336. package/dist/internal/Tidx.d.ts.map +1 -1
  337. package/dist/internal/Tidx.js +79 -11
  338. package/dist/internal/Tidx.js.map +1 -1
  339. package/dist/internal/Viem.d.ts +82 -65
  340. package/dist/internal/Viem.d.ts.map +1 -1
  341. package/dist/internal/Viem.js +83 -12
  342. package/dist/internal/Viem.js.map +1 -1
  343. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  344. package/dist/internal/WebhookDestination.js +34 -28
  345. package/dist/internal/WebhookDestination.js.map +1 -1
  346. package/dist/internal/Webhooks.d.ts +9 -0
  347. package/dist/internal/Webhooks.d.ts.map +1 -1
  348. package/dist/internal/Webhooks.js +15 -10
  349. package/dist/internal/Webhooks.js.map +1 -1
  350. package/dist/internal/exchange/Provider.d.ts +219 -0
  351. package/dist/internal/exchange/Provider.d.ts.map +1 -0
  352. package/dist/internal/exchange/Provider.js +117 -0
  353. package/dist/internal/exchange/Provider.js.map +1 -0
  354. package/dist/internal/exchange/index.d.ts +2 -0
  355. package/dist/internal/exchange/index.d.ts.map +1 -0
  356. package/dist/internal/exchange/index.js +2 -0
  357. package/dist/internal/exchange/index.js.map +1 -0
  358. package/dist/internal/exchange/providers/uniswap.d.ts +57 -0
  359. package/dist/internal/exchange/providers/uniswap.d.ts.map +1 -0
  360. package/dist/internal/exchange/providers/uniswap.js +553 -0
  361. package/dist/internal/exchange/providers/uniswap.js.map +1 -0
  362. package/dist/internal/funding/Action.d.ts +55 -0
  363. package/dist/internal/funding/Action.d.ts.map +1 -0
  364. package/dist/internal/funding/Action.js +74 -0
  365. package/dist/internal/funding/Action.js.map +1 -0
  366. package/dist/internal/funding/Catalog.d.ts +107 -0
  367. package/dist/internal/funding/Catalog.d.ts.map +1 -0
  368. package/dist/internal/funding/Catalog.js +270 -0
  369. package/dist/internal/funding/Catalog.js.map +1 -0
  370. package/dist/internal/funding/Chain.d.ts +76 -0
  371. package/dist/internal/funding/Chain.d.ts.map +1 -0
  372. package/dist/internal/funding/Chain.js +38 -0
  373. package/dist/internal/funding/Chain.js.map +1 -0
  374. package/dist/internal/funding/Deposit.d.ts +366 -0
  375. package/dist/internal/funding/Deposit.d.ts.map +1 -0
  376. package/dist/internal/funding/Deposit.js +286 -0
  377. package/dist/internal/funding/Deposit.js.map +1 -0
  378. package/dist/internal/funding/DepositAddress.d.ts +345 -0
  379. package/dist/internal/funding/DepositAddress.d.ts.map +1 -0
  380. package/dist/internal/funding/DepositAddress.js +189 -0
  381. package/dist/internal/funding/DepositAddress.js.map +1 -0
  382. package/dist/internal/funding/Evidence.d.ts +52 -0
  383. package/dist/internal/funding/Evidence.d.ts.map +1 -0
  384. package/dist/internal/funding/Evidence.js +314 -0
  385. package/dist/internal/funding/Evidence.js.map +1 -0
  386. package/dist/internal/funding/Provider.d.ts +1186 -0
  387. package/dist/internal/funding/Provider.d.ts.map +1 -0
  388. package/dist/internal/funding/Provider.js +1081 -0
  389. package/dist/internal/funding/Provider.js.map +1 -0
  390. package/dist/internal/funding/Reconciliation.d.ts +113 -0
  391. package/dist/internal/funding/Reconciliation.d.ts.map +1 -0
  392. package/dist/internal/funding/Reconciliation.js +676 -0
  393. package/dist/internal/funding/Reconciliation.js.map +1 -0
  394. package/dist/internal/funding/Route.d.ts +21 -0
  395. package/dist/internal/funding/Route.d.ts.map +1 -0
  396. package/dist/internal/funding/Route.js +5 -0
  397. package/dist/internal/funding/Route.js.map +1 -0
  398. package/dist/internal/funding/Subsidy.d.ts +113 -0
  399. package/dist/internal/funding/Subsidy.d.ts.map +1 -0
  400. package/dist/internal/funding/Subsidy.js +138 -0
  401. package/dist/internal/funding/Subsidy.js.map +1 -0
  402. package/dist/internal/funding/Token.d.ts +41 -0
  403. package/dist/internal/funding/Token.d.ts.map +1 -0
  404. package/dist/internal/funding/Token.js +22 -0
  405. package/dist/internal/funding/Token.js.map +1 -0
  406. package/dist/internal/funding/Transfer.d.ts +643 -0
  407. package/dist/internal/funding/Transfer.d.ts.map +1 -0
  408. package/dist/internal/funding/Transfer.js +332 -0
  409. package/dist/internal/funding/Transfer.js.map +1 -0
  410. package/dist/internal/funding/index.d.ts +13 -0
  411. package/dist/internal/funding/index.d.ts.map +1 -0
  412. package/dist/internal/funding/index.js +13 -0
  413. package/dist/internal/funding/index.js.map +1 -0
  414. package/dist/internal/funding/providers/across.d.ts +21 -0
  415. package/dist/internal/funding/providers/across.d.ts.map +1 -0
  416. package/dist/internal/funding/providers/across.js +117 -0
  417. package/dist/internal/funding/providers/across.js.map +1 -0
  418. package/dist/internal/funding/providers/relay.d.ts +46 -0
  419. package/dist/internal/funding/providers/relay.d.ts.map +1 -0
  420. package/dist/internal/funding/providers/relay.js +464 -0
  421. package/dist/internal/funding/providers/relay.js.map +1 -0
  422. package/dist/internal/funding/providers/rhino.d.ts +15 -0
  423. package/dist/internal/funding/providers/rhino.d.ts.map +1 -0
  424. package/dist/internal/funding/providers/rhino.js +116 -0
  425. package/dist/internal/funding/providers/rhino.js.map +1 -0
  426. package/dist/internal/funding/providers/squid.d.ts +19 -0
  427. package/dist/internal/funding/providers/squid.d.ts.map +1 -0
  428. package/dist/internal/funding/providers/squid.js +120 -0
  429. package/dist/internal/funding/providers/squid.js.map +1 -0
  430. package/dist/internal/funding/providers/symbiosis.d.ts +20 -0
  431. package/dist/internal/funding/providers/symbiosis.d.ts.map +1 -0
  432. package/dist/internal/funding/providers/symbiosis.js +176 -0
  433. package/dist/internal/funding/providers/symbiosis.js.map +1 -0
  434. package/dist/internal/index.d.ts +5 -1
  435. package/dist/internal/index.d.ts.map +1 -1
  436. package/dist/internal/index.js +5 -1
  437. package/dist/internal/index.js.map +1 -1
  438. package/dist/internal/webhookDestinations/url.js +61 -57
  439. package/dist/internal/webhookDestinations/url.js.map +1 -1
  440. package/dist/providers/index.d.ts +43 -0
  441. package/dist/providers/index.d.ts.map +1 -0
  442. package/dist/providers/index.js +19 -0
  443. package/dist/providers/index.js.map +1 -0
  444. package/package.json +9 -4
  445. package/src/ApiKeys.test.ts +11 -7
  446. package/src/ApiKeys.ts +37 -9
  447. package/src/App.test.ts +85 -10
  448. package/src/App.ts +224 -137
  449. package/src/Assets.ts +29 -4
  450. package/src/Client.test-d.ts +650 -30
  451. package/src/Scope.test.ts +19 -0
  452. package/src/Scope.ts +28 -11
  453. package/src/Webhooks.test.ts +25 -0
  454. package/src/Webhooks.ts +1 -1
  455. package/src/admin/App.test.ts +9 -2
  456. package/src/admin/App.ts +32 -9
  457. package/src/admin/apps/api-keys.test.ts +167 -23
  458. package/src/admin/apps/api-keys.ts +166 -28
  459. package/src/admin/apps/earn-vaults.test.ts +338 -0
  460. package/src/admin/apps/earn-vaults.ts +572 -0
  461. package/src/admin/apps/scopes.test.ts +16 -1
  462. package/src/admin/ui/plugin.ts +2 -2
  463. package/src/admin/ui/src/components/EarnVaultForm.tsx +137 -0
  464. package/src/admin/ui/src/components/MobileProfile.tsx +2 -2
  465. package/src/admin/ui/src/components/Page.tsx +1 -1
  466. package/src/admin/ui/src/components/ThemeToggle.tsx +1 -1
  467. package/src/admin/ui/src/lib/api-keys.ts +37 -7
  468. package/src/admin/ui/src/lib/client.ts +1 -1
  469. package/src/admin/ui/src/lib/config.ts +1 -1
  470. package/src/admin/ui/src/lib/early-access.ts +8 -9
  471. package/src/admin/ui/src/lib/earn-vaults.ts +207 -0
  472. package/src/admin/ui/src/lib/format.ts +13 -0
  473. package/src/admin/ui/src/lib/organizations.test.ts +14 -4
  474. package/src/admin/ui/src/lib/organizations.ts +7 -1
  475. package/src/admin/ui/src/lib/response.ts +22 -0
  476. package/src/admin/ui/src/lib/scopes.test.ts +19 -11
  477. package/src/admin/ui/src/lib/scopes.ts +28 -7
  478. package/src/admin/ui/src/lib/verified-tokens.ts +10 -9
  479. package/src/admin/ui/src/routeTree.gen.ts +70 -7
  480. package/src/admin/ui/src/router.tsx +2 -2
  481. package/src/admin/ui/src/routes/__root.tsx +9 -8
  482. package/src/admin/ui/src/routes/api-keys.tsx +475 -64
  483. package/src/admin/ui/src/routes/early-access.tsx +5 -12
  484. package/src/admin/ui/src/routes/earn.vaults.tsx +348 -0
  485. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +154 -0
  486. package/src/admin/ui/src/routes/earn.vaults_.new.tsx +190 -0
  487. package/src/admin/ui/src/routes/organizations.tsx +4 -7
  488. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +56 -54
  489. package/src/admin/ui/src/routes/verified-tokens.tsx +4 -4
  490. package/src/admin/ui/vite.config.ts +4 -1
  491. package/src/admin/ui.generated.ts +189 -95
  492. package/src/analytics/Analytics.test.ts +69 -10
  493. package/src/analytics/Analytics.ts +20 -1
  494. package/src/analytics/tables/requestEvents.ts +79 -37
  495. package/src/apps/Zones.test.ts +33 -0
  496. package/src/apps/Zones.ts +40 -0
  497. package/src/apps/composition.test.ts +84 -1
  498. package/src/apps/data/App.test.ts +38 -9
  499. package/src/apps/data/App.ts +96 -33
  500. package/src/apps/data/FxOracle.ts +1 -1
  501. package/src/apps/data/index.ts +0 -1
  502. package/src/apps/data/routes/activities.test.ts +82 -6
  503. package/src/apps/data/routes/activities.ts +131 -71
  504. package/src/apps/data/routes/balances.test.ts +50 -37
  505. package/src/apps/data/routes/balances.ts +5 -5
  506. package/src/apps/data/routes/blocks.test.ts +2 -0
  507. package/src/apps/data/routes/coingecko.test.ts +22 -0
  508. package/src/apps/data/routes/coingecko.ts +84 -48
  509. package/src/apps/data/routes/earn.test.ts +1108 -0
  510. package/src/apps/data/routes/earn.ts +1391 -0
  511. package/src/apps/data/routes/exchanges.test.ts +82 -33
  512. package/src/apps/data/routes/exchanges.ts +842 -227
  513. package/src/apps/data/routes/fee-amm.test.ts +4 -6
  514. package/src/apps/data/routes/fee-amm.ts +69 -12
  515. package/src/apps/data/routes/funding.test.ts +2712 -221
  516. package/src/apps/data/routes/funding.ts +1604 -387
  517. package/src/apps/data/routes/indexer.test.ts +43 -34
  518. package/src/apps/data/routes/indexer.ts +19 -2
  519. package/src/apps/data/routes/receipts.test.ts +5 -3
  520. package/src/apps/data/routes/receipts.ts +13 -11
  521. package/src/apps/data/routes/rpc.test.ts +446 -28
  522. package/src/apps/data/routes/rpc.ts +243 -25
  523. package/src/apps/data/routes/tokenlist.test.ts +15 -0
  524. package/src/apps/data/routes/tokenlist.ts +88 -22
  525. package/src/apps/data/routes/tokens.ts +262 -86
  526. package/src/apps/data/routes/transactions.test.ts +2 -0
  527. package/src/apps/data/routes/transactions.ts +77 -42
  528. package/src/apps/data/routes/transfers.test.ts +166 -4
  529. package/src/apps/data/routes/transfers.ts +216 -78
  530. package/src/apps/data/routes/valuation.test.ts +62 -9
  531. package/src/apps/data/routes/valuation.ts +2 -2
  532. package/src/apps/data/routes/verified-tokens.test.ts +51 -0
  533. package/src/apps/data/routes/verified-tokens.ts +52 -15
  534. package/src/apps/data/routes/webhooks.test.ts +432 -33
  535. package/src/apps/data/routes/webhooks.ts +189 -79
  536. package/src/apps/data/routes/zones.test.ts +473 -0
  537. package/src/apps/data/routes/zones.ts +338 -0
  538. package/src/apps/docs/reference.ts +8 -6
  539. package/src/apps/index.ts +2 -1
  540. package/src/apps/management/Billing.test.ts +29 -0
  541. package/src/apps/management/Billing.ts +23 -0
  542. package/src/apps/management/Error.ts +12 -0
  543. package/src/apps/management/routes/api-keys.test.ts +5 -3
  544. package/src/apps/management/routes/api-keys.ts +2 -9
  545. package/src/apps/management/routes/invitations.ts +4 -11
  546. package/src/apps/management/routes/orgs.ts +2 -9
  547. package/src/apps/management/routes/scopes.test.ts +7 -2
  548. package/src/apps/management/routes/scopes.ts +2 -2
  549. package/src/apps/management/routes/webhooks.test.ts +6 -1
  550. package/src/apps/management/routes/webhooks.ts +15 -3
  551. package/src/apps/mpp/App.ts +276 -29
  552. package/src/apps/mpp/Mpp.test.ts +631 -26
  553. package/src/apps/mpp/Mpp.ts +0 -42
  554. package/src/apps/mpp/internal/Adapter.ts +108 -3
  555. package/src/apps/mpp/internal/Error.test.ts +33 -0
  556. package/src/apps/mpp/internal/Error.ts +44 -6
  557. package/src/apps/mpp/internal/Idempotency.test.ts +213 -0
  558. package/src/apps/mpp/internal/Idempotency.ts +59 -5
  559. package/src/apps/mpp/internal/Policy.test.ts +122 -0
  560. package/src/apps/mpp/internal/Policy.ts +98 -0
  561. package/src/apps/mpp/internal/ScreenManager.test.ts +68 -0
  562. package/src/apps/mpp/internal/ScreenManager.ts +41 -0
  563. package/src/apps/mpp/internal/Screening.ts +40 -3
  564. package/src/apps/mpp/internal/Sponsorship.ts +144 -0
  565. package/src/apps/mpp/internal/Utils.ts +30 -0
  566. package/src/apps/relay/App.test.ts +495 -64
  567. package/src/apps/relay/App.ts +234 -24
  568. package/src/apps/relay/Sponsorships.test.ts +51 -0
  569. package/src/apps/relay/Sponsorships.ts +19 -7
  570. package/src/db/Db.ts +439 -0
  571. package/src/db/Schema.ts +325 -2
  572. package/src/db/tables/earnVaults.test.ts +185 -0
  573. package/src/db/tables/earnVaults.ts +151 -0
  574. package/src/db/tables/fundingCatalog.test.ts +108 -0
  575. package/src/db/tables/fundingCatalog.ts +157 -0
  576. package/src/db/tables/fundingDepositAddresses.test.ts +580 -0
  577. package/src/db/tables/fundingDepositAddresses.ts +376 -0
  578. package/src/db/tables/fundingDeposits.test.ts +261 -0
  579. package/src/db/tables/fundingDeposits.ts +229 -0
  580. package/src/db/tables/fundingIdempotency.test.ts +182 -0
  581. package/src/db/tables/fundingIdempotency.ts +180 -0
  582. package/src/db/tables/fundingTransferEvents.ts +28 -0
  583. package/src/db/tables/fundingTransferTransactions.test.ts +69 -0
  584. package/src/db/tables/fundingTransferTransactions.ts +37 -0
  585. package/src/db/tables/fundingTransfers.test.ts +89 -0
  586. package/src/db/tables/fundingTransfers.ts +153 -0
  587. package/src/db/tables/organizations.test.ts +88 -0
  588. package/src/db/tables/organizations.ts +42 -1
  589. package/src/db/tables/projects.ts +16 -0
  590. package/src/db/tables/requestUsage.ts +23 -0
  591. package/src/db/tables/sponsoredTransactions.test.ts +52 -2
  592. package/src/db/tables/sponsoredTransactions.ts +18 -6
  593. package/src/db/tables/users.ts +16 -0
  594. package/src/db/tables/verifiedTokens.ts +19 -14
  595. package/src/db/tables/webhookSubscriptions.ts +58 -3
  596. package/src/handlers/relay.test.ts +2 -0
  597. package/src/handlers/relay.ts +95 -99
  598. package/src/internal/Auth.test.ts +48 -0
  599. package/src/internal/Auth.ts +61 -2
  600. package/src/internal/Cache.ts +13 -9
  601. package/src/internal/Cursor.test.ts +2 -2
  602. package/src/internal/Cursor.ts +1 -1
  603. package/src/internal/EarnRates.test.ts +125 -0
  604. package/src/internal/EarnRates.ts +409 -0
  605. package/src/internal/EarnVaults.test.ts +195 -0
  606. package/src/internal/EarnVaults.ts +876 -0
  607. package/src/internal/EdgeCache.test.ts +37 -5
  608. package/src/internal/EdgeCache.ts +39 -7
  609. package/src/internal/Id.ts +14 -0
  610. package/src/internal/Idempotency.test.ts +109 -0
  611. package/src/internal/Idempotency.ts +237 -0
  612. package/src/internal/Log.test-d.ts +41 -8
  613. package/src/internal/Log.test.ts +189 -8
  614. package/src/internal/Log.ts +124 -17
  615. package/src/internal/MetricSink.test.ts +244 -4
  616. package/src/internal/MetricSink.ts +51 -1
  617. package/src/internal/OpenApi.ts +3 -0
  618. package/src/internal/Provider.test.ts +17 -0
  619. package/src/internal/Provider.ts +17 -0
  620. package/src/internal/Schema.test.ts +13 -0
  621. package/src/internal/Schema.ts +41 -24
  622. package/src/internal/Store.test.ts +25 -0
  623. package/src/internal/Store.ts +46 -0
  624. package/src/internal/Tidx.local.test.ts +78 -3
  625. package/src/internal/Tidx.test.ts +212 -18
  626. package/src/internal/Tidx.ts +112 -32
  627. package/src/internal/VerifiedTokens.test.ts +15 -0
  628. package/src/internal/Viem.test.ts +170 -17
  629. package/src/internal/Viem.ts +111 -28
  630. package/src/internal/WebhookDestination.ts +25 -23
  631. package/src/internal/Webhooks.test.ts +59 -0
  632. package/src/internal/Webhooks.ts +34 -10
  633. package/src/internal/exchange/Provider.test.ts +216 -0
  634. package/src/internal/exchange/Provider.ts +326 -0
  635. package/src/internal/exchange/index.test.ts +6 -0
  636. package/src/internal/exchange/index.ts +1 -0
  637. package/src/internal/exchange/providers/uniswap.test.ts +122 -0
  638. package/src/internal/exchange/providers/uniswap.ts +682 -0
  639. package/src/internal/funding/Action.test.ts +138 -0
  640. package/src/internal/funding/Action.ts +117 -0
  641. package/src/internal/funding/Catalog.test.ts +311 -0
  642. package/src/internal/funding/Catalog.ts +347 -0
  643. package/src/internal/funding/Chain.test.ts +79 -0
  644. package/src/internal/funding/Chain.ts +120 -0
  645. package/src/internal/funding/Deposit.test.ts +26 -0
  646. package/src/internal/funding/Deposit.ts +453 -0
  647. package/src/internal/funding/DepositAddress.test.ts +18 -0
  648. package/src/internal/funding/DepositAddress.ts +312 -0
  649. package/src/internal/funding/Evidence.test.ts +297 -0
  650. package/src/internal/funding/Evidence.ts +430 -0
  651. package/src/internal/funding/Provider.test.ts +1328 -0
  652. package/src/internal/funding/Provider.ts +1796 -0
  653. package/src/internal/funding/Reconciliation.local.test.ts +504 -0
  654. package/src/internal/funding/Reconciliation.test.ts +2020 -0
  655. package/src/internal/funding/Reconciliation.ts +930 -0
  656. package/src/internal/funding/Route.test.ts +66 -0
  657. package/src/internal/funding/Route.ts +29 -0
  658. package/src/internal/funding/Subsidy.local.test.ts +152 -0
  659. package/src/internal/funding/Subsidy.test.ts +75 -0
  660. package/src/internal/funding/Subsidy.ts +260 -0
  661. package/src/internal/funding/Token.test.ts +47 -0
  662. package/src/internal/funding/Token.ts +59 -0
  663. package/src/internal/funding/Transfer.test.ts +369 -0
  664. package/src/internal/funding/Transfer.ts +522 -0
  665. package/src/internal/funding/index.test.ts +20 -0
  666. package/src/internal/funding/index.ts +12 -0
  667. package/src/internal/funding/providers/across.ts +149 -0
  668. package/src/{apps/data/FundingQuoteProviders.test.ts → internal/funding/providers/index.test.ts} +178 -157
  669. package/src/internal/funding/providers/relay.test.ts +474 -0
  670. package/src/internal/funding/providers/relay.ts +559 -0
  671. package/src/internal/funding/providers/rhino.ts +139 -0
  672. package/src/internal/funding/providers/squid.ts +151 -0
  673. package/src/internal/funding/providers/symbiosis.ts +221 -0
  674. package/src/internal/index.test.ts +5 -1
  675. package/src/internal/index.ts +5 -1
  676. package/src/internal/webhookDestinations/url.ts +49 -45
  677. package/src/providers/index.test.ts +14 -0
  678. package/src/providers/index.ts +54 -0
  679. package/dist/apps/data/FundingQuoteProviders.d.ts +0 -174
  680. package/dist/apps/data/FundingQuoteProviders.d.ts.map +0 -1
  681. package/dist/apps/data/FundingQuoteProviders.js +0 -775
  682. package/dist/apps/data/FundingQuoteProviders.js.map +0 -1
  683. package/dist/internal/FundingQuotes.d.ts +0 -864
  684. package/dist/internal/FundingQuotes.d.ts.map +0 -1
  685. package/dist/internal/FundingQuotes.js +0 -672
  686. package/dist/internal/FundingQuotes.js.map +0 -1
  687. package/src/apps/data/FundingQuoteProviders.ts +0 -1043
  688. package/src/internal/FundingQuotes.test.ts +0 -456
  689. package/src/internal/FundingQuotes.ts +0 -944
@@ -0,0 +1,2020 @@
1
+ import * as NodeBuffer from 'node:buffer'
2
+
3
+ import { AbiEvent } from 'ox'
4
+
5
+ import * as FundingCatalog from '../../db/tables/fundingCatalog.js'
6
+ import * as FundingDepositAddresses from '../../db/tables/fundingDepositAddresses.js'
7
+ import * as FundingDeposits from '../../db/tables/fundingDeposits.js'
8
+ import * as TestApp from '../../../test/App.js'
9
+ import * as TestFunding from '../../../test/Funding.js'
10
+ import * as TestRelay from '../../../test/Relay.js'
11
+ import * as Chain from './Chain.js'
12
+ import * as Deposit from './Deposit.js'
13
+ import * as DepositAddress from './DepositAddress.js'
14
+ import * as Evidence from './Evidence.js'
15
+ import * as Provider from './Provider.js'
16
+ import * as Reconciliation from './Reconciliation.js'
17
+ import * as Relay from './providers/relay.js'
18
+
19
+ const db = TestApp.database()
20
+ const transferTopic = AbiEvent.getSelector('event Transfer(address,address,uint256)')
21
+
22
+ describe('createTracker', () => {
23
+ test('requires action when Relay completes without destination funds', async () => {
24
+ const route = TestFunding.transferSnapshot()
25
+ const depositAddress = `0x${'44'.repeat(20)}`
26
+ const sourceTransactionHash = `0x${'aa'.repeat(32)}`
27
+ const providerTransactionHash = `0x${'cc'.repeat(32)}`
28
+ const address = await DepositAddress.create(db, {
29
+ apiKeyId: 'key_reconciliation',
30
+ deliveryStrategy: 'provider',
31
+ environment: 'production',
32
+ now: new Date('2026-01-01T00:00:00.000Z'),
33
+ orgId: 'org_reconciliation',
34
+ providerOutputToken: route.destinationToken,
35
+ snapshot: {
36
+ address: depositAddress,
37
+ destinationChain: route.destinationChain,
38
+ destinationToken: route.destinationToken,
39
+ provider: { id: 'relay', name: 'Relay' },
40
+ recipient: route.recipient,
41
+ refundAddress: `0x${'55'.repeat(20)}`,
42
+ sourceChain: route.sourceChain,
43
+ sourceToken: route.sourceToken,
44
+ subsidize: false,
45
+ },
46
+ })
47
+ const server = await TestRelay.createServer(async (request, response) => {
48
+ if (request.method === 'GET') {
49
+ response.writeHead(200, { 'content-type': 'application/json' })
50
+ response.end(
51
+ JSON.stringify({
52
+ requests: [
53
+ {
54
+ createdAt: '2026-01-01T00:01:00.000Z',
55
+ data: {
56
+ inTxs: [{ hash: providerTransactionHash }],
57
+ outTxs: [],
58
+ },
59
+ depositAddress: { depositTxHash: sourceTransactionHash },
60
+ id: 'relay-child-request',
61
+ status: 'success',
62
+ updatedAt: '2026-01-01T00:02:00.000Z',
63
+ },
64
+ ],
65
+ }),
66
+ )
67
+ return
68
+ }
69
+
70
+ const chunks: Uint8Array[] = []
71
+ for await (const chunk of request) chunks.push(chunk)
72
+ const body = JSON.parse(NodeBuffer.Buffer.concat(chunks).toString()) as {
73
+ id: number
74
+ method: string
75
+ params?: readonly string[] | undefined
76
+ }
77
+ const hash = body.params?.[0]
78
+ const result =
79
+ body.method === 'eth_blockNumber'
80
+ ? '0x100'
81
+ : {
82
+ blockNumber: '0x80',
83
+ logs: [
84
+ hash === sourceTransactionHash
85
+ ? transferLog({
86
+ amount: 1_000_000n,
87
+ recipient: depositAddress,
88
+ sender: `0x${'66'.repeat(20)}`,
89
+ token: route.sourceToken.address,
90
+ })
91
+ : transferLog({
92
+ amount: 950_000n,
93
+ recipient: route.recipient,
94
+ sender: `0x${'77'.repeat(20)}`,
95
+ token: route.destinationToken.address,
96
+ }),
97
+ ],
98
+ status: '0x1',
99
+ }
100
+ response.writeHead(200, { 'content-type': 'application/json' })
101
+ response.end(JSON.stringify({ id: body.id, jsonrpc: '2.0', result }))
102
+ })
103
+
104
+ try {
105
+ await FundingCatalog.publish(db, {
106
+ chainTokens: [],
107
+ chains: [
108
+ {
109
+ aliases: [],
110
+ id: route.destinationChain.id as Chain.Id,
111
+ name: route.destinationChain.name,
112
+ parentChainId: null,
113
+ rpcUrls: [server.url],
114
+ slug: 'destination',
115
+ },
116
+ {
117
+ aliases: [],
118
+ id: route.sourceChain.id as Chain.Id,
119
+ name: route.sourceChain.name,
120
+ parentChainId: null,
121
+ rpcUrls: [server.url],
122
+ slug: 'source',
123
+ },
124
+ ],
125
+ routes: [],
126
+ tokens: [],
127
+ })
128
+ const messages: Provider.Webhook.receive.Dispatchable[] = []
129
+ const provider = Relay.relay({ apiKey: 'relay-api-key', baseUrl: server.url })
130
+ const tracker = Reconciliation.createTracker({
131
+ db,
132
+ dispatch: async (message) => {
133
+ messages.push(message)
134
+ },
135
+ now: () => new Date('2027-01-01T00:00:00.000Z'),
136
+ providers: [provider],
137
+ verifyTransfers: Evidence.createVerifier({
138
+ db,
139
+ }),
140
+ })
141
+
142
+ const tick = await tracker.tick()
143
+ const message = messages[0]
144
+ if (!message) throw new Error('Expected a reconciliation message.')
145
+ const reconciliation = await tracker.reconcile(message)
146
+ const duplicate = await tracker.reconcile(message)
147
+ const [record] = await FundingDeposits.listBySourceTransaction(db, {
148
+ depositAddressId: address.id,
149
+ sourceTransactionHash,
150
+ })
151
+ const storedAddress = await FundingDepositAddresses.get(db, address.id)
152
+
153
+ expect({ duplicate, reconciliation, record, storedAddress, tick }).toMatchInlineSnapshot(
154
+ {
155
+ record: {
156
+ createdAt: expect.any(String),
157
+ depositAddressId: expect.any(String),
158
+ id: expect.any(String),
159
+ snapshot: { depositAddressId: expect.any(String) },
160
+ updatedAt: expect.any(String),
161
+ },
162
+ storedAddress: {
163
+ id: expect.any(String),
164
+ providerOutputToken: expect.any(Object),
165
+ snapshot: expect.any(Object),
166
+ },
167
+ },
168
+ `
169
+ {
170
+ "duplicate": {
171
+ "type": "busy",
172
+ },
173
+ "reconciliation": {
174
+ "created": 1,
175
+ "type": "completed",
176
+ "updated": 1,
177
+ },
178
+ "record": {
179
+ "createdAt": Any<String>,
180
+ "depositAddressId": Any<String>,
181
+ "environment": "production",
182
+ "id": Any<String>,
183
+ "orgId": "org_reconciliation",
184
+ "projectId": null,
185
+ "providerOutputAmount": null,
186
+ "providerOutputToken": {
187
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
188
+ "currency": "USD",
189
+ "decimals": 6,
190
+ "name": "Bridged USDC (Stargate)",
191
+ "standard": "TIP-20",
192
+ "symbol": "USDC.e",
193
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
194
+ "verified": true,
195
+ },
196
+ "providerRequestId": "relay-child-request",
197
+ "providerRequestIds": [
198
+ "relay-child-request",
199
+ ],
200
+ "providerState": null,
201
+ "providerTransactionHashes": [],
202
+ "providerTransferIndex": 0,
203
+ "retryState": null,
204
+ "settlementTransaction": null,
205
+ "settlementTransactionHash": null,
206
+ "snapshot": {
207
+ "depositAddressId": Any<String>,
208
+ "destinationChain": {
209
+ "addressFormat": "hex",
210
+ "id": "eip155:4217",
211
+ "kind": "evm",
212
+ "name": "Tempo",
213
+ },
214
+ "destinationToken": {
215
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
216
+ "currency": "USD",
217
+ "decimals": 6,
218
+ "name": "Bridged USDC (Stargate)",
219
+ "standard": "TIP-20",
220
+ "symbol": "USDC.e",
221
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
222
+ "verified": true,
223
+ },
224
+ "destinationTransactionHashes": [],
225
+ "provider": {
226
+ "id": "relay",
227
+ "name": "Relay",
228
+ },
229
+ "recipient": "0x1111111111111111111111111111111111111111",
230
+ "refundAddress": "0x5555555555555555555555555555555555555555",
231
+ "sender": "0x6666666666666666666666666666666666666666",
232
+ "sourceAmount": {
233
+ "baseUnits": "1000000",
234
+ "currency": "USD",
235
+ "decimals": 6,
236
+ "formatted": "1",
237
+ },
238
+ "sourceChain": {
239
+ "addressFormat": "hex",
240
+ "id": "eip155:8453",
241
+ "kind": "evm",
242
+ "name": "Base",
243
+ },
244
+ "sourceToken": {
245
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
246
+ "currency": "USD",
247
+ "decimals": 6,
248
+ "name": "USD Coin",
249
+ "standard": "ERC-20",
250
+ "symbol": "USDC",
251
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
252
+ "verified": true,
253
+ },
254
+ "sourceTransactionHashes": [
255
+ "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
256
+ ],
257
+ },
258
+ "sourceChainId": "eip155:8453",
259
+ "sourceTransactionHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
260
+ "sourceTransferIndex": 0,
261
+ "status": "action-required",
262
+ "statusReason": {
263
+ "code": "delivery_failed",
264
+ "message": "The provider completed without delivering destination funds.",
265
+ },
266
+ "subsidyAmount": null,
267
+ "tempoGasPaid": null,
268
+ "updatedAt": Any<String>,
269
+ "version": 3,
270
+ },
271
+ "storedAddress": {
272
+ "address": "0x4444444444444444444444444444444444444444",
273
+ "apiKeyId": "key_reconciliation",
274
+ "createdAt": "2026-01-01T00:00:00.000Z",
275
+ "deliveryStrategy": "provider",
276
+ "destinationTokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
277
+ "environment": "production",
278
+ "id": Any<String>,
279
+ "lastPolledAt": "2027-01-01T00:00:00.000Z",
280
+ "nextPollAt": "2027-01-01T00:01:00.000Z",
281
+ "orgId": "org_reconciliation",
282
+ "pollFailureCount": 0,
283
+ "pollLeaseUntil": null,
284
+ "pollLeaseVersion": 1,
285
+ "projectId": null,
286
+ "providerId": "relay",
287
+ "providerOutputToken": Any<Object>,
288
+ "providerRequestIds": [],
289
+ "providerState": {
290
+ "reconciliation": {
291
+ "pendingRequestIds": [],
292
+ "requestIds": [
293
+ "relay-child-request",
294
+ ],
295
+ "updatedAt": "2026-01-01T00:02:00.000Z",
296
+ },
297
+ },
298
+ "recipient": "0x1111111111111111111111111111111111111111",
299
+ "refundAddress": "0x5555555555555555555555555555555555555555",
300
+ "snapshot": Any<Object>,
301
+ "sourceChainId": "eip155:8453",
302
+ "sourceTokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
303
+ "status": "active",
304
+ "subsidize": false,
305
+ "updatedAt": "2026-01-01T00:00:00.000Z",
306
+ "version": 1,
307
+ },
308
+ "tick": {
309
+ "claimed": 1,
310
+ "failed": 0,
311
+ },
312
+ }
313
+ `,
314
+ )
315
+ } finally {
316
+ await server.closeAsync()
317
+ }
318
+ })
319
+
320
+ test('completes an unsubsidized deposit at its verified provider output', async () => {
321
+ const route = TestFunding.transferSnapshot()
322
+ const depositAddress = `0x${'45'.repeat(20)}`
323
+ const destinationTransactionHash = `0x${'cd'.repeat(32)}`
324
+ const sourceTransactionHash = `0x${'ab'.repeat(32)}`
325
+ const address = await DepositAddress.create(db, {
326
+ apiKeyId: 'key_unsubsidized_reconciliation',
327
+ deliveryStrategy: 'provider',
328
+ environment: 'production',
329
+ now: new Date('2026-01-06T00:00:00.000Z'),
330
+ orgId: 'org_unsubsidized_reconciliation',
331
+ providerOutputToken: route.destinationToken,
332
+ snapshot: {
333
+ address: depositAddress,
334
+ destinationChain: route.destinationChain,
335
+ destinationToken: route.destinationToken,
336
+ provider: { id: 'local', name: 'Local' },
337
+ recipient: route.recipient,
338
+ refundAddress: `0x${'46'.repeat(20)}`,
339
+ sourceChain: route.sourceChain,
340
+ sourceToken: route.sourceToken,
341
+ subsidize: false,
342
+ },
343
+ })
344
+ const server = await TestRelay.createServer(async (request, response) => {
345
+ const chunks: Uint8Array[] = []
346
+ for await (const chunk of request) chunks.push(chunk)
347
+ const body = JSON.parse(NodeBuffer.Buffer.concat(chunks).toString()) as {
348
+ id: number
349
+ method: string
350
+ params?: readonly string[] | undefined
351
+ }
352
+ const source = body.params?.[0] === sourceTransactionHash
353
+ const result =
354
+ body.method === 'eth_blockNumber'
355
+ ? '0x100'
356
+ : {
357
+ blockNumber: '0x80',
358
+ logs: [
359
+ transferLog({
360
+ amount: source ? 1_000_000n : 950_000n,
361
+ recipient: source ? depositAddress : route.recipient,
362
+ sender: `0x${'47'.repeat(20)}`,
363
+ token: source ? route.sourceToken.address : route.destinationToken.address,
364
+ }),
365
+ ],
366
+ status: '0x1',
367
+ }
368
+ response.writeHead(200, { 'content-type': 'application/json' })
369
+ response.end(JSON.stringify({ id: body.id, jsonrpc: '2.0', result }))
370
+ })
371
+
372
+ try {
373
+ await FundingCatalog.publish(db, {
374
+ chainTokens: [],
375
+ chains: [
376
+ {
377
+ aliases: [],
378
+ id: route.destinationChain.id as Chain.Id,
379
+ name: route.destinationChain.name,
380
+ parentChainId: null,
381
+ rpcUrls: [server.url],
382
+ slug: 'destination-unsubsidized',
383
+ },
384
+ {
385
+ aliases: [],
386
+ id: route.sourceChain.id as Chain.Id,
387
+ name: route.sourceChain.name,
388
+ parentChainId: null,
389
+ rpcUrls: [server.url],
390
+ slug: 'source-unsubsidized',
391
+ },
392
+ ],
393
+ routes: [],
394
+ tokens: [],
395
+ })
396
+ const result = await Reconciliation.reconcileRequest(db, {
397
+ address,
398
+ request: {
399
+ createdAt: '2026-01-06T00:01:00.000Z',
400
+ destinationTransactionHashes: [destinationTransactionHash],
401
+ id: 'unsubsidized-request',
402
+ sourceTransactionHashes: [sourceTransactionHash],
403
+ status: 'success',
404
+ updatedAt: '2026-01-06T00:02:00.000Z',
405
+ },
406
+ verifyTransfers: Evidence.createVerifier({ db }),
407
+ })
408
+ const [record] = await FundingDeposits.listBySourceTransaction(db, {
409
+ depositAddressId: address.id,
410
+ sourceTransactionHash,
411
+ })
412
+
413
+ expect({ record, result }).toMatchInlineSnapshot(
414
+ {
415
+ record: {
416
+ createdAt: expect.any(String),
417
+ depositAddressId: expect.any(String),
418
+ id: expect.any(String),
419
+ snapshot: { depositAddressId: expect.any(String) },
420
+ updatedAt: expect.any(String),
421
+ },
422
+ },
423
+ `
424
+ {
425
+ "record": {
426
+ "createdAt": Any<String>,
427
+ "depositAddressId": Any<String>,
428
+ "environment": "production",
429
+ "id": Any<String>,
430
+ "orgId": "org_unsubsidized_reconciliation",
431
+ "projectId": null,
432
+ "providerOutputAmount": {
433
+ "baseUnits": "950000",
434
+ "currency": "USD",
435
+ "decimals": 6,
436
+ "formatted": "0.95",
437
+ },
438
+ "providerOutputToken": {
439
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
440
+ "currency": "USD",
441
+ "decimals": 6,
442
+ "name": "Bridged USDC (Stargate)",
443
+ "standard": "TIP-20",
444
+ "symbol": "USDC.e",
445
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
446
+ "verified": true,
447
+ },
448
+ "providerRequestId": "unsubsidized-request",
449
+ "providerRequestIds": [
450
+ "unsubsidized-request",
451
+ ],
452
+ "providerState": null,
453
+ "providerTransactionHashes": [
454
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
455
+ ],
456
+ "providerTransferIndex": 0,
457
+ "retryState": null,
458
+ "settlementTransaction": null,
459
+ "settlementTransactionHash": null,
460
+ "snapshot": {
461
+ "depositAddressId": Any<String>,
462
+ "destinationAmount": {
463
+ "baseUnits": "950000",
464
+ "currency": "USD",
465
+ "decimals": 6,
466
+ "formatted": "0.95",
467
+ },
468
+ "destinationAmountRequired": {
469
+ "baseUnits": "950000",
470
+ "currency": "USD",
471
+ "decimals": 6,
472
+ "formatted": "0.95",
473
+ },
474
+ "destinationChain": {
475
+ "addressFormat": "hex",
476
+ "id": "eip155:4217",
477
+ "kind": "evm",
478
+ "name": "Tempo",
479
+ },
480
+ "destinationToken": {
481
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
482
+ "currency": "USD",
483
+ "decimals": 6,
484
+ "name": "Bridged USDC (Stargate)",
485
+ "standard": "TIP-20",
486
+ "symbol": "USDC.e",
487
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
488
+ "verified": true,
489
+ },
490
+ "destinationTransactionHashes": [
491
+ "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
492
+ ],
493
+ "provider": {
494
+ "id": "local",
495
+ "name": "Local",
496
+ },
497
+ "recipient": "0x1111111111111111111111111111111111111111",
498
+ "refundAddress": "0x4646464646464646464646464646464646464646",
499
+ "sender": "0x4747474747474747474747474747474747474747",
500
+ "sourceAmount": {
501
+ "baseUnits": "1000000",
502
+ "currency": "USD",
503
+ "decimals": 6,
504
+ "formatted": "1",
505
+ },
506
+ "sourceChain": {
507
+ "addressFormat": "hex",
508
+ "id": "eip155:8453",
509
+ "kind": "evm",
510
+ "name": "Base",
511
+ },
512
+ "sourceToken": {
513
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
514
+ "currency": "USD",
515
+ "decimals": 6,
516
+ "name": "USD Coin",
517
+ "standard": "ERC-20",
518
+ "symbol": "USDC",
519
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
520
+ "verified": true,
521
+ },
522
+ "sourceTransactionHashes": [
523
+ "0xabababababababababababababababababababababababababababababababab",
524
+ ],
525
+ },
526
+ "sourceChainId": "eip155:8453",
527
+ "sourceTransactionHash": "0xabababababababababababababababababababababababababababababababab",
528
+ "sourceTransferIndex": 0,
529
+ "status": "completed",
530
+ "statusReason": null,
531
+ "subsidyAmount": null,
532
+ "tempoGasPaid": null,
533
+ "updatedAt": Any<String>,
534
+ "version": 3,
535
+ },
536
+ "result": {
537
+ "created": 1,
538
+ "terminal": true,
539
+ "updated": 1,
540
+ },
541
+ }
542
+ `,
543
+ )
544
+ } finally {
545
+ await server.closeAsync()
546
+ }
547
+ })
548
+
549
+ test('persists a detected deposit before source verification succeeds', async () => {
550
+ const route = TestFunding.transferSnapshot()
551
+ const depositAddress = `0x${'20'.repeat(20)}`
552
+ const sourceTransactionHash = `0x${'21'.repeat(32)}`
553
+ const address = await DepositAddress.create(db, {
554
+ apiKeyId: 'key_observed_reconciliation',
555
+ deliveryStrategy: 'provider',
556
+ environment: 'production',
557
+ now: new Date('2026-01-04T00:00:00.000Z'),
558
+ orgId: 'org_observed_reconciliation',
559
+ providerOutputToken: route.destinationToken,
560
+ snapshot: {
561
+ address: depositAddress,
562
+ destinationChain: route.destinationChain,
563
+ destinationToken: route.destinationToken,
564
+ provider: { id: 'relay', name: 'Relay' },
565
+ recipient: route.recipient,
566
+ refundAddress: `0x${'22'.repeat(20)}`,
567
+ sourceChain: route.sourceChain,
568
+ sourceToken: route.sourceToken,
569
+ subsidize: false,
570
+ },
571
+ })
572
+ const server = await TestRelay.createServer(async (request, response) => {
573
+ response.writeHead(200, { 'content-type': 'application/json' })
574
+ if (request.method === 'GET') {
575
+ response.end(
576
+ JSON.stringify({
577
+ requests: [
578
+ {
579
+ createdAt: '2026-01-04T00:01:00.000Z',
580
+ data: { inTxs: [{ hash: sourceTransactionHash }], outTxs: [] },
581
+ depositAddress: { depositTxHash: sourceTransactionHash },
582
+ id: 'relay-observed-request',
583
+ status: 'pending',
584
+ updatedAt: '2026-01-04T00:02:00.000Z',
585
+ },
586
+ ],
587
+ }),
588
+ )
589
+ return
590
+ }
591
+
592
+ const chunks: Uint8Array[] = []
593
+ for await (const chunk of request) chunks.push(chunk)
594
+ const body = JSON.parse(NodeBuffer.Buffer.concat(chunks).toString()) as { id: number }
595
+ response.end(
596
+ JSON.stringify({
597
+ error: { code: -32_000, message: 'Source RPC unavailable.' },
598
+ id: body.id,
599
+ jsonrpc: '2.0',
600
+ }),
601
+ )
602
+ })
603
+
604
+ try {
605
+ await FundingCatalog.publish(db, {
606
+ chainTokens: [],
607
+ chains: [
608
+ {
609
+ aliases: [],
610
+ id: route.destinationChain.id as Chain.Id,
611
+ name: route.destinationChain.name,
612
+ parentChainId: null,
613
+ rpcUrls: [server.url],
614
+ slug: 'destination',
615
+ },
616
+ {
617
+ aliases: [],
618
+ id: route.sourceChain.id as Chain.Id,
619
+ name: route.sourceChain.name,
620
+ parentChainId: null,
621
+ rpcUrls: [server.url],
622
+ slug: 'source',
623
+ },
624
+ ],
625
+ routes: [],
626
+ tokens: [],
627
+ })
628
+ const tracker = Reconciliation.createTracker({
629
+ db,
630
+ dispatch: async () => undefined,
631
+ providers: [Relay.relay({ apiKey: 'relay-api-key', baseUrl: server.url })],
632
+ verifyTransfers: Evidence.createVerifier({ db }),
633
+ })
634
+ const failures: boolean[] = []
635
+ for (let attempt = 0; attempt < 2; attempt++)
636
+ failures.push(
637
+ await tracker
638
+ .reconcile({
639
+ addressId: address.id,
640
+ type: 'funding:deposit-address:reconcile',
641
+ })
642
+ .then(
643
+ () => false,
644
+ () => true,
645
+ ),
646
+ )
647
+ const records = await FundingDeposits.listByAddress(
648
+ db,
649
+ { environment: 'production', orgId: 'org_observed_reconciliation' },
650
+ address.id,
651
+ { limit: 10 },
652
+ )
653
+
654
+ expect({ failures, records: records.map(Deposit.toPublic) }).toMatchInlineSnapshot(
655
+ {
656
+ records: [
657
+ {
658
+ createdAt: expect.any(String),
659
+ depositAddressId: expect.any(String),
660
+ id: expect.any(String),
661
+ updatedAt: expect.any(String),
662
+ },
663
+ ],
664
+ },
665
+ `
666
+ {
667
+ "failures": [
668
+ true,
669
+ true,
670
+ ],
671
+ "records": [
672
+ {
673
+ "createdAt": Any<String>,
674
+ "depositAddressId": Any<String>,
675
+ "destinationChain": {
676
+ "addressFormat": "hex",
677
+ "id": "eip155:4217",
678
+ "kind": "evm",
679
+ "name": "Tempo",
680
+ },
681
+ "destinationToken": {
682
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
683
+ "currency": "USD",
684
+ "decimals": 6,
685
+ "name": "Bridged USDC (Stargate)",
686
+ "standard": "TIP-20",
687
+ "symbol": "USDC.e",
688
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
689
+ "verified": true,
690
+ },
691
+ "id": Any<String>,
692
+ "provider": {
693
+ "id": "relay",
694
+ "name": "Relay",
695
+ },
696
+ "recipient": "0x1111111111111111111111111111111111111111",
697
+ "refundAddress": "0x2222222222222222222222222222222222222222",
698
+ "sourceChain": {
699
+ "addressFormat": "hex",
700
+ "id": "eip155:8453",
701
+ "kind": "evm",
702
+ "name": "Base",
703
+ },
704
+ "sourceToken": {
705
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
706
+ "currency": "USD",
707
+ "decimals": 6,
708
+ "name": "USD Coin",
709
+ "standard": "ERC-20",
710
+ "symbol": "USDC",
711
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
712
+ "verified": true,
713
+ },
714
+ "sourceTransactionHashes": [
715
+ "0x2121212121212121212121212121212121212121212121212121212121212121",
716
+ ],
717
+ "status": "detected",
718
+ "updatedAt": Any<String>,
719
+ },
720
+ ],
721
+ }
722
+ `,
723
+ )
724
+ } finally {
725
+ await server.closeAsync()
726
+ }
727
+ })
728
+
729
+ test('rechecks pending Relay requests by id until delivery is verified', async () => {
730
+ const route = TestFunding.transferSnapshot()
731
+ const depositAddress = `0x${'88'.repeat(20)}`
732
+ const sourceTransactionHash = `0x${'cc'.repeat(32)}`
733
+ const destinationTransactionHash = `0x${'dd'.repeat(32)}`
734
+ const address = await DepositAddress.create(db, {
735
+ apiKeyId: 'key_pending_reconciliation',
736
+ deliveryStrategy: 'provider',
737
+ environment: 'production',
738
+ now: new Date('2026-01-02T00:00:00.000Z'),
739
+ orgId: 'org_pending_reconciliation',
740
+ providerOutputToken: route.destinationToken,
741
+ snapshot: {
742
+ address: depositAddress,
743
+ destinationChain: route.destinationChain,
744
+ destinationToken: route.destinationToken,
745
+ provider: { id: 'relay', name: 'Relay' },
746
+ recipient: route.recipient,
747
+ refundAddress: `0x${'99'.repeat(20)}`,
748
+ sourceChain: route.sourceChain,
749
+ sourceToken: route.sourceToken,
750
+ subsidize: false,
751
+ },
752
+ })
753
+ const requestTargets: string[] = []
754
+ let delivered = false
755
+ let includeAddressRequest = true
756
+ let returnOlderDirect = false
757
+ const server = await TestRelay.createServer(async (request, response) => {
758
+ if (request.method === 'GET') {
759
+ const url = new URL(request.url ?? '/', server.url)
760
+ requestTargets.push(`${url.pathname}${url.search}`)
761
+ const direct = url.searchParams.get('id') === 'relay-pending-request'
762
+ response.writeHead(200, { 'content-type': 'application/json' })
763
+ response.end(
764
+ JSON.stringify({
765
+ requests:
766
+ includeAddressRequest || direct
767
+ ? [
768
+ {
769
+ createdAt: '2026-01-02T00:01:00.000Z',
770
+ data: {
771
+ inTxs: [{ hash: sourceTransactionHash }],
772
+ outTxs: delivered ? [{ hash: destinationTransactionHash }] : [],
773
+ },
774
+ depositAddress: { depositTxHash: sourceTransactionHash },
775
+ id: 'relay-pending-request',
776
+ status: delivered ? 'success' : 'pending',
777
+ updatedAt: (() => {
778
+ if (direct && returnOlderDirect) return '2026-01-01T00:00:00.000Z'
779
+ if (delivered) return '2026-01-02T00:03:00.500Z'
780
+ return '2026-01-02T00:03:00Z'
781
+ })(),
782
+ },
783
+ ]
784
+ : [],
785
+ }),
786
+ )
787
+ return
788
+ }
789
+
790
+ const chunks: Uint8Array[] = []
791
+ for await (const chunk of request) chunks.push(chunk)
792
+ const body = JSON.parse(NodeBuffer.Buffer.concat(chunks).toString()) as {
793
+ id: number
794
+ method: string
795
+ params?: readonly string[] | undefined
796
+ }
797
+ const hash = body.params?.[0]
798
+ const result =
799
+ body.method === 'eth_blockNumber'
800
+ ? '0x100'
801
+ : {
802
+ blockNumber: '0x80',
803
+ logs: [
804
+ hash === sourceTransactionHash
805
+ ? transferLog({
806
+ amount: 1_000_000n,
807
+ recipient: depositAddress,
808
+ sender: `0x${'aa'.repeat(20)}`,
809
+ token: route.sourceToken.address,
810
+ })
811
+ : transferLog({
812
+ amount: 1_000_000n,
813
+ recipient: route.recipient,
814
+ sender: `0x${'bb'.repeat(20)}`,
815
+ token: route.destinationToken.address,
816
+ }),
817
+ ],
818
+ status: '0x1',
819
+ }
820
+ response.writeHead(200, { 'content-type': 'application/json' })
821
+ response.end(JSON.stringify({ id: body.id, jsonrpc: '2.0', result }))
822
+ })
823
+
824
+ try {
825
+ await FundingCatalog.publish(db, {
826
+ chainTokens: [],
827
+ chains: [
828
+ {
829
+ aliases: [],
830
+ id: route.destinationChain.id as Chain.Id,
831
+ name: route.destinationChain.name,
832
+ parentChainId: null,
833
+ rpcUrls: [server.url],
834
+ slug: 'destination',
835
+ },
836
+ {
837
+ aliases: [],
838
+ id: route.sourceChain.id as Chain.Id,
839
+ name: route.sourceChain.name,
840
+ parentChainId: null,
841
+ rpcUrls: [server.url],
842
+ slug: 'source',
843
+ },
844
+ ],
845
+ routes: [],
846
+ tokens: [],
847
+ })
848
+ const provider = Relay.relay({ apiKey: 'relay-api-key', baseUrl: server.url })
849
+ const tracker = Reconciliation.createTracker({
850
+ db,
851
+ dispatch: async () => undefined,
852
+ now: () => new Date('2027-01-02T00:00:00.000Z'),
853
+ providers: [provider],
854
+ verifyTransfers: Evidence.createVerifier({ db }),
855
+ })
856
+
857
+ const pending = await tracker.reconcile({
858
+ addressId: address.id,
859
+ type: 'funding:deposit-address:reconcile',
860
+ })
861
+ delivered = true
862
+ includeAddressRequest = false
863
+ const completed = await tracker.reconcile({
864
+ addressId: address.id,
865
+ type: 'funding:deposit-address:reconcile',
866
+ })
867
+ includeAddressRequest = true
868
+ const replay = await tracker.reconcile({
869
+ addressId: address.id,
870
+ type: 'funding:deposit-address:reconcile',
871
+ })
872
+ includeAddressRequest = false
873
+ returnOlderDirect = true
874
+ const webhook = await tracker.reconcile({
875
+ addressId: address.id,
876
+ requestId: 'relay-pending-request',
877
+ type: 'funding:deposit-address:reconcile',
878
+ })
879
+ const [record] = await FundingDeposits.listBySourceTransaction(db, {
880
+ depositAddressId: address.id,
881
+ sourceTransactionHash,
882
+ })
883
+ const storedAddress = await FundingDepositAddresses.get(db, address.id)
884
+
885
+ expect({
886
+ completed,
887
+ pending,
888
+ record,
889
+ replay,
890
+ requestTargets,
891
+ storedAddress,
892
+ webhook,
893
+ }).toMatchInlineSnapshot(
894
+ {
895
+ record: {
896
+ createdAt: expect.any(String),
897
+ depositAddressId: expect.any(String),
898
+ id: expect.any(String),
899
+ snapshot: { depositAddressId: expect.any(String) },
900
+ updatedAt: expect.any(String),
901
+ },
902
+ storedAddress: {
903
+ id: expect.any(String),
904
+ providerOutputToken: expect.any(Object),
905
+ snapshot: expect.any(Object),
906
+ },
907
+ },
908
+ `
909
+ {
910
+ "completed": {
911
+ "created": 0,
912
+ "type": "completed",
913
+ "updated": 1,
914
+ },
915
+ "pending": {
916
+ "created": 1,
917
+ "type": "completed",
918
+ "updated": 1,
919
+ },
920
+ "record": {
921
+ "createdAt": Any<String>,
922
+ "depositAddressId": Any<String>,
923
+ "environment": "production",
924
+ "id": Any<String>,
925
+ "orgId": "org_pending_reconciliation",
926
+ "projectId": null,
927
+ "providerOutputAmount": {
928
+ "baseUnits": "1000000",
929
+ "currency": "USD",
930
+ "decimals": 6,
931
+ "formatted": "1",
932
+ },
933
+ "providerOutputToken": {
934
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
935
+ "currency": "USD",
936
+ "decimals": 6,
937
+ "name": "Bridged USDC (Stargate)",
938
+ "standard": "TIP-20",
939
+ "symbol": "USDC.e",
940
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
941
+ "verified": true,
942
+ },
943
+ "providerRequestId": "relay-pending-request",
944
+ "providerRequestIds": [
945
+ "relay-pending-request",
946
+ ],
947
+ "providerState": null,
948
+ "providerTransactionHashes": [
949
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
950
+ ],
951
+ "providerTransferIndex": 0,
952
+ "retryState": null,
953
+ "settlementTransaction": null,
954
+ "settlementTransactionHash": null,
955
+ "snapshot": {
956
+ "depositAddressId": Any<String>,
957
+ "destinationAmount": {
958
+ "baseUnits": "1000000",
959
+ "currency": "USD",
960
+ "decimals": 6,
961
+ "formatted": "1",
962
+ },
963
+ "destinationAmountRequired": {
964
+ "baseUnits": "1000000",
965
+ "currency": "USD",
966
+ "decimals": 6,
967
+ "formatted": "1",
968
+ },
969
+ "destinationChain": {
970
+ "addressFormat": "hex",
971
+ "id": "eip155:4217",
972
+ "kind": "evm",
973
+ "name": "Tempo",
974
+ },
975
+ "destinationToken": {
976
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
977
+ "currency": "USD",
978
+ "decimals": 6,
979
+ "name": "Bridged USDC (Stargate)",
980
+ "standard": "TIP-20",
981
+ "symbol": "USDC.e",
982
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
983
+ "verified": true,
984
+ },
985
+ "destinationTransactionHashes": [
986
+ "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
987
+ ],
988
+ "provider": {
989
+ "id": "relay",
990
+ "name": "Relay",
991
+ },
992
+ "recipient": "0x1111111111111111111111111111111111111111",
993
+ "refundAddress": "0x9999999999999999999999999999999999999999",
994
+ "sender": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
995
+ "sourceAmount": {
996
+ "baseUnits": "1000000",
997
+ "currency": "USD",
998
+ "decimals": 6,
999
+ "formatted": "1",
1000
+ },
1001
+ "sourceChain": {
1002
+ "addressFormat": "hex",
1003
+ "id": "eip155:8453",
1004
+ "kind": "evm",
1005
+ "name": "Base",
1006
+ },
1007
+ "sourceToken": {
1008
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1009
+ "currency": "USD",
1010
+ "decimals": 6,
1011
+ "name": "USD Coin",
1012
+ "standard": "ERC-20",
1013
+ "symbol": "USDC",
1014
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1015
+ "verified": true,
1016
+ },
1017
+ "sourceTransactionHashes": [
1018
+ "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
1019
+ ],
1020
+ },
1021
+ "sourceChainId": "eip155:8453",
1022
+ "sourceTransactionHash": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
1023
+ "sourceTransferIndex": 0,
1024
+ "status": "completed",
1025
+ "statusReason": null,
1026
+ "subsidyAmount": null,
1027
+ "tempoGasPaid": null,
1028
+ "updatedAt": Any<String>,
1029
+ "version": 5,
1030
+ },
1031
+ "replay": {
1032
+ "created": 0,
1033
+ "type": "completed",
1034
+ "updated": 0,
1035
+ },
1036
+ "requestTargets": [
1037
+ "/requests/v2?depositAddress=0x8888888888888888888888888888888888888888&includeChildRequests=true&limit=50&sortBy=updatedAt&sortDirection=desc",
1038
+ "/requests/v2?depositAddress=0x8888888888888888888888888888888888888888&includeChildRequests=true&limit=50&sortBy=updatedAt&sortDirection=desc",
1039
+ "/requests/v2?id=relay-pending-request&limit=50&sortBy=updatedAt&sortDirection=desc",
1040
+ "/requests/v2?depositAddress=0x8888888888888888888888888888888888888888&includeChildRequests=true&limit=50&sortBy=updatedAt&sortDirection=desc",
1041
+ "/requests/v2?depositAddress=0x8888888888888888888888888888888888888888&includeChildRequests=true&limit=50&sortBy=updatedAt&sortDirection=desc",
1042
+ "/requests/v2?id=relay-pending-request&limit=50&sortBy=updatedAt&sortDirection=desc",
1043
+ ],
1044
+ "storedAddress": {
1045
+ "address": "0x8888888888888888888888888888888888888888",
1046
+ "apiKeyId": "key_pending_reconciliation",
1047
+ "createdAt": "2026-01-02T00:00:00.000Z",
1048
+ "deliveryStrategy": "provider",
1049
+ "destinationTokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1050
+ "environment": "production",
1051
+ "id": Any<String>,
1052
+ "lastPolledAt": "2027-01-02T00:00:00.000Z",
1053
+ "nextPollAt": "2027-01-02T00:01:00.000Z",
1054
+ "orgId": "org_pending_reconciliation",
1055
+ "pollFailureCount": 0,
1056
+ "pollLeaseUntil": null,
1057
+ "pollLeaseVersion": 4,
1058
+ "projectId": null,
1059
+ "providerId": "relay",
1060
+ "providerOutputToken": Any<Object>,
1061
+ "providerRequestIds": [],
1062
+ "providerState": {
1063
+ "reconciliation": {
1064
+ "pendingRequestIds": [],
1065
+ "requestIds": [
1066
+ "relay-pending-request",
1067
+ ],
1068
+ "updatedAt": "2026-01-02T00:03:00.500Z",
1069
+ },
1070
+ },
1071
+ "recipient": "0x1111111111111111111111111111111111111111",
1072
+ "refundAddress": "0x9999999999999999999999999999999999999999",
1073
+ "snapshot": Any<Object>,
1074
+ "sourceChainId": "eip155:8453",
1075
+ "sourceTokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1076
+ "status": "active",
1077
+ "subsidize": false,
1078
+ "updatedAt": "2026-01-02T00:00:00.000Z",
1079
+ "version": 1,
1080
+ },
1081
+ "webhook": {
1082
+ "created": 0,
1083
+ "type": "completed",
1084
+ "updated": 0,
1085
+ },
1086
+ }
1087
+ `,
1088
+ )
1089
+ } finally {
1090
+ await server.closeAsync()
1091
+ }
1092
+ })
1093
+
1094
+ test('renews the address lease during a slow provider read', async () => {
1095
+ const route = TestFunding.transferSnapshot()
1096
+ const depositAddress = `0x${'16'.repeat(20)}`
1097
+ const address = await DepositAddress.create(db, {
1098
+ apiKeyId: 'key_lease_reconciliation',
1099
+ deliveryStrategy: 'provider',
1100
+ environment: 'production',
1101
+ now: new Date('2026-01-04T00:00:00.000Z'),
1102
+ orgId: 'org_lease_reconciliation',
1103
+ providerOutputToken: route.destinationToken,
1104
+ snapshot: {
1105
+ address: depositAddress,
1106
+ destinationChain: route.destinationChain,
1107
+ destinationToken: route.destinationToken,
1108
+ provider: { id: 'relay', name: 'Relay' },
1109
+ recipient: route.recipient,
1110
+ refundAddress: `0x${'17'.repeat(20)}`,
1111
+ sourceChain: route.sourceChain,
1112
+ sourceToken: route.sourceToken,
1113
+ subsidize: false,
1114
+ },
1115
+ })
1116
+ const providerReleased = Promise.withResolvers<void>()
1117
+ const providerStarted = Promise.withResolvers<void>()
1118
+ const server = await TestRelay.createServer(async (request, response) => {
1119
+ if (request.method !== 'GET') throw new Error('Expected a Relay request read.')
1120
+ providerStarted.resolve()
1121
+ await providerReleased.promise
1122
+ response.writeHead(200, { 'content-type': 'application/json' })
1123
+ response.end(JSON.stringify({ requests: [] }))
1124
+ })
1125
+
1126
+ try {
1127
+ vi.useFakeTimers({ now: new Date('2027-01-04T00:00:00.000Z') })
1128
+ const tracker = Reconciliation.createTracker({
1129
+ db,
1130
+ dispatch: async () => undefined,
1131
+ leaseMs: 100,
1132
+ now: () => new Date(),
1133
+ providers: [Relay.relay({ apiKey: 'relay-api-key', baseUrl: server.url })],
1134
+ verifyTransfers: Evidence.createVerifier({ db }),
1135
+ })
1136
+
1137
+ const reconciliation = tracker.reconcile({
1138
+ addressId: address.id,
1139
+ type: 'funding:deposit-address:reconcile',
1140
+ })
1141
+ await providerStarted.promise
1142
+ await vi.advanceTimersByTimeAsync(60)
1143
+ const renewed = await FundingDepositAddresses.get(db, address.id)
1144
+ providerReleased.resolve()
1145
+ const result = await reconciliation
1146
+ const completed = await FundingDepositAddresses.get(db, address.id)
1147
+
1148
+ expect({ completed, renewed, result }).toMatchInlineSnapshot(
1149
+ {
1150
+ completed: {
1151
+ id: expect.any(String),
1152
+ providerOutputToken: expect.any(Object),
1153
+ snapshot: expect.any(Object),
1154
+ },
1155
+ renewed: {
1156
+ id: expect.any(String),
1157
+ providerOutputToken: expect.any(Object),
1158
+ snapshot: expect.any(Object),
1159
+ },
1160
+ },
1161
+ `
1162
+ {
1163
+ "completed": {
1164
+ "address": "0x1616161616161616161616161616161616161616",
1165
+ "apiKeyId": "key_lease_reconciliation",
1166
+ "createdAt": "2026-01-04T00:00:00.000Z",
1167
+ "deliveryStrategy": "provider",
1168
+ "destinationTokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1169
+ "environment": "production",
1170
+ "id": Any<String>,
1171
+ "lastPolledAt": "2027-01-04T00:00:00.060Z",
1172
+ "nextPollAt": "2027-01-04T00:01:00.060Z",
1173
+ "orgId": "org_lease_reconciliation",
1174
+ "pollFailureCount": 0,
1175
+ "pollLeaseUntil": null,
1176
+ "pollLeaseVersion": 1,
1177
+ "projectId": null,
1178
+ "providerId": "relay",
1179
+ "providerOutputToken": Any<Object>,
1180
+ "providerRequestIds": [],
1181
+ "providerState": {
1182
+ "reconciliation": {
1183
+ "pendingRequestIds": [],
1184
+ "requestIds": [],
1185
+ },
1186
+ },
1187
+ "recipient": "0x1111111111111111111111111111111111111111",
1188
+ "refundAddress": "0x1717171717171717171717171717171717171717",
1189
+ "snapshot": Any<Object>,
1190
+ "sourceChainId": "eip155:8453",
1191
+ "sourceTokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1192
+ "status": "active",
1193
+ "subsidize": false,
1194
+ "updatedAt": "2026-01-04T00:00:00.000Z",
1195
+ "version": 1,
1196
+ },
1197
+ "renewed": {
1198
+ "address": "0x1616161616161616161616161616161616161616",
1199
+ "apiKeyId": "key_lease_reconciliation",
1200
+ "createdAt": "2026-01-04T00:00:00.000Z",
1201
+ "deliveryStrategy": "provider",
1202
+ "destinationTokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1203
+ "environment": "production",
1204
+ "id": Any<String>,
1205
+ "lastPolledAt": null,
1206
+ "nextPollAt": "2026-01-04T00:00:00.000Z",
1207
+ "orgId": "org_lease_reconciliation",
1208
+ "pollFailureCount": 0,
1209
+ "pollLeaseUntil": "2027-01-04T00:00:00.150Z",
1210
+ "pollLeaseVersion": 1,
1211
+ "projectId": null,
1212
+ "providerId": "relay",
1213
+ "providerOutputToken": Any<Object>,
1214
+ "providerRequestIds": [],
1215
+ "providerState": null,
1216
+ "recipient": "0x1111111111111111111111111111111111111111",
1217
+ "refundAddress": "0x1717171717171717171717171717171717171717",
1218
+ "snapshot": Any<Object>,
1219
+ "sourceChainId": "eip155:8453",
1220
+ "sourceTokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1221
+ "status": "active",
1222
+ "subsidize": false,
1223
+ "updatedAt": "2026-01-04T00:00:00.000Z",
1224
+ "version": 1,
1225
+ },
1226
+ "result": {
1227
+ "created": 0,
1228
+ "type": "completed",
1229
+ "updated": 0,
1230
+ },
1231
+ }
1232
+ `,
1233
+ )
1234
+ } finally {
1235
+ providerReleased.resolve()
1236
+ vi.useRealTimers()
1237
+ await server.closeAsync()
1238
+ }
1239
+ })
1240
+
1241
+ test('waits for every source transaction before allocating provider output', async () => {
1242
+ const route = TestFunding.transferSnapshot()
1243
+ const depositAddress = `0x${'21'.repeat(20)}`
1244
+ const firstSourceTransactionHash = `0x${'31'.repeat(32)}`
1245
+ const secondSourceTransactionHash = `0x${'32'.repeat(32)}`
1246
+ const destinationTransactionHash = `0x${'33'.repeat(32)}`
1247
+ const address = await DepositAddress.create(db, {
1248
+ apiKeyId: 'key_source_evidence_reconciliation',
1249
+ deliveryStrategy: 'provider',
1250
+ environment: 'production',
1251
+ now: new Date('2026-01-05T00:00:00.000Z'),
1252
+ orgId: 'org_source_evidence_reconciliation',
1253
+ providerOutputToken: route.destinationToken,
1254
+ snapshot: {
1255
+ address: depositAddress,
1256
+ destinationChain: route.destinationChain,
1257
+ destinationToken: route.destinationToken,
1258
+ provider: { id: 'local', name: 'Local' },
1259
+ recipient: route.recipient,
1260
+ refundAddress: `0x${'22'.repeat(20)}`,
1261
+ sourceChain: route.sourceChain,
1262
+ sourceToken: route.sourceToken,
1263
+ subsidize: false,
1264
+ },
1265
+ })
1266
+ let destinationReceiptReads = 0
1267
+ const server = await TestRelay.createServer(async (request, response) => {
1268
+ const chunks: Uint8Array[] = []
1269
+ for await (const chunk of request) chunks.push(chunk)
1270
+ const body = JSON.parse(NodeBuffer.Buffer.concat(chunks).toString()) as {
1271
+ id: number
1272
+ method: string
1273
+ params?: readonly string[] | undefined
1274
+ }
1275
+ const hash = body.params?.[0]
1276
+ const result = (() => {
1277
+ if (body.method === 'eth_blockNumber') return '0x100'
1278
+ if (hash === secondSourceTransactionHash) return null
1279
+ const source = hash === firstSourceTransactionHash
1280
+ if (!source) destinationReceiptReads += 1
1281
+ return {
1282
+ blockNumber: '0x80',
1283
+ logs: [
1284
+ transferLog({
1285
+ amount: source ? 1_000_000n : 3_000_000n,
1286
+ recipient: source ? depositAddress : route.recipient,
1287
+ sender: `0x${'23'.repeat(20)}`,
1288
+ token: source ? route.sourceToken.address : route.destinationToken.address,
1289
+ }),
1290
+ ],
1291
+ status: '0x1',
1292
+ }
1293
+ })()
1294
+ response.writeHead(200, { 'content-type': 'application/json' })
1295
+ response.end(JSON.stringify({ id: body.id, jsonrpc: '2.0', result }))
1296
+ })
1297
+
1298
+ try {
1299
+ await FundingCatalog.publish(db, {
1300
+ chainTokens: [],
1301
+ chains: [
1302
+ {
1303
+ aliases: [],
1304
+ id: route.destinationChain.id as Chain.Id,
1305
+ name: route.destinationChain.name,
1306
+ parentChainId: null,
1307
+ rpcUrls: [server.url],
1308
+ slug: 'destination-source-evidence',
1309
+ },
1310
+ {
1311
+ aliases: [],
1312
+ id: route.sourceChain.id as Chain.Id,
1313
+ name: route.sourceChain.name,
1314
+ parentChainId: null,
1315
+ rpcUrls: [server.url],
1316
+ slug: 'source-source-evidence',
1317
+ },
1318
+ ],
1319
+ routes: [],
1320
+ tokens: [],
1321
+ })
1322
+ const result = await Reconciliation.reconcileRequest(db, {
1323
+ address,
1324
+ request: {
1325
+ createdAt: '2026-01-05T00:01:00.000Z',
1326
+ destinationTransactionHashes: [destinationTransactionHash],
1327
+ id: 'source-evidence-request',
1328
+ sourceTransactionHashes: [firstSourceTransactionHash, secondSourceTransactionHash],
1329
+ status: 'success',
1330
+ updatedAt: '2026-01-05T00:02:00.000Z',
1331
+ },
1332
+ verifyTransfers: Evidence.createVerifier({ db }),
1333
+ })
1334
+ const records = (
1335
+ await FundingDeposits.listByAddress(
1336
+ db,
1337
+ { environment: 'production', orgId: 'org_source_evidence_reconciliation' },
1338
+ address.id,
1339
+ { limit: 10 },
1340
+ )
1341
+ ).toSorted((left, right) =>
1342
+ left.sourceTransactionHash.localeCompare(right.sourceTransactionHash),
1343
+ )
1344
+
1345
+ expect({ destinationReceiptReads, records, result }).toMatchInlineSnapshot(
1346
+ {
1347
+ records: [
1348
+ {
1349
+ createdAt: expect.any(String),
1350
+ depositAddressId: expect.any(String),
1351
+ id: expect.any(String),
1352
+ snapshot: { depositAddressId: expect.any(String) },
1353
+ updatedAt: expect.any(String),
1354
+ },
1355
+ {
1356
+ createdAt: expect.any(String),
1357
+ depositAddressId: expect.any(String),
1358
+ id: expect.any(String),
1359
+ snapshot: { depositAddressId: expect.any(String) },
1360
+ updatedAt: expect.any(String),
1361
+ },
1362
+ ],
1363
+ },
1364
+ `
1365
+ {
1366
+ "destinationReceiptReads": 0,
1367
+ "records": [
1368
+ {
1369
+ "createdAt": Any<String>,
1370
+ "depositAddressId": Any<String>,
1371
+ "environment": "production",
1372
+ "id": Any<String>,
1373
+ "orgId": "org_source_evidence_reconciliation",
1374
+ "projectId": null,
1375
+ "providerOutputAmount": null,
1376
+ "providerOutputToken": {
1377
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1378
+ "currency": "USD",
1379
+ "decimals": 6,
1380
+ "name": "Bridged USDC (Stargate)",
1381
+ "standard": "TIP-20",
1382
+ "symbol": "USDC.e",
1383
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1384
+ "verified": true,
1385
+ },
1386
+ "providerRequestId": "source-evidence-request",
1387
+ "providerRequestIds": [
1388
+ "source-evidence-request",
1389
+ ],
1390
+ "providerState": null,
1391
+ "providerTransactionHashes": [],
1392
+ "providerTransferIndex": 0,
1393
+ "retryState": null,
1394
+ "settlementTransaction": null,
1395
+ "settlementTransactionHash": null,
1396
+ "snapshot": {
1397
+ "depositAddressId": Any<String>,
1398
+ "destinationChain": {
1399
+ "addressFormat": "hex",
1400
+ "id": "eip155:4217",
1401
+ "kind": "evm",
1402
+ "name": "Tempo",
1403
+ },
1404
+ "destinationToken": {
1405
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1406
+ "currency": "USD",
1407
+ "decimals": 6,
1408
+ "name": "Bridged USDC (Stargate)",
1409
+ "standard": "TIP-20",
1410
+ "symbol": "USDC.e",
1411
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1412
+ "verified": true,
1413
+ },
1414
+ "provider": {
1415
+ "id": "local",
1416
+ "name": "Local",
1417
+ },
1418
+ "recipient": "0x1111111111111111111111111111111111111111",
1419
+ "refundAddress": "0x2222222222222222222222222222222222222222",
1420
+ "sender": "0x2323232323232323232323232323232323232323",
1421
+ "sourceAmount": {
1422
+ "baseUnits": "1000000",
1423
+ "currency": "USD",
1424
+ "decimals": 6,
1425
+ "formatted": "1",
1426
+ },
1427
+ "sourceChain": {
1428
+ "addressFormat": "hex",
1429
+ "id": "eip155:8453",
1430
+ "kind": "evm",
1431
+ "name": "Base",
1432
+ },
1433
+ "sourceToken": {
1434
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1435
+ "currency": "USD",
1436
+ "decimals": 6,
1437
+ "name": "USD Coin",
1438
+ "standard": "ERC-20",
1439
+ "symbol": "USDC",
1440
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1441
+ "verified": true,
1442
+ },
1443
+ "sourceTransactionHashes": [
1444
+ "0x3131313131313131313131313131313131313131313131313131313131313131",
1445
+ ],
1446
+ },
1447
+ "sourceChainId": "eip155:8453",
1448
+ "sourceTransactionHash": "0x3131313131313131313131313131313131313131313131313131313131313131",
1449
+ "sourceTransferIndex": 0,
1450
+ "status": "bridging",
1451
+ "statusReason": null,
1452
+ "subsidyAmount": null,
1453
+ "tempoGasPaid": null,
1454
+ "updatedAt": Any<String>,
1455
+ "version": 2,
1456
+ },
1457
+ {
1458
+ "createdAt": Any<String>,
1459
+ "depositAddressId": Any<String>,
1460
+ "environment": "production",
1461
+ "id": Any<String>,
1462
+ "orgId": "org_source_evidence_reconciliation",
1463
+ "projectId": null,
1464
+ "providerOutputAmount": null,
1465
+ "providerOutputToken": {
1466
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1467
+ "currency": "USD",
1468
+ "decimals": 6,
1469
+ "name": "Bridged USDC (Stargate)",
1470
+ "standard": "TIP-20",
1471
+ "symbol": "USDC.e",
1472
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1473
+ "verified": true,
1474
+ },
1475
+ "providerRequestId": "source-evidence-request",
1476
+ "providerRequestIds": [
1477
+ "source-evidence-request",
1478
+ ],
1479
+ "providerState": null,
1480
+ "providerTransactionHashes": [],
1481
+ "providerTransferIndex": 0,
1482
+ "retryState": null,
1483
+ "settlementTransaction": null,
1484
+ "settlementTransactionHash": null,
1485
+ "snapshot": {
1486
+ "depositAddressId": Any<String>,
1487
+ "destinationChain": {
1488
+ "addressFormat": "hex",
1489
+ "id": "eip155:4217",
1490
+ "kind": "evm",
1491
+ "name": "Tempo",
1492
+ },
1493
+ "destinationToken": {
1494
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1495
+ "currency": "USD",
1496
+ "decimals": 6,
1497
+ "name": "Bridged USDC (Stargate)",
1498
+ "standard": "TIP-20",
1499
+ "symbol": "USDC.e",
1500
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1501
+ "verified": true,
1502
+ },
1503
+ "provider": {
1504
+ "id": "local",
1505
+ "name": "Local",
1506
+ },
1507
+ "recipient": "0x1111111111111111111111111111111111111111",
1508
+ "refundAddress": "0x2222222222222222222222222222222222222222",
1509
+ "sourceChain": {
1510
+ "addressFormat": "hex",
1511
+ "id": "eip155:8453",
1512
+ "kind": "evm",
1513
+ "name": "Base",
1514
+ },
1515
+ "sourceToken": {
1516
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1517
+ "currency": "USD",
1518
+ "decimals": 6,
1519
+ "name": "USD Coin",
1520
+ "standard": "ERC-20",
1521
+ "symbol": "USDC",
1522
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1523
+ "verified": true,
1524
+ },
1525
+ "sourceTransactionHashes": [
1526
+ "0x3232323232323232323232323232323232323232323232323232323232323232",
1527
+ ],
1528
+ },
1529
+ "sourceChainId": "eip155:8453",
1530
+ "sourceTransactionHash": "0x3232323232323232323232323232323232323232323232323232323232323232",
1531
+ "sourceTransferIndex": null,
1532
+ "status": "detected",
1533
+ "statusReason": null,
1534
+ "subsidyAmount": null,
1535
+ "tempoGasPaid": null,
1536
+ "updatedAt": Any<String>,
1537
+ "version": 1,
1538
+ },
1539
+ ],
1540
+ "result": {
1541
+ "created": 2,
1542
+ "terminal": false,
1543
+ "updated": 1,
1544
+ },
1545
+ }
1546
+ `,
1547
+ )
1548
+ } finally {
1549
+ await server.closeAsync()
1550
+ }
1551
+ })
1552
+
1553
+ test('records multiple source transfers with finer precision independently', async () => {
1554
+ const route = TestFunding.transferSnapshot()
1555
+ const sourceToken = { ...route.sourceToken, decimals: 18 }
1556
+ const depositAddress = `0x${'12'.repeat(20)}`
1557
+ const sourceTransactionHash = `0x${'ee'.repeat(32)}`
1558
+ const destinationTransactionHash = `0x${'ff'.repeat(32)}`
1559
+ const address = await DepositAddress.create(db, {
1560
+ apiKeyId: 'key_precision_reconciliation',
1561
+ deliveryStrategy: 'provider',
1562
+ environment: 'production',
1563
+ now: new Date('2026-01-03T00:00:00.000Z'),
1564
+ orgId: 'org_precision_reconciliation',
1565
+ providerOutputToken: route.destinationToken,
1566
+ providerRequestIds: ['relay-address-request'],
1567
+ snapshot: {
1568
+ address: depositAddress,
1569
+ destinationChain: route.destinationChain,
1570
+ destinationToken: route.destinationToken,
1571
+ provider: { id: 'relay', name: 'Relay' },
1572
+ recipient: route.recipient,
1573
+ refundAddress: `0x${'13'.repeat(20)}`,
1574
+ sourceChain: route.sourceChain,
1575
+ sourceToken,
1576
+ subsidize: false,
1577
+ },
1578
+ })
1579
+ const server = await TestRelay.createServer(async (request, response) => {
1580
+ if (request.method === 'GET') {
1581
+ response.writeHead(200, { 'content-type': 'application/json' })
1582
+ response.end(
1583
+ JSON.stringify({
1584
+ requests: [
1585
+ {
1586
+ createdAt: '2026-01-03T00:01:00.000Z',
1587
+ data: {
1588
+ inTxs: [{ hash: sourceTransactionHash }],
1589
+ outTxs: [{ hash: destinationTransactionHash }],
1590
+ },
1591
+ depositAddress: { depositTxHash: sourceTransactionHash },
1592
+ id: 'relay-multiple-transfer-request',
1593
+ status: 'success',
1594
+ updatedAt: '2026-01-03T00:02:00.000Z',
1595
+ },
1596
+ ],
1597
+ }),
1598
+ )
1599
+ return
1600
+ }
1601
+
1602
+ const chunks: Uint8Array[] = []
1603
+ for await (const chunk of request) chunks.push(chunk)
1604
+ const body = JSON.parse(NodeBuffer.Buffer.concat(chunks).toString()) as {
1605
+ id: number
1606
+ method: string
1607
+ params?: readonly string[] | undefined
1608
+ }
1609
+ const hash = body.params?.[0]
1610
+ const result =
1611
+ body.method === 'eth_blockNumber'
1612
+ ? '0x100'
1613
+ : {
1614
+ blockNumber: '0x80',
1615
+ logs:
1616
+ hash === sourceTransactionHash
1617
+ ? [
1618
+ transferLog({
1619
+ amount: 1_000_000_000_000_000_001n,
1620
+ logIndex: 0,
1621
+ recipient: depositAddress,
1622
+ sender: `0x${'14'.repeat(20)}`,
1623
+ token: sourceToken.address,
1624
+ }),
1625
+ transferLog({
1626
+ amount: 2_000_000_000_000_000_001n,
1627
+ logIndex: 1,
1628
+ recipient: depositAddress,
1629
+ sender: `0x${'14'.repeat(20)}`,
1630
+ token: sourceToken.address,
1631
+ }),
1632
+ ]
1633
+ : [
1634
+ transferLog({
1635
+ amount: 3_000_000n,
1636
+ logIndex: 0,
1637
+ recipient: route.recipient,
1638
+ sender: `0x${'15'.repeat(20)}`,
1639
+ token: route.destinationToken.address,
1640
+ }),
1641
+ ],
1642
+ status: '0x1',
1643
+ }
1644
+ response.writeHead(200, { 'content-type': 'application/json' })
1645
+ response.end(JSON.stringify({ id: body.id, jsonrpc: '2.0', result }))
1646
+ })
1647
+
1648
+ try {
1649
+ await FundingCatalog.publish(db, {
1650
+ chainTokens: [],
1651
+ chains: [
1652
+ {
1653
+ aliases: [],
1654
+ id: route.destinationChain.id as Chain.Id,
1655
+ name: route.destinationChain.name,
1656
+ parentChainId: null,
1657
+ rpcUrls: [server.url],
1658
+ slug: 'destination',
1659
+ },
1660
+ {
1661
+ aliases: [],
1662
+ id: route.sourceChain.id as Chain.Id,
1663
+ name: route.sourceChain.name,
1664
+ parentChainId: null,
1665
+ rpcUrls: [server.url],
1666
+ slug: 'source',
1667
+ },
1668
+ ],
1669
+ routes: [],
1670
+ tokens: [],
1671
+ })
1672
+ const tracker = Reconciliation.createTracker({
1673
+ db,
1674
+ dispatch: async () => undefined,
1675
+ now: () => new Date('2027-01-03T00:00:00.000Z'),
1676
+ providers: [Relay.relay({ apiKey: 'relay-api-key', baseUrl: server.url })],
1677
+ verifyTransfers: Evidence.createVerifier({ db }),
1678
+ })
1679
+
1680
+ const result = await tracker.reconcile({
1681
+ addressId: address.id,
1682
+ type: 'funding:deposit-address:reconcile',
1683
+ })
1684
+ const records = (
1685
+ await FundingDeposits.listByAddress(
1686
+ db,
1687
+ { environment: 'production', orgId: 'org_precision_reconciliation' },
1688
+ address.id,
1689
+ { limit: 10 },
1690
+ )
1691
+ ).toSorted(
1692
+ (left, right) => (right.sourceTransferIndex ?? -1) - (left.sourceTransferIndex ?? -1),
1693
+ )
1694
+ const storedAddress = await FundingDepositAddresses.get(db, address.id)
1695
+
1696
+ expect({ records, result, storedAddress }).toMatchInlineSnapshot(
1697
+ {
1698
+ records: [
1699
+ {
1700
+ createdAt: expect.any(String),
1701
+ depositAddressId: expect.any(String),
1702
+ id: expect.any(String),
1703
+ snapshot: { depositAddressId: expect.any(String) },
1704
+ updatedAt: expect.any(String),
1705
+ },
1706
+ {
1707
+ createdAt: expect.any(String),
1708
+ depositAddressId: expect.any(String),
1709
+ id: expect.any(String),
1710
+ snapshot: { depositAddressId: expect.any(String) },
1711
+ updatedAt: expect.any(String),
1712
+ },
1713
+ ],
1714
+ storedAddress: {
1715
+ id: expect.any(String),
1716
+ providerOutputToken: expect.any(Object),
1717
+ snapshot: expect.any(Object),
1718
+ },
1719
+ },
1720
+ `
1721
+ {
1722
+ "records": [
1723
+ {
1724
+ "createdAt": Any<String>,
1725
+ "depositAddressId": Any<String>,
1726
+ "environment": "production",
1727
+ "id": Any<String>,
1728
+ "orgId": "org_precision_reconciliation",
1729
+ "projectId": null,
1730
+ "providerOutputAmount": {
1731
+ "baseUnits": "2000000",
1732
+ "currency": "USD",
1733
+ "decimals": 6,
1734
+ "formatted": "2",
1735
+ },
1736
+ "providerOutputToken": {
1737
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1738
+ "currency": "USD",
1739
+ "decimals": 6,
1740
+ "name": "Bridged USDC (Stargate)",
1741
+ "standard": "TIP-20",
1742
+ "symbol": "USDC.e",
1743
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1744
+ "verified": true,
1745
+ },
1746
+ "providerRequestId": "relay-multiple-transfer-request",
1747
+ "providerRequestIds": [
1748
+ "relay-multiple-transfer-request",
1749
+ ],
1750
+ "providerState": null,
1751
+ "providerTransactionHashes": [
1752
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1753
+ ],
1754
+ "providerTransferIndex": 1,
1755
+ "retryState": null,
1756
+ "settlementTransaction": null,
1757
+ "settlementTransactionHash": null,
1758
+ "snapshot": {
1759
+ "depositAddressId": Any<String>,
1760
+ "destinationAmount": {
1761
+ "baseUnits": "2000000",
1762
+ "currency": "USD",
1763
+ "decimals": 6,
1764
+ "formatted": "2",
1765
+ },
1766
+ "destinationAmountRequired": {
1767
+ "baseUnits": "2000000",
1768
+ "currency": "USD",
1769
+ "decimals": 6,
1770
+ "formatted": "2",
1771
+ },
1772
+ "destinationChain": {
1773
+ "addressFormat": "hex",
1774
+ "id": "eip155:4217",
1775
+ "kind": "evm",
1776
+ "name": "Tempo",
1777
+ },
1778
+ "destinationToken": {
1779
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1780
+ "currency": "USD",
1781
+ "decimals": 6,
1782
+ "name": "Bridged USDC (Stargate)",
1783
+ "standard": "TIP-20",
1784
+ "symbol": "USDC.e",
1785
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1786
+ "verified": true,
1787
+ },
1788
+ "destinationTransactionHashes": [
1789
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1790
+ ],
1791
+ "provider": {
1792
+ "id": "relay",
1793
+ "name": "Relay",
1794
+ },
1795
+ "recipient": "0x1111111111111111111111111111111111111111",
1796
+ "refundAddress": "0x1313131313131313131313131313131313131313",
1797
+ "sender": "0x1414141414141414141414141414141414141414",
1798
+ "sourceAmount": {
1799
+ "baseUnits": "2000000000000000001",
1800
+ "currency": "USD",
1801
+ "decimals": 18,
1802
+ "formatted": "2.000000000000000001",
1803
+ },
1804
+ "sourceChain": {
1805
+ "addressFormat": "hex",
1806
+ "id": "eip155:8453",
1807
+ "kind": "evm",
1808
+ "name": "Base",
1809
+ },
1810
+ "sourceToken": {
1811
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1812
+ "currency": "USD",
1813
+ "decimals": 18,
1814
+ "name": "USD Coin",
1815
+ "standard": "ERC-20",
1816
+ "symbol": "USDC",
1817
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1818
+ "verified": true,
1819
+ },
1820
+ "sourceTransactionHashes": [
1821
+ "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
1822
+ ],
1823
+ },
1824
+ "sourceChainId": "eip155:8453",
1825
+ "sourceTransactionHash": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
1826
+ "sourceTransferIndex": 1,
1827
+ "status": "completed",
1828
+ "statusReason": null,
1829
+ "subsidyAmount": null,
1830
+ "tempoGasPaid": null,
1831
+ "updatedAt": Any<String>,
1832
+ "version": 3,
1833
+ },
1834
+ {
1835
+ "createdAt": Any<String>,
1836
+ "depositAddressId": Any<String>,
1837
+ "environment": "production",
1838
+ "id": Any<String>,
1839
+ "orgId": "org_precision_reconciliation",
1840
+ "projectId": null,
1841
+ "providerOutputAmount": {
1842
+ "baseUnits": "1000000",
1843
+ "currency": "USD",
1844
+ "decimals": 6,
1845
+ "formatted": "1",
1846
+ },
1847
+ "providerOutputToken": {
1848
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1849
+ "currency": "USD",
1850
+ "decimals": 6,
1851
+ "name": "Bridged USDC (Stargate)",
1852
+ "standard": "TIP-20",
1853
+ "symbol": "USDC.e",
1854
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1855
+ "verified": true,
1856
+ },
1857
+ "providerRequestId": "relay-multiple-transfer-request",
1858
+ "providerRequestIds": [
1859
+ "relay-multiple-transfer-request",
1860
+ ],
1861
+ "providerState": null,
1862
+ "providerTransactionHashes": [
1863
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1864
+ ],
1865
+ "providerTransferIndex": 0,
1866
+ "retryState": null,
1867
+ "settlementTransaction": null,
1868
+ "settlementTransactionHash": null,
1869
+ "snapshot": {
1870
+ "depositAddressId": Any<String>,
1871
+ "destinationAmount": {
1872
+ "baseUnits": "1000000",
1873
+ "currency": "USD",
1874
+ "decimals": 6,
1875
+ "formatted": "1",
1876
+ },
1877
+ "destinationAmountRequired": {
1878
+ "baseUnits": "1000000",
1879
+ "currency": "USD",
1880
+ "decimals": 6,
1881
+ "formatted": "1",
1882
+ },
1883
+ "destinationChain": {
1884
+ "addressFormat": "hex",
1885
+ "id": "eip155:4217",
1886
+ "kind": "evm",
1887
+ "name": "Tempo",
1888
+ },
1889
+ "destinationToken": {
1890
+ "address": "0x20c000000000000000000000b9537d11c60e8b50",
1891
+ "currency": "USD",
1892
+ "decimals": 6,
1893
+ "name": "Bridged USDC (Stargate)",
1894
+ "standard": "TIP-20",
1895
+ "symbol": "USDC.e",
1896
+ "tokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1897
+ "verified": true,
1898
+ },
1899
+ "destinationTransactionHashes": [
1900
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1901
+ ],
1902
+ "provider": {
1903
+ "id": "relay",
1904
+ "name": "Relay",
1905
+ },
1906
+ "recipient": "0x1111111111111111111111111111111111111111",
1907
+ "refundAddress": "0x1313131313131313131313131313131313131313",
1908
+ "sender": "0x1414141414141414141414141414141414141414",
1909
+ "sourceAmount": {
1910
+ "baseUnits": "1000000000000000001",
1911
+ "currency": "USD",
1912
+ "decimals": 18,
1913
+ "formatted": "1.000000000000000001",
1914
+ },
1915
+ "sourceChain": {
1916
+ "addressFormat": "hex",
1917
+ "id": "eip155:8453",
1918
+ "kind": "evm",
1919
+ "name": "Base",
1920
+ },
1921
+ "sourceToken": {
1922
+ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1923
+ "currency": "USD",
1924
+ "decimals": 18,
1925
+ "name": "USD Coin",
1926
+ "standard": "ERC-20",
1927
+ "symbol": "USDC",
1928
+ "tokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1929
+ "verified": true,
1930
+ },
1931
+ "sourceTransactionHashes": [
1932
+ "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
1933
+ ],
1934
+ },
1935
+ "sourceChainId": "eip155:8453",
1936
+ "sourceTransactionHash": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
1937
+ "sourceTransferIndex": 0,
1938
+ "status": "completed",
1939
+ "statusReason": null,
1940
+ "subsidyAmount": null,
1941
+ "tempoGasPaid": null,
1942
+ "updatedAt": Any<String>,
1943
+ "version": 3,
1944
+ },
1945
+ ],
1946
+ "result": {
1947
+ "created": 2,
1948
+ "type": "completed",
1949
+ "updated": 2,
1950
+ },
1951
+ "storedAddress": {
1952
+ "address": "0x1212121212121212121212121212121212121212",
1953
+ "apiKeyId": "key_precision_reconciliation",
1954
+ "createdAt": "2026-01-03T00:00:00.000Z",
1955
+ "deliveryStrategy": "provider",
1956
+ "destinationTokenKey": "eip155:4217/erc20:0x20c000000000000000000000b9537d11c60e8b50",
1957
+ "environment": "production",
1958
+ "id": Any<String>,
1959
+ "lastPolledAt": "2027-01-03T00:00:00.000Z",
1960
+ "nextPollAt": "2027-01-03T00:01:00.000Z",
1961
+ "orgId": "org_precision_reconciliation",
1962
+ "pollFailureCount": 0,
1963
+ "pollLeaseUntil": null,
1964
+ "pollLeaseVersion": 1,
1965
+ "projectId": null,
1966
+ "providerId": "relay",
1967
+ "providerOutputToken": Any<Object>,
1968
+ "providerRequestIds": [
1969
+ "relay-address-request",
1970
+ ],
1971
+ "providerState": {
1972
+ "reconciliation": {
1973
+ "pendingRequestIds": [],
1974
+ "requestIds": [
1975
+ "relay-multiple-transfer-request",
1976
+ ],
1977
+ "updatedAt": "2026-01-03T00:02:00.000Z",
1978
+ },
1979
+ },
1980
+ "recipient": "0x1111111111111111111111111111111111111111",
1981
+ "refundAddress": "0x1313131313131313131313131313131313131313",
1982
+ "snapshot": Any<Object>,
1983
+ "sourceChainId": "eip155:8453",
1984
+ "sourceTokenKey": "eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
1985
+ "status": "active",
1986
+ "subsidize": false,
1987
+ "updatedAt": "2026-01-03T00:00:00.000Z",
1988
+ "version": 1,
1989
+ },
1990
+ }
1991
+ `,
1992
+ )
1993
+ } finally {
1994
+ await server.closeAsync()
1995
+ }
1996
+ })
1997
+ })
1998
+
1999
+ function transferLog(options: transferLog.Options) {
2000
+ return {
2001
+ address: options.token,
2002
+ data: `0x${options.amount.toString(16).padStart(64, '0')}`,
2003
+ logIndex: `0x${(options.logIndex ?? 0).toString(16)}`,
2004
+ topics: [transferTopic, topicAddress(options.sender), topicAddress(options.recipient)],
2005
+ }
2006
+ }
2007
+
2008
+ declare namespace transferLog {
2009
+ type Options = {
2010
+ amount: bigint
2011
+ logIndex?: number | undefined
2012
+ recipient: string
2013
+ sender: string
2014
+ token: string
2015
+ }
2016
+ }
2017
+
2018
+ function topicAddress(address: string) {
2019
+ return `0x${address.slice(2).padStart(64, '0')}`
2020
+ }