tapimo 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/dist/App.d.ts +11333 -9563
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +39 -3
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +2 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +17 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/Webhooks.js +2 -2
  10. package/dist/Webhooks.js.map +1 -1
  11. package/dist/admin/App.d.ts +1064 -924
  12. package/dist/admin/App.d.ts.map +1 -1
  13. package/dist/admin/apps/api-keys.d.ts +80 -0
  14. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  15. package/dist/admin/apps/api-keys.js +54 -0
  16. package/dist/admin/apps/api-keys.js.map +1 -1
  17. package/dist/admin/apps/earn-vaults.d.ts +63 -15
  18. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  19. package/dist/admin/apps/earn-vaults.js +2 -1
  20. package/dist/admin/apps/earn-vaults.js.map +1 -1
  21. package/dist/admin/apps/organizations.d.ts +92 -3
  22. package/dist/admin/apps/organizations.d.ts.map +1 -1
  23. package/dist/admin/apps/organizations.js +69 -7
  24. package/dist/admin/apps/organizations.js.map +1 -1
  25. package/dist/admin/apps/verified-tokens.d.ts +7 -7
  26. package/dist/admin/ui.generated.d.ts +1 -1
  27. package/dist/admin/ui.generated.d.ts.map +1 -1
  28. package/dist/admin/ui.generated.js +151 -151
  29. package/dist/admin/ui.generated.js.map +1 -1
  30. package/dist/apps/Zones.d.ts +28 -28
  31. package/dist/apps/Zones.js +7 -7
  32. package/dist/apps/data/App.d.ts +2909 -2523
  33. package/dist/apps/data/App.d.ts.map +1 -1
  34. package/dist/apps/data/App.js +26 -21
  35. package/dist/apps/data/App.js.map +1 -1
  36. package/dist/apps/data/ZoneSelection.d.ts +8 -8
  37. package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
  38. package/dist/apps/data/ZoneSelection.js +1 -1
  39. package/dist/apps/data/ZoneSelection.js.map +1 -1
  40. package/dist/apps/data/routes/activities.d.ts +588 -584
  41. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  42. package/dist/apps/data/routes/activities.js +1561 -1239
  43. package/dist/apps/data/routes/activities.js.map +1 -1
  44. package/dist/apps/data/routes/addresses.d.ts +22 -22
  45. package/dist/apps/data/routes/balances.d.ts +26 -26
  46. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  47. package/dist/apps/data/routes/balances.js +43 -16
  48. package/dist/apps/data/routes/balances.js.map +1 -1
  49. package/dist/apps/data/routes/blocks.d.ts +44 -44
  50. package/dist/apps/data/routes/blocks.d.ts.map +1 -1
  51. package/dist/apps/data/routes/blocks.js +21 -11
  52. package/dist/apps/data/routes/blocks.js.map +1 -1
  53. package/dist/apps/data/routes/coingecko.d.ts +3 -3
  54. package/dist/apps/data/routes/earn.d.ts +201 -25
  55. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  56. package/dist/apps/data/routes/earn.js +434 -75
  57. package/dist/apps/data/routes/earn.js.map +1 -1
  58. package/dist/apps/data/routes/exchanges.d.ts +160 -145
  59. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  60. package/dist/apps/data/routes/exchanges.js +180 -114
  61. package/dist/apps/data/routes/exchanges.js.map +1 -1
  62. package/dist/apps/data/routes/fee-amm.d.ts +2 -2
  63. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  64. package/dist/apps/data/routes/fee-amm.js +18 -19
  65. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  66. package/dist/apps/data/routes/receipts.d.ts +155 -55
  67. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  68. package/dist/apps/data/routes/receipts.js +28 -10
  69. package/dist/apps/data/routes/receipts.js.map +1 -1
  70. package/dist/apps/data/routes/tokenlist.d.ts +13 -13
  71. package/dist/apps/data/routes/tokens.d.ts +5 -5
  72. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  73. package/dist/apps/data/routes/tokens.js +51 -20
  74. package/dist/apps/data/routes/tokens.js.map +1 -1
  75. package/dist/apps/data/routes/transactions.d.ts +7 -2
  76. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  77. package/dist/apps/data/routes/transactions.js +37 -17
  78. package/dist/apps/data/routes/transactions.js.map +1 -1
  79. package/dist/apps/data/routes/transfers.d.ts +13 -13
  80. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  81. package/dist/apps/data/routes/transfers.js +18 -12
  82. package/dist/apps/data/routes/transfers.js.map +1 -1
  83. package/dist/apps/data/routes/valuation.d.ts +31 -31
  84. package/dist/apps/data/routes/valuation.d.ts.map +1 -1
  85. package/dist/apps/data/routes/valuation.js +12 -7
  86. package/dist/apps/data/routes/valuation.js.map +1 -1
  87. package/dist/apps/data/routes/verified-tokens.d.ts +8 -8
  88. package/dist/apps/data/routes/webhooks.d.ts +816 -113
  89. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  90. package/dist/apps/data/routes/webhooks.js +47 -5
  91. package/dist/apps/data/routes/webhooks.js.map +1 -1
  92. package/dist/apps/data/routes/zones.d.ts +14 -14
  93. package/dist/apps/data/routes/zones.js +1 -1
  94. package/dist/apps/funding/App.d.ts +466 -44
  95. package/dist/apps/funding/App.d.ts.map +1 -1
  96. package/dist/apps/funding/App.js +1 -1
  97. package/dist/apps/funding/App.js.map +1 -1
  98. package/dist/apps/funding/routes/deposit-addresses.d.ts +17 -17
  99. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  100. package/dist/apps/funding/routes/deposit-addresses.js +17 -12
  101. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  102. package/dist/apps/funding/routes/transfers.d.ts +650 -57
  103. package/dist/apps/funding/routes/transfers.d.ts.map +1 -1
  104. package/dist/apps/funding/routes/transfers.js +238 -24
  105. package/dist/apps/funding/routes/transfers.js.map +1 -1
  106. package/dist/apps/management/App.d.ts +5120 -4152
  107. package/dist/apps/management/App.d.ts.map +1 -1
  108. package/dist/apps/management/Billing.d.ts +1 -1
  109. package/dist/apps/management/Billing.d.ts.map +1 -1
  110. package/dist/apps/management/Billing.js +17 -1
  111. package/dist/apps/management/Billing.js.map +1 -1
  112. package/dist/apps/management/routes/api-keys.d.ts +632 -81
  113. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  114. package/dist/apps/management/routes/api-keys.js +236 -4
  115. package/dist/apps/management/routes/api-keys.js.map +1 -1
  116. package/dist/apps/management/routes/billing.d.ts +76 -76
  117. package/dist/apps/management/routes/faucet.d.ts +6 -6
  118. package/dist/apps/management/routes/invitations.d.ts +24 -24
  119. package/dist/apps/management/routes/invite-links.d.ts +4 -4
  120. package/dist/apps/management/routes/members.d.ts +16 -16
  121. package/dist/apps/management/routes/orgs.d.ts +20 -20
  122. package/dist/apps/management/routes/projects.d.ts +4 -4
  123. package/dist/apps/management/routes/usage.d.ts +8 -8
  124. package/dist/apps/management/routes/verified-token-requests.d.ts +17 -17
  125. package/dist/apps/management/routes/webhooks.d.ts +675 -25
  126. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  127. package/dist/apps/management/routes/webhooks.js +21 -21
  128. package/dist/apps/management/routes/webhooks.js.map +1 -1
  129. package/dist/apps/relay/App.d.ts.map +1 -1
  130. package/dist/apps/relay/App.js +115 -23
  131. package/dist/apps/relay/App.js.map +1 -1
  132. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  133. package/dist/apps/relay/Sponsorships.js +27 -1
  134. package/dist/apps/relay/Sponsorships.js.map +1 -1
  135. package/dist/db/Db.d.ts.map +1 -1
  136. package/dist/db/Db.js +34 -1
  137. package/dist/db/Db.js.map +1 -1
  138. package/dist/db/Schema.d.ts +11 -5
  139. package/dist/db/Schema.d.ts.map +1 -1
  140. package/dist/db/Schema.js +21 -3
  141. package/dist/db/Schema.js.map +1 -1
  142. package/dist/db/tables/fundingCatalog.d.ts +4 -2
  143. package/dist/db/tables/fundingCatalog.d.ts.map +1 -1
  144. package/dist/db/tables/fundingCatalog.js +5 -1
  145. package/dist/db/tables/fundingCatalog.js.map +1 -1
  146. package/dist/db/tables/fundingTransferTransactions.d.ts +38 -0
  147. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -1
  148. package/dist/db/tables/fundingTransferTransactions.js +44 -3
  149. package/dist/db/tables/fundingTransferTransactions.js.map +1 -1
  150. package/dist/db/tables/fundingTransfers.d.ts +17 -0
  151. package/dist/db/tables/fundingTransfers.d.ts.map +1 -1
  152. package/dist/db/tables/fundingTransfers.js +21 -0
  153. package/dist/db/tables/fundingTransfers.js.map +1 -1
  154. package/dist/db/tables/organizations.d.ts +24 -0
  155. package/dist/db/tables/organizations.d.ts.map +1 -1
  156. package/dist/db/tables/organizations.js +27 -0
  157. package/dist/db/tables/organizations.js.map +1 -1
  158. package/dist/db/tables/projects.d.ts +25 -1
  159. package/dist/db/tables/projects.d.ts.map +1 -1
  160. package/dist/db/tables/projects.js +26 -0
  161. package/dist/db/tables/projects.js.map +1 -1
  162. package/dist/db/tables/sponsoredTransactions.d.ts +44 -0
  163. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  164. package/dist/db/tables/sponsoredTransactions.js +146 -13
  165. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  166. package/dist/db/tables/webhookSubscriptions.d.ts +10 -6
  167. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  168. package/dist/db/tables/webhookSubscriptions.js +9 -6
  169. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  170. package/dist/handlers/internal/utils.d.ts.map +1 -1
  171. package/dist/handlers/internal/utils.js +9 -0
  172. package/dist/handlers/internal/utils.js.map +1 -1
  173. package/dist/internal/Auth.d.ts +3 -1
  174. package/dist/internal/Auth.d.ts.map +1 -1
  175. package/dist/internal/Auth.js +6 -3
  176. package/dist/internal/Auth.js.map +1 -1
  177. package/dist/internal/EarnVaults.d.ts +3 -3
  178. package/dist/internal/EarnVaults.js +5 -5
  179. package/dist/internal/EarnVaults.js.map +1 -1
  180. package/dist/internal/Fetch.d.ts +23 -0
  181. package/dist/internal/Fetch.d.ts.map +1 -0
  182. package/dist/internal/Fetch.js +26 -0
  183. package/dist/internal/Fetch.js.map +1 -0
  184. package/dist/internal/Log.d.ts +4 -2
  185. package/dist/internal/Log.d.ts.map +1 -1
  186. package/dist/internal/Log.js +16 -3
  187. package/dist/internal/Log.js.map +1 -1
  188. package/dist/internal/OpenApi.d.ts +5 -3
  189. package/dist/internal/OpenApi.d.ts.map +1 -1
  190. package/dist/internal/OpenApi.js +13 -3
  191. package/dist/internal/OpenApi.js.map +1 -1
  192. package/dist/internal/Response.d.ts +1 -1
  193. package/dist/internal/Response.d.ts.map +1 -1
  194. package/dist/internal/Schema.d.ts +6 -3
  195. package/dist/internal/Schema.d.ts.map +1 -1
  196. package/dist/internal/Schema.js +8 -2
  197. package/dist/internal/Schema.js.map +1 -1
  198. package/dist/internal/Store.d.ts +16 -3
  199. package/dist/internal/Store.d.ts.map +1 -1
  200. package/dist/internal/Store.js +82 -16
  201. package/dist/internal/Store.js.map +1 -1
  202. package/dist/internal/Tidx.d.ts.map +1 -1
  203. package/dist/internal/Tidx.js +7 -2
  204. package/dist/internal/Tidx.js.map +1 -1
  205. package/dist/internal/Timeout.d.ts +36 -0
  206. package/dist/internal/Timeout.d.ts.map +1 -0
  207. package/dist/internal/Timeout.js +37 -0
  208. package/dist/internal/Timeout.js.map +1 -0
  209. package/dist/internal/Timing.d.ts +9 -0
  210. package/dist/internal/Timing.d.ts.map +1 -1
  211. package/dist/internal/Timing.js +7 -0
  212. package/dist/internal/Timing.js.map +1 -1
  213. package/dist/internal/Viem.d.ts +16 -1
  214. package/dist/internal/Viem.d.ts.map +1 -1
  215. package/dist/internal/Viem.js +12 -2
  216. package/dist/internal/Viem.js.map +1 -1
  217. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  218. package/dist/internal/WebhookDestination.js +3 -0
  219. package/dist/internal/WebhookDestination.js.map +1 -1
  220. package/dist/internal/funding/Action.d.ts +9 -0
  221. package/dist/internal/funding/Action.d.ts.map +1 -1
  222. package/dist/internal/funding/Action.js +16 -8
  223. package/dist/internal/funding/Action.js.map +1 -1
  224. package/dist/internal/funding/Catalog.d.ts +9 -2
  225. package/dist/internal/funding/Catalog.d.ts.map +1 -1
  226. package/dist/internal/funding/Catalog.js +19 -8
  227. package/dist/internal/funding/Catalog.js.map +1 -1
  228. package/dist/internal/funding/DepositWebhook.d.ts.map +1 -1
  229. package/dist/internal/funding/DepositWebhook.js +2 -1
  230. package/dist/internal/funding/DepositWebhook.js.map +1 -1
  231. package/dist/internal/funding/Evidence.d.ts.map +1 -1
  232. package/dist/internal/funding/Evidence.js +2 -10
  233. package/dist/internal/funding/Evidence.js.map +1 -1
  234. package/dist/internal/funding/Provider.d.ts +108 -13
  235. package/dist/internal/funding/Provider.d.ts.map +1 -1
  236. package/dist/internal/funding/Provider.js +34 -8
  237. package/dist/internal/funding/Provider.js.map +1 -1
  238. package/dist/internal/funding/SourceObservation.d.ts.map +1 -1
  239. package/dist/internal/funding/SourceObservation.js +5 -7
  240. package/dist/internal/funding/SourceObservation.js.map +1 -1
  241. package/dist/internal/funding/Transfer.d.ts +114 -18
  242. package/dist/internal/funding/Transfer.d.ts.map +1 -1
  243. package/dist/internal/funding/Transfer.js +185 -55
  244. package/dist/internal/funding/Transfer.js.map +1 -1
  245. package/dist/internal/funding/TransferReconciliation.d.ts +52 -0
  246. package/dist/internal/funding/TransferReconciliation.d.ts.map +1 -0
  247. package/dist/internal/funding/TransferReconciliation.js +192 -0
  248. package/dist/internal/funding/TransferReconciliation.js.map +1 -0
  249. package/dist/internal/funding/TransferWebhook.d.ts +21 -0
  250. package/dist/internal/funding/TransferWebhook.d.ts.map +1 -0
  251. package/dist/internal/funding/TransferWebhook.js +43 -0
  252. package/dist/internal/funding/TransferWebhook.js.map +1 -0
  253. package/dist/internal/funding/index.d.ts +1 -0
  254. package/dist/internal/funding/index.d.ts.map +1 -1
  255. package/dist/internal/funding/index.js +1 -0
  256. package/dist/internal/funding/index.js.map +1 -1
  257. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  258. package/dist/internal/funding/providers/relay.js +3 -12
  259. package/dist/internal/funding/providers/relay.js.map +1 -1
  260. package/dist/internal/funding/providers/stargate.d.ts +127 -9
  261. package/dist/internal/funding/providers/stargate.d.ts.map +1 -1
  262. package/dist/internal/funding/providers/stargate.js +548 -112
  263. package/dist/internal/funding/providers/stargate.js.map +1 -1
  264. package/dist/internal/index.d.ts +1 -0
  265. package/dist/internal/index.d.ts.map +1 -1
  266. package/dist/internal/index.js +1 -0
  267. package/dist/internal/index.js.map +1 -1
  268. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
  269. package/dist/internal/webhookDestinations/slack.js +16 -0
  270. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  271. package/package.json +2 -2
  272. package/src/App.test.ts +100 -0
  273. package/src/App.ts +43 -3
  274. package/src/Client.test-d.ts +129 -2
  275. package/src/Scope.test.ts +20 -0
  276. package/src/Scope.ts +16 -0
  277. package/src/Webhooks.ts +2 -2
  278. package/src/admin/apps/api-keys.test.ts +75 -0
  279. package/src/admin/apps/api-keys.ts +64 -0
  280. package/src/admin/apps/earn-vaults.test.ts +1 -1
  281. package/src/admin/apps/earn-vaults.ts +2 -1
  282. package/src/admin/apps/organizations.test.ts +53 -1
  283. package/src/admin/apps/organizations.ts +90 -5
  284. package/src/admin/ui/src/lib/api-keys.ts +19 -0
  285. package/src/admin/ui/src/lib/organizations.test.ts +112 -1
  286. package/src/admin/ui/src/lib/organizations.ts +55 -0
  287. package/src/admin/ui/src/routes/api-keys.tsx +89 -2
  288. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +116 -0
  289. package/src/admin/ui.generated.ts +189 -151
  290. package/src/apps/Zones.test.ts +7 -7
  291. package/src/apps/Zones.ts +7 -7
  292. package/src/apps/composition.test.ts +2 -1
  293. package/src/apps/data/App.test.ts +41 -0
  294. package/src/apps/data/App.ts +30 -21
  295. package/src/apps/data/ZoneSelection.test.ts +2 -2
  296. package/src/apps/data/ZoneSelection.ts +8 -8
  297. package/src/apps/data/routes/activities.test.ts +1526 -12
  298. package/src/apps/data/routes/activities.ts +1774 -1356
  299. package/src/apps/data/routes/balances.ts +133 -97
  300. package/src/apps/data/routes/blocks.test.ts +533 -0
  301. package/src/apps/data/routes/blocks.ts +35 -16
  302. package/src/apps/data/routes/earn.test.ts +373 -16
  303. package/src/apps/data/routes/earn.ts +548 -79
  304. package/src/apps/data/routes/earn.zone-access.test.ts +6 -6
  305. package/src/apps/data/routes/exchanges.test.ts +740 -15
  306. package/src/apps/data/routes/exchanges.ts +954 -753
  307. package/src/apps/data/routes/fee-amm.ts +17 -18
  308. package/src/apps/data/routes/receipts.ts +47 -16
  309. package/src/apps/data/routes/tokens.test.ts +578 -0
  310. package/src/apps/data/routes/tokens.ts +283 -225
  311. package/src/apps/data/routes/transactions.ts +68 -29
  312. package/src/apps/data/routes/transfers.test.ts +421 -0
  313. package/src/apps/data/routes/transfers.ts +45 -27
  314. package/src/apps/data/routes/valuation.ts +94 -78
  315. package/src/apps/data/routes/webhooks.test.ts +21 -5
  316. package/src/apps/data/routes/webhooks.ts +63 -8
  317. package/src/apps/data/routes/zones.test.ts +2 -2
  318. package/src/apps/data/routes/zones.ts +1 -1
  319. package/src/apps/funding/App.ts +12 -2
  320. package/src/apps/funding/routes/deposit-addresses.test.ts +29 -7
  321. package/src/apps/funding/routes/deposit-addresses.ts +19 -14
  322. package/src/apps/funding/routes/quotes.test.ts +5 -9
  323. package/src/apps/funding/routes/transfers.test.ts +901 -23
  324. package/src/apps/funding/routes/transfers.ts +347 -64
  325. package/src/apps/management/Billing.ts +18 -5
  326. package/src/apps/management/routes/api-keys.test.ts +220 -0
  327. package/src/apps/management/routes/api-keys.ts +280 -3
  328. package/src/apps/management/routes/webhooks.test.ts +3 -3
  329. package/src/apps/management/routes/webhooks.ts +24 -25
  330. package/src/apps/relay/App.test.ts +212 -1
  331. package/src/apps/relay/App.ts +158 -29
  332. package/src/apps/relay/Sponsorships.ts +24 -1
  333. package/src/db/Db.ts +48 -1
  334. package/src/db/Schema.ts +22 -4
  335. package/src/db/tables/earnVaults.test.ts +9 -9
  336. package/src/db/tables/fundingCatalog.test.ts +16 -2
  337. package/src/db/tables/fundingCatalog.ts +9 -3
  338. package/src/db/tables/fundingTransferTransactions.test.ts +43 -0
  339. package/src/db/tables/fundingTransferTransactions.ts +79 -3
  340. package/src/db/tables/fundingTransfers.test.ts +26 -0
  341. package/src/db/tables/fundingTransfers.ts +36 -0
  342. package/src/db/tables/organizations.test.ts +41 -0
  343. package/src/db/tables/organizations.ts +49 -0
  344. package/src/db/tables/projects.test.ts +55 -0
  345. package/src/db/tables/projects.ts +46 -1
  346. package/src/db/tables/sponsoredTransactions.test.ts +338 -0
  347. package/src/db/tables/sponsoredTransactions.ts +197 -13
  348. package/src/db/tables/webhookSubscriptions.ts +18 -11
  349. package/src/handlers/internal/utils.test.ts +53 -0
  350. package/src/handlers/internal/utils.ts +15 -0
  351. package/src/internal/Auth.ts +12 -4
  352. package/src/internal/EarnVaults.capabilities.test.ts +2 -2
  353. package/src/internal/EarnVaults.test.ts +1 -1
  354. package/src/internal/EarnVaults.ts +5 -5
  355. package/src/internal/Fetch.test.ts +46 -0
  356. package/src/internal/Fetch.ts +56 -0
  357. package/src/internal/Log.test-d.ts +2 -0
  358. package/src/internal/Log.test.ts +21 -0
  359. package/src/internal/Log.ts +22 -5
  360. package/src/internal/OpenApi.ts +14 -4
  361. package/src/internal/Response.ts +1 -1
  362. package/src/internal/Schema.test.ts +2 -0
  363. package/src/internal/Schema.ts +12 -2
  364. package/src/internal/Store.test.ts +178 -0
  365. package/src/internal/Store.ts +104 -18
  366. package/src/internal/Tidx.test.ts +2 -1
  367. package/src/internal/Tidx.ts +7 -2
  368. package/src/internal/Timeout.test.ts +35 -0
  369. package/src/internal/Timeout.ts +73 -0
  370. package/src/internal/Timing.ts +16 -0
  371. package/src/internal/Viem.ts +39 -3
  372. package/src/internal/WebhookDestination.ts +3 -0
  373. package/src/internal/Webhooks.test.ts +45 -0
  374. package/src/internal/funding/Action.test.ts +41 -1
  375. package/src/internal/funding/Action.ts +69 -41
  376. package/src/internal/funding/Catalog.test.ts +34 -0
  377. package/src/internal/funding/Catalog.ts +28 -11
  378. package/src/internal/funding/DepositWebhook.ts +2 -1
  379. package/src/internal/funding/Evidence.ts +2 -13
  380. package/src/internal/funding/Provider.test.ts +73 -0
  381. package/src/internal/funding/Provider.ts +125 -28
  382. package/src/internal/funding/SourceObservation.ts +5 -9
  383. package/src/internal/funding/Transfer.test.ts +155 -0
  384. package/src/internal/funding/Transfer.ts +387 -147
  385. package/src/internal/funding/TransferReconciliation.test.ts +147 -0
  386. package/src/internal/funding/TransferReconciliation.ts +264 -0
  387. package/src/internal/funding/TransferWebhook.test.ts +128 -0
  388. package/src/internal/funding/TransferWebhook.ts +67 -0
  389. package/src/internal/funding/index.test.ts +1 -0
  390. package/src/internal/funding/index.ts +1 -0
  391. package/src/internal/funding/providers/index.test.ts +4 -167
  392. package/src/internal/funding/providers/relay.test.ts +2 -2
  393. package/src/internal/funding/providers/relay.ts +3 -15
  394. package/src/internal/funding/providers/stargate.test.ts +328 -0
  395. package/src/internal/funding/providers/stargate.ts +807 -135
  396. package/src/internal/index.test.ts +1 -0
  397. package/src/internal/index.ts +1 -0
  398. package/src/internal/webhookDestinations/slack.ts +15 -0
@@ -1,59 +1,101 @@
1
- import { Address } from 'ox'
1
+ import { AbiEvent, AbiFunction, Address, Hash, Hex } from 'ox'
2
+ import {
3
+ BlockNotFoundError,
4
+ parseEventLogs,
5
+ publicActions,
6
+ TransactionReceiptNotFoundError,
7
+ } from 'viem'
2
8
  import * as z from 'zod/mini'
3
9
 
10
+ import * as Viem from '../../Viem.js'
11
+ import * as Action from '../Action.js'
12
+ import type * as Catalog from '../Catalog.js'
13
+ import * as Chain from '../Chain.js'
4
14
  import * as FundingProvider from '../Provider.js'
5
15
 
6
- const defaultBaseUrl = 'https://transfer.layerzero-api.com/v1'
16
+ const approve = AbiFunction.from('function approve(address spender, uint256 amount)')
7
17
  const defaultFetch: FundingProvider.Fetch = (input, init) => globalThis.fetch(input, init)
8
- const sourceDetails = {
9
- STARGATE_V2_BUS: 'stargate:value-transfer-v1:bus',
10
- STARGATE_V2_TAXI: 'stargate:value-transfer-v1:taxi',
18
+ const destinationChainId = 'eip155:4217'
19
+ const destinationEid = 30_410
20
+ const destinationPool = '0x8c76e2f6c5ceda9aa7772e7eff30280226c44392'
21
+ const destinationToken = '0x20C000000000000000000000b9537d11c60E8b50'
22
+ const layerZeroEndpoint = '0x1a44076050125825900e736c501f859c50fE728c'
23
+ const oftReceived = AbiEvent.from(
24
+ 'event OFTReceived(bytes32 indexed guid, uint32 srcEid, address indexed toAddress, uint256 amountReceivedLD)',
25
+ )
26
+ const oftSent = AbiEvent.from(
27
+ 'event OFTSent(bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD)',
28
+ )
29
+ const packetSent = AbiEvent.from(
30
+ 'event PacketSent(bytes encodedPayload, bytes options, address sendLibrary)',
31
+ )
32
+ const quoteOft = AbiFunction.from(
33
+ 'function quoteOFT((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam) view returns ((uint256 minAmountLD, uint256 maxAmountLD) limit, (int256 feeAmountLD, string description)[] oftFeeDetails, (uint256 amountSentLD, uint256 amountReceivedLD) receipt)',
34
+ )
35
+ const quoteSend = AbiFunction.from(
36
+ 'function quoteSend((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam, bool payInLzToken) view returns ((uint256 nativeFee, uint256 lzTokenFee) fee)',
37
+ )
38
+ const sendToken = AbiFunction.from(
39
+ 'function sendToken((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam, (uint256 nativeFee, uint256 lzTokenFee) fee, address refundAddress) payable returns ((bytes32 guid, uint64 nonce, (uint256 nativeFee, uint256 lzTokenFee) fee) msgReceipt, (uint256 amountSentLD, uint256 amountReceivedLD) oftReceipt, (uint72 ticketId, bytes passengerBytes) ticket)',
40
+ )
41
+ const quoteTtlMs = 60_000
42
+ const maximumLogBlockRange = 100_000n
43
+ const sourceEids = {
44
+ 'eip155:1': 30_101,
45
+ 'eip155:8453': 30_184,
11
46
  } as const
47
+ const transferEvent = AbiEvent.from(
48
+ 'event Transfer(address indexed from, address indexed to, uint256 value)',
49
+ )
50
+ const zeroAddress = '0x0000000000000000000000000000000000000000'
12
51
 
13
- namespace schema {
14
- const DecimalString = z.string().check(z.regex(/^\d+(\.\d+)?$/))
15
- const IntegerString = z.string().check(z.regex(/^\d+$/))
16
-
17
- const Quote = z.object({
18
- dstAmount: IntegerString,
19
- dstAmountMin: IntegerString,
20
- duration: z.optional(
21
- z.object({
22
- estimated: z.nullable(DecimalString),
23
- }),
24
- ),
25
- expiresAt: z.optional(z.iso.datetime()),
26
- id: z.string().check(z.minLength(1)),
27
- routeSteps: z
28
- .array(
29
- z.object({
30
- srcChainKey: z.string(),
31
- type: z.string(),
32
- }),
33
- )
34
- .check(z.minLength(1)),
35
- srcAmount: IntegerString,
36
- })
37
-
38
- const Error = z.object({
39
- message: z.string(),
40
- status: z.union([z.string(), z.number()]),
41
- })
52
+ /** Stargate provider schemas. */
53
+ export namespace schema {
54
+ /** Configuration captured from one selected Stargate route. */
55
+ export const Configuration = z
56
+ .strictObject({
57
+ destinationPoolAddress: z.optional(
58
+ z
59
+ .string()
60
+ .check(
61
+ z.regex(/^0x[0-9a-fA-F]{40}$/),
62
+ z.describe('Stargate pool contract on the Tempo destination chain.'),
63
+ ),
64
+ ),
65
+ poolAddress: z
66
+ .string()
67
+ .check(
68
+ z.regex(/^0x[0-9a-fA-F]{40}$/),
69
+ z.describe('Stargate pool contract on the source chain.'),
70
+ ),
71
+ sourceEid: z.optional(
72
+ z
73
+ .number()
74
+ .check(z.int(), z.positive(), z.describe('LayerZero endpoint ID for the source chain.')),
75
+ ),
76
+ })
77
+ .check(z.describe('Stargate configuration for one funding route.'))
78
+ }
42
79
 
43
- export const Quotes = z.object({
44
- error: z.optional(z.nullable(Error)),
45
- quotes: z.array(Quote),
46
- })
80
+ type Deployment = {
81
+ destinationPool: Address.Address
82
+ pool: Address.Address
83
+ sourceEid: number
84
+ sourceToken: Address.Address
47
85
  }
48
86
 
49
- /** Creates a Stargate funding provider backed by LayerZero's `/quotes` endpoint. */
50
- export function stargate(options: stargate.Options) {
51
- if (options.apiKey.trim().length === 0) throw new FundingProvider.ProviderConfigurationError()
52
- if (!Address.validate(options.userAddress, { strict: false }))
53
- throw new FundingProvider.ProviderConfigurationError()
87
+ type SendParam = {
88
+ amountLD: bigint
89
+ composeMsg: Hex.Hex
90
+ dstEid: number
91
+ extraOptions: Hex.Hex
92
+ minAmountLD: bigint
93
+ oftCmd: Hex.Hex
94
+ to: Hex.Hex
95
+ }
54
96
 
55
- const endpoint = new URL(options.baseUrl ?? defaultBaseUrl)
56
- endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, '')}/quotes`
97
+ /** Creates a direct Stargate V2 OFT funding provider. */
98
+ export function stargate(options: stargate.Options = {}) {
57
99
  const fetch = options.fetch ?? defaultFetch
58
100
  const now = options.now ?? (() => new Date())
59
101
 
@@ -61,52 +103,205 @@ export function stargate(options: stargate.Options) {
61
103
  id: 'stargate',
62
104
  name: 'Stargate',
63
105
  async getQuote({ candidate, route }, signal) {
64
- const response = await FundingProvider.requestJson(endpoint, {
65
- body: {
66
- amount: candidate.sourceAmount.amount,
67
- dstChainKey: route.destination.chain.slug,
68
- dstTokenAddress: candidate.destinationToken.address,
69
- dstWalletAddress: options.userAddress,
70
- options: { amountType: 'EXACT_SRC_AMOUNT' },
71
- srcChainKey: route.source.chain.slug,
72
- srcTokenAddress: candidate.sourceToken.address,
73
- srcWalletAddress: options.userAddress,
74
- },
106
+ const deployment = resolveDeployment({
107
+ configuration: candidate.configuration,
108
+ destinationChainId: route.destination.chain.id,
109
+ destinationTokenAddress: route.destination.address,
110
+ sourceChainId: route.source.chain.id,
111
+ sourceTokenAddress: route.source.address,
112
+ })
113
+ const sampledAt = now()
114
+ if (!deployment) return unavailable({ message: 'ROUTE_NOT_CONFIGURED', now: sampledAt })
115
+
116
+ const sendParam = createSendParam({
117
+ amount: candidate.sourceAmount.amount,
118
+ recipient: zeroAddress,
119
+ })
120
+ const [limit, feeDetails, receipt] = await rpcClient({
75
121
  fetch,
76
- headers: { 'user-agent': 'tempo-api', 'x-api-key': options.apiKey },
77
- method: 'POST',
78
122
  signal,
123
+ urls: route.source.chain.rpcUrls,
124
+ }).readContract({
125
+ abi: [quoteOft],
126
+ address: deployment.pool,
127
+ args: [sendParam],
128
+ functionName: 'quoteOFT',
79
129
  })
80
- const parsed = schema.Quotes.safeParse(response)
81
- if (!parsed.success || parsed.data.error) throw new FundingProvider.ProviderPayloadError()
130
+ if (!isExactQuote({ amount: candidate.sourceAmount.amount, feeDetails, limit, receipt }))
131
+ return unavailable({ message: 'NO_EXACT_ROUTE', now: sampledAt })
82
132
 
83
- const sampledAt = now()
84
- const selected = selectQuote(parsed.data.quotes, {
85
- amount: candidate.sourceAmount.amount,
86
- now: sampledAt,
87
- sourceChainKey: route.source.chain.slug,
133
+ return {
134
+ destinationAmountMin: receipt.amountReceivedLD.toString(),
135
+ destinationAmount: receipt.amountReceivedLD.toString(),
136
+ quality: quality(),
137
+ sampledAt: sampledAt.toISOString(),
138
+ status: 'available',
139
+ }
140
+ },
141
+ async prepareTransfer({ candidate, method, mode, recipient, sender }, signal) {
142
+ if (
143
+ method !== 'transaction' ||
144
+ mode !== 'exactSource' ||
145
+ !Address.validate(recipient, { strict: false }) ||
146
+ !Address.validate(sender, { strict: false })
147
+ )
148
+ throw new FundingProvider.ProviderConfigurationError()
149
+
150
+ const deployment = resolveDeployment({
151
+ configuration: candidate.configuration,
152
+ destinationChainId: candidate.destinationChain.id,
153
+ destinationTokenAddress: candidate.destinationToken.address,
154
+ sourceChainId: candidate.sourceChain.id,
155
+ sourceTokenAddress: candidate.sourceToken.address,
156
+ })
157
+ if (!deployment) throw new FundingProvider.ProviderUnavailableError()
158
+ const sendParam = createSendParam({ amount: candidate.sourceAmount.amount, recipient })
159
+ const rpc = rpcClient({ fetch, signal, urls: candidate.route.source.chain.rpcUrls })
160
+ const [[limit, feeDetails, receipt], messagingFee, destinationBlock] = await Promise.all([
161
+ rpc.readContract({
162
+ abi: [quoteOft],
163
+ address: deployment.pool,
164
+ args: [sendParam],
165
+ functionName: 'quoteOFT',
166
+ }),
167
+ rpc.readContract({
168
+ abi: [quoteSend],
169
+ account: sender as Address.Address,
170
+ address: deployment.pool,
171
+ args: [sendParam, false],
172
+ functionName: 'quoteSend',
173
+ }),
174
+ rpcClient({
175
+ fetch,
176
+ signal,
177
+ urls: candidate.route.destination.chain.rpcUrls,
178
+ }).getBlock({ blockTag: 'finalized', includeTransactions: false }),
179
+ ])
180
+ if (!isExactQuote({ amount: candidate.sourceAmount.amount, feeDetails, limit, receipt }))
181
+ throw new FundingProvider.ProviderUnavailableError()
182
+ if (messagingFee.lzTokenFee !== 0n) throw new FundingProvider.ProviderPayloadError()
183
+
184
+ const action = {
185
+ calls: [
186
+ {
187
+ data: AbiFunction.encodeData(approve, [
188
+ deployment.pool,
189
+ BigInt(candidate.sourceAmount.amount),
190
+ ]),
191
+ to: deployment.sourceToken,
192
+ value: '0x0',
193
+ },
194
+ {
195
+ data: AbiFunction.encodeData(sendToken, [sendParam, messagingFee, sender]),
196
+ to: deployment.pool,
197
+ value: Hex.fromNumber(messagingFee.nativeFee),
198
+ },
199
+ ],
200
+ type: 'evm:calls' as const,
201
+ }
202
+ validateAction({
203
+ action,
204
+ configuration: candidate.configuration,
205
+ destinationTokenAddress: candidate.destinationToken.address,
206
+ recipient,
207
+ sender,
208
+ sourceAmount: candidate.sourceAmount.amount,
209
+ sourceChainId: candidate.sourceChain.id,
210
+ sourceTokenAddress: candidate.sourceToken.address,
88
211
  })
89
- if (!selected)
90
- return FundingProvider.unavailableResult({
91
- detail: 'stargate:value-transfer-v1',
92
- message: 'NO_STARGATE_ROUTE',
93
- now: sampledAt,
94
- source: 'providerQuote',
95
- })
96
-
97
- const estimated = estimatedSeconds(selected.quote.duration?.estimated)
212
+
213
+ const sampledAt = now()
98
214
  return {
99
- ...(selected.quote.expiresAt ? { expiresAt: selected.quote.expiresAt } : {}),
100
- destinationAmountMin: selected.quote.dstAmountMin,
101
- destinationAmount: selected.quote.dstAmount,
102
- quality: {
103
- ...(estimated === undefined ? {} : { estimatedSeconds: estimated }),
104
- liquiditySource: 'providerQuote',
105
- sourceDetail: sourceDetails[selected.type],
106
- tier: 'liquid',
215
+ action,
216
+ destinationAmountMin: receipt.amountReceivedLD.toString(),
217
+ destinationAmount: receipt.amountReceivedLD.toString(),
218
+ expiresAt: new Date(sampledAt.getTime() + quoteTtlMs).toISOString(),
219
+ fees:
220
+ messagingFee.nativeFee === 0n
221
+ ? []
222
+ : [
223
+ {
224
+ amount: messagingFee.nativeFee.toString(),
225
+ side: 'source' as const,
226
+ token: nativeToken(candidate.sourceChain.id),
227
+ },
228
+ ],
229
+ providerState: {
230
+ destinationBlockNumber: destinationBlock.number.toString(),
231
+ routeConfiguration: candidate.configuration,
107
232
  },
233
+ quality: quality(),
108
234
  sampledAt: sampledAt.toISOString(),
109
- status: 'available',
235
+ }
236
+ },
237
+ validateTransferAction({ action, candidate, recipient, sender }) {
238
+ return {
239
+ nativeValues: validateAction({
240
+ action,
241
+ configuration: candidate.configuration,
242
+ destinationTokenAddress: candidate.destinationToken.address,
243
+ recipient,
244
+ sender,
245
+ sourceAmount: candidate.sourceAmount.amount,
246
+ sourceChainId: candidate.sourceChain.id,
247
+ sourceTokenAddress: candidate.sourceToken.address,
248
+ }),
249
+ }
250
+ },
251
+ async verifySourceTransaction(
252
+ {
253
+ destinationChain,
254
+ providerState,
255
+ routeConfiguration,
256
+ sourceChain,
257
+ transactionHash,
258
+ transfer,
259
+ },
260
+ signal,
261
+ ) {
262
+ if (transfer.method !== 'transaction' || transfer.mode !== 'exactSource' || !transfer.sender)
263
+ return { type: 'invalid' }
264
+ const configuration = providerState?.['routeConfiguration'] ?? routeConfiguration
265
+ const evidence = await verifySourceTransaction({
266
+ configuration,
267
+ destinationTokenAddress: transfer.destinationToken.address,
268
+ fetch,
269
+ recipient: transfer.recipient,
270
+ sender: transfer.sender,
271
+ signal,
272
+ sourceAmount: transfer.sourceAmount.baseUnits,
273
+ sourceChain,
274
+ sourceTokenAddress: transfer.sourceToken.address,
275
+ transactionHash,
276
+ validAfter: transfer.quote.sampledAt,
277
+ })
278
+ if (evidence.type === 'invalid') return evidence
279
+ if (evidence.type !== 'verified') return { type: 'pending' }
280
+ const destinationBlockNumber =
281
+ typeof providerState?.['destinationBlockNumber'] === 'string' &&
282
+ /^\d+$/.test(providerState['destinationBlockNumber'])
283
+ ? providerState['destinationBlockNumber']
284
+ : await blockNumberBefore({
285
+ chain: destinationChain,
286
+ fetch,
287
+ signal,
288
+ timestamp: transfer.quote.sampledAt,
289
+ })
290
+ return {
291
+ providerState: {
292
+ ...providerState,
293
+ destinationBlockNumber,
294
+ routeConfiguration: configuration,
295
+ stargate: {
296
+ guid: evidence.guid,
297
+ sourceBlock: {
298
+ hash: evidence.sourceBlock.hash,
299
+ number: evidence.sourceBlock.number.toString(),
300
+ },
301
+ sourceEid: evidence.sourceEid,
302
+ },
303
+ },
304
+ type: 'verified',
110
305
  }
111
306
  },
112
307
  })
@@ -115,70 +310,547 @@ export function stargate(options: stargate.Options) {
115
310
  export declare namespace stargate {
116
311
  /** Stargate provider options. */
117
312
  type Options = {
118
- /** LayerZero Value Transfer API key sent in the required request header. */
119
- apiKey: string
120
- /** Override for tests or LayerZero test environments. */
121
- baseUrl?: string | undefined
122
- /** Fetch implementation override for tests. */
313
+ /** Fetch implementation override for chain RPC requests. */
123
314
  fetch?: FundingProvider.Fetch | undefined
124
315
  /** Clock override for deterministic tests. */
125
316
  now?: (() => Date) | undefined
126
- /** Quote wallet address used as the LayerZero sender and recipient. */
127
- userAddress: string
128
- }
129
- }
130
-
131
- function selectQuote(
132
- quotes: z.output<typeof schema.Quotes>['quotes'],
133
- options: selectQuote.Options,
134
- ) {
135
- return quotes
136
- .flatMap((quote) => {
137
- const type = quote.routeSteps
138
- .filter((step) => step.srcChainKey === options.sourceChainKey)
139
- .map((step) => step.type)
140
- .find(isStargateType)
141
- if (!type) return []
142
- if (
143
- quote.expiresAt !== undefined &&
144
- new Date(quote.expiresAt).getTime() <= options.now.getTime()
317
+ }
318
+ }
319
+
320
+ /** Validates one direct Stargate action and returns its exact native-value allowance. */
321
+ export function validateAction(options: validateAction.Options): readonly Action.NativeValue[] {
322
+ try {
323
+ const action = Action.schema.EvmCalls.parse(options.action)
324
+ const deployment = resolveDeployment({
325
+ configuration: options.configuration,
326
+ destinationChainId,
327
+ destinationTokenAddress: options.destinationTokenAddress,
328
+ sourceChainId: options.sourceChainId,
329
+ sourceTokenAddress: options.sourceTokenAddress,
330
+ })
331
+ if (!deployment || action.calls.length !== 2) throw new FundingProvider.ProviderPayloadError()
332
+ const [approval, execution] = action.calls
333
+ if (
334
+ !Address.isEqual(approval!.to as Address.Address, deployment.sourceToken) ||
335
+ BigInt(approval!.value) !== 0n
336
+ )
337
+ throw new FundingProvider.ProviderPayloadError()
338
+
339
+ const [spender, approvalAmount] = AbiFunction.decodeData(approve, approval!.data as Hex.Hex)
340
+ if (
341
+ !Address.isEqual(spender, deployment.pool) ||
342
+ approvalAmount !== BigInt(options.sourceAmount)
343
+ )
344
+ throw new FundingProvider.ProviderPayloadError()
345
+ validateSend({
346
+ data: execution!.data as Hex.Hex,
347
+ nativeValue: BigInt(execution!.value),
348
+ pool: deployment.pool,
349
+ recipient: options.recipient,
350
+ sender: options.sender,
351
+ sourceAmount: options.sourceAmount,
352
+ to: execution!.to as Address.Address,
353
+ })
354
+ return [{ to: execution!.to, value: execution!.value }]
355
+ } catch (cause) {
356
+ if (cause instanceof FundingProvider.ProviderPayloadError) throw cause
357
+ throw new FundingProvider.ProviderPayloadError()
358
+ }
359
+ }
360
+
361
+ export declare namespace validateAction {
362
+ /** Expected route terms for one Stargate action. */
363
+ type Options = {
364
+ /** Prepared Stargate action. */
365
+ action: Action.EvmCalls
366
+ /** Stargate configuration for the selected route. */
367
+ configuration?: Catalog.Configuration | undefined
368
+ /** Curated Tempo destination token. */
369
+ destinationTokenAddress: string
370
+ /** Tempo beneficiary. */
371
+ recipient: string
372
+ /** Source account and LayerZero refund recipient. */
373
+ sender: string
374
+ /** Exact source amount in base units. */
375
+ sourceAmount: string
376
+ /** Source chain CAIP-2 id. */
377
+ sourceChainId: string
378
+ /** Curated source token. */
379
+ sourceTokenAddress: string
380
+ }
381
+ }
382
+
383
+ /** Verifies a submitted Stargate source transaction against stored transfer terms. */
384
+ export async function verifySourceTransaction(
385
+ options: verifySourceTransaction.Options,
386
+ ): Promise<verifySourceTransaction.Result> {
387
+ const deployment = resolveDeployment({
388
+ configuration: options.configuration,
389
+ destinationChainId,
390
+ destinationTokenAddress: options.destinationTokenAddress,
391
+ sourceChainId: options.sourceChain.id,
392
+ sourceTokenAddress: options.sourceTokenAddress,
393
+ })
394
+ if (
395
+ !deployment ||
396
+ options.sourceChain.kind !== 'evm' ||
397
+ !Hash.validate(options.transactionHash) ||
398
+ !Address.validate(options.recipient, { strict: false }) ||
399
+ !Address.validate(options.sender, { strict: false })
400
+ )
401
+ return { type: 'invalid' }
402
+
403
+ const rpc = rpcClient({
404
+ fetch: options.fetch ?? defaultFetch,
405
+ signal: options.signal,
406
+ urls: options.sourceChain.rpcUrls,
407
+ })
408
+ const hash = options.transactionHash as Hex.Hex
409
+ const [chainId, receipt] = await Promise.all([
410
+ rpc.getChainId(),
411
+ rpc.getTransactionReceipt({ hash }).catch((cause) => {
412
+ if (cause instanceof TransactionReceiptNotFoundError) return undefined
413
+ throw cause
414
+ }),
415
+ ])
416
+ if (chainId !== Number(Chain.eip155Id(options.sourceChain))) return { type: 'invalid' }
417
+ const block =
418
+ !receipt || receipt.blockNumber === null
419
+ ? undefined
420
+ : await rpc
421
+ .getBlock({ blockNumber: receipt.blockNumber, includeTransactions: false })
422
+ .catch((cause) => {
423
+ if (cause instanceof BlockNotFoundError) return undefined
424
+ throw cause
425
+ })
426
+ if (
427
+ !receipt ||
428
+ receipt.blockNumber === null ||
429
+ !block ||
430
+ !Hex.isEqual(block.hash, receipt.blockHash)
431
+ ) {
432
+ if (!options.sourceBlock) return { type: 'pending' }
433
+ const finalized = await rpc.getBlock({ blockTag: 'finalized', includeTransactions: false })
434
+ if (finalized.number < options.sourceBlock.number) return { type: 'pending' }
435
+ const canonical = await rpc
436
+ .getBlock({ blockNumber: options.sourceBlock.number, includeTransactions: false })
437
+ .catch((cause) => {
438
+ if (cause instanceof BlockNotFoundError) return undefined
439
+ throw cause
440
+ })
441
+ if (!canonical || Hex.isEqual(canonical.hash, options.sourceBlock.hash))
442
+ return { type: 'pending' }
443
+ return { type: 'reorged' }
444
+ }
445
+ if (receipt.status !== 'success') return { type: 'invalid' }
446
+ const validAfter = Math.floor(Date.parse(options.validAfter) / 1_000)
447
+ if (!Number.isSafeInteger(validAfter) || block.timestamp < BigInt(validAfter))
448
+ return { type: 'invalid' }
449
+
450
+ const amount = BigInt(options.sourceAmount)
451
+ const sourceEvents = parseEventLogs({ abi: [oftSent], logs: receipt.logs, strict: true }).filter(
452
+ ({ address, args }) =>
453
+ Address.isEqual(address, deployment.pool) &&
454
+ args.dstEid === destinationEid &&
455
+ Address.isEqual(args.fromAddress, options.sender as Address.Address) &&
456
+ args.amountSentLD === amount &&
457
+ args.amountReceivedLD === amount,
458
+ )
459
+ const transfers = parseEventLogs({
460
+ abi: [transferEvent],
461
+ logs: receipt.logs,
462
+ strict: true,
463
+ }).filter(
464
+ ({ address, args }) =>
465
+ Address.isEqual(address, deployment.sourceToken) &&
466
+ Address.isEqual(args.from, options.sender as Address.Address) &&
467
+ Address.isEqual(args.to, deployment.pool) &&
468
+ args.value === amount,
469
+ )
470
+ if (sourceEvents.length !== 1 || transfers.length !== 1) return { type: 'invalid' }
471
+ const guid = sourceEvents[0]!.args.guid
472
+ // PacketV1's 113-byte header is followed by Stargate's type, asset id, recipient, and amount.
473
+ const packets = parseEventLogs({ abi: [packetSent], logs: receipt.logs, strict: true }).filter(
474
+ ({ address, args }) => {
475
+ const packet = args.encodedPayload
476
+ return (
477
+ Address.isEqual(address, layerZeroEndpoint) &&
478
+ Hex.size(packet) === 156 &&
479
+ Hex.toNumber(Hex.slice(packet, 0, 1)) === 1 &&
480
+ Hex.toNumber(Hex.slice(packet, 9, 13)) === deployment.sourceEid &&
481
+ Hex.toNumber(Hex.slice(packet, 45, 49)) === destinationEid &&
482
+ Hex.isEqual(Hex.slice(packet, 81, 113), guid) &&
483
+ Hex.toNumber(Hex.slice(packet, 113, 114)) === 1 &&
484
+ Hex.isEqual(Hex.slice(packet, 116, 148), recipientBytes32(options.recipient)) &&
485
+ Hex.toBigInt(Hex.slice(packet, 148, 156)) === amount
145
486
  )
146
- return []
147
- if (
148
- quote.srcAmount !== options.amount ||
149
- BigInt(quote.dstAmount) <= 0n ||
150
- BigInt(quote.dstAmountMin) <= 0n ||
151
- BigInt(quote.dstAmountMin) > BigInt(quote.dstAmount)
487
+ },
488
+ )
489
+ if (packets.length !== 1) return { type: 'invalid' }
490
+ return {
491
+ guid,
492
+ sourceBlock: { hash: block.hash, number: block.number },
493
+ sourceEid: deployment.sourceEid,
494
+ type: 'verified',
495
+ }
496
+ }
497
+
498
+ export declare namespace verifySourceTransaction {
499
+ /** Canonical source block that originally contained the transaction. */
500
+ type Block = {
501
+ /** Source block hash. */
502
+ hash: Hex.Hex
503
+ /** Source block number. */
504
+ number: bigint
505
+ }
506
+
507
+ /** Stored terms and chain access needed to verify one source transaction. */
508
+ type Options = {
509
+ /** Stargate configuration for the selected route. */
510
+ configuration: unknown
511
+ /** Curated Tempo destination token. */
512
+ destinationTokenAddress: string
513
+ /** Fetch implementation override for chain RPC requests. */
514
+ fetch?: FundingProvider.Fetch | undefined
515
+ /** Tempo beneficiary encoded in the Stargate send. */
516
+ recipient: string
517
+ /** Source account and LayerZero refund recipient. */
518
+ sender: string
519
+ /** Optional request cancellation signal. */
520
+ signal?: AbortSignal | undefined
521
+ /** Exact source amount in base units. */
522
+ sourceAmount: string
523
+ /** Canonical block captured during initial verification. */
524
+ sourceBlock?: Block | undefined
525
+ /** Source chain and RPC endpoints. */
526
+ sourceChain: Chain.Chain
527
+ /** Curated source token. */
528
+ sourceTokenAddress: string
529
+ /** Submitted EVM transaction hash. */
530
+ transactionHash: string
531
+ /** Earliest accepted source block timestamp. */
532
+ validAfter: string
533
+ }
534
+
535
+ /** Source transaction verification outcome. */
536
+ type Result =
537
+ | { type: 'invalid' }
538
+ | { type: 'pending' }
539
+ | { type: 'reorged' }
540
+ | { guid: Hex.Hex; sourceBlock: Block; sourceEid: number; type: 'verified' }
541
+ }
542
+
543
+ /** Finds and verifies the finalized Stargate delivery directly on Tempo. */
544
+ export async function verifyDestinationDelivery(
545
+ options: verifyDestinationDelivery.Options,
546
+ ): Promise<verifyDestinationDelivery.Result> {
547
+ const deployment = resolveDeployment({
548
+ configuration: options.configuration,
549
+ destinationChainId: options.destinationChain.id,
550
+ destinationTokenAddress: options.destinationTokenAddress,
551
+ sourceChainId: options.sourceChainId,
552
+ sourceTokenAddress: options.sourceTokenAddress,
553
+ })
554
+ if (
555
+ !deployment ||
556
+ options.destinationChain.kind !== 'evm' ||
557
+ !/^\d+$/.test(options.destinationBlockNumber) ||
558
+ !Hash.validate(options.guid) ||
559
+ !Address.validate(options.recipient, { strict: false })
560
+ )
561
+ return { type: 'invalid' }
562
+
563
+ const rpc = rpcClient({
564
+ fetch: options.fetch ?? defaultFetch,
565
+ urls: options.destinationChain.rpcUrls,
566
+ })
567
+ const [chainId, finalized] = await Promise.all([
568
+ rpc.getChainId(),
569
+ rpc.getBlock({ blockTag: 'finalized', includeTransactions: false }),
570
+ ])
571
+ if (chainId !== Number(Chain.eip155Id(options.destinationChain))) return { type: 'invalid' }
572
+ const amount = BigInt(options.destinationAmount)
573
+ const discovered = await (async () => {
574
+ for (
575
+ let fromBlock = BigInt(options.destinationBlockNumber);
576
+ fromBlock <= finalized.number;
577
+ fromBlock += maximumLogBlockRange
578
+ ) {
579
+ const toBlock = fromBlock + maximumLogBlockRange - 1n
580
+ const logs = await rpc.getLogs({
581
+ address: deployment.destinationPool,
582
+ args: { guid: options.guid, toAddress: options.recipient as Address.Address },
583
+ event: oftReceived,
584
+ fromBlock,
585
+ strict: true,
586
+ toBlock: toBlock > finalized.number ? finalized.number : toBlock,
587
+ })
588
+ const matches = logs.filter(
589
+ ({ args, transactionHash }) =>
590
+ transactionHash &&
591
+ args.srcEid === deployment.sourceEid &&
592
+ args.amountReceivedLD === amount,
152
593
  )
153
- throw new FundingProvider.ProviderPayloadError()
154
- return [{ quote, type }]
155
- })
156
- .sort((a, b) => {
157
- const delta = BigInt(b.quote.dstAmountMin) - BigInt(a.quote.dstAmountMin)
158
- if (delta > 0n) return 1
159
- if (delta < 0n) return -1
160
- return 0
161
- })[0]
594
+ if (logs.length !== matches.length || matches.length > 1) return { type: 'invalid' as const }
595
+ if (matches[0]?.transactionHash)
596
+ return { transactionHash: matches[0].transactionHash, type: 'found' as const }
597
+ }
598
+ return { type: 'pending' as const }
599
+ })()
600
+ if (discovered.type !== 'found') return discovered
601
+ const { transactionHash } = discovered
602
+ const receipt = await rpc.getTransactionReceipt({ hash: transactionHash }).catch((cause) => {
603
+ if (cause instanceof TransactionReceiptNotFoundError) return undefined
604
+ throw cause
605
+ })
606
+ if (!receipt || receipt.blockNumber === null) return { type: 'pending' }
607
+ if (receipt.status !== 'success' || receipt.blockNumber > finalized.number)
608
+ return { type: 'invalid' }
609
+
610
+ const transfers = parseEventLogs({
611
+ abi: [transferEvent],
612
+ logs: receipt.logs,
613
+ strict: true,
614
+ }).filter(
615
+ ({ address, args }) =>
616
+ Address.isEqual(address, destinationToken as Address.Address) &&
617
+ Address.isEqual(args.from, zeroAddress) &&
618
+ Address.isEqual(args.to, options.recipient as Address.Address) &&
619
+ args.value === amount,
620
+ )
621
+ if (transfers.length !== 1) return { type: 'invalid' }
622
+ return { transactionHash, type: 'verified' }
623
+ }
624
+
625
+ export declare namespace verifyDestinationDelivery {
626
+ /** Stored terms and chain access needed to discover and verify one delivery. */
627
+ type Options = {
628
+ /** Stargate configuration for the selected route. */
629
+ configuration: unknown
630
+ /** Exact destination amount in base units. */
631
+ destinationAmount: string
632
+ /** Tempo block captured before the source transaction was broadcast. */
633
+ destinationBlockNumber: string
634
+ /** Tempo chain and RPC endpoints. */
635
+ destinationChain: Chain.Chain
636
+ /** Curated Tempo destination token. */
637
+ destinationTokenAddress: string
638
+ /** Fetch implementation override for Tempo RPC requests. */
639
+ fetch?: FundingProvider.Fetch | undefined
640
+ /** GUID emitted by the verified source Stargate pool. */
641
+ guid: Hex.Hex
642
+ /** Tempo beneficiary. */
643
+ recipient: string
644
+ /** Source chain CAIP-2 id. */
645
+ sourceChainId: string
646
+ /** Curated source token. */
647
+ sourceTokenAddress: string
648
+ }
649
+
650
+ /** Destination delivery verification outcome. */
651
+ type Result =
652
+ | { type: 'invalid' }
653
+ | { type: 'pending' }
654
+ | { transactionHash: Hex.Hex; type: 'verified' }
655
+ }
656
+
657
+ function rpcClient(options: rpcClient.Options) {
658
+ if (options.urls.length === 0) throw new FundingProvider.ProviderConfigurationError()
659
+ return Viem.createEvmClient(options).extend(publicActions)
660
+ }
661
+
662
+ declare namespace rpcClient {
663
+ type Options = {
664
+ fetch: FundingProvider.Fetch
665
+ signal?: AbortSignal | undefined
666
+ urls: readonly string[]
667
+ }
668
+ }
669
+
670
+ async function blockNumberBefore(options: blockNumberBefore.Options) {
671
+ if (options.chain.kind !== 'evm') throw new FundingProvider.ProviderConfigurationError()
672
+ const timestamp = Date.parse(options.timestamp)
673
+ if (!Number.isFinite(timestamp)) throw new FundingProvider.ProviderPayloadError()
674
+ const rpc = rpcClient({
675
+ fetch: options.fetch,
676
+ signal: options.signal,
677
+ urls: options.chain.rpcUrls,
678
+ })
679
+ const [chainId, finalized] = await Promise.all([
680
+ rpc.getChainId(),
681
+ rpc.getBlock({ blockTag: 'finalized', includeTransactions: false }),
682
+ ])
683
+ if (chainId !== Number(Chain.eip155Id(options.chain)))
684
+ throw new FundingProvider.ProviderConfigurationError()
685
+
686
+ const target = BigInt(Math.floor(timestamp / 1_000) - 1)
687
+ let lower = 0n
688
+ let upper = finalized.number
689
+ while (lower < upper) {
690
+ const middle = (lower + upper + 1n) / 2n
691
+ const block = await rpc.getBlock({ blockNumber: middle, includeTransactions: false })
692
+ if (block.timestamp <= target) lower = middle
693
+ else upper = middle - 1n
694
+ }
695
+ return lower.toString()
696
+ }
697
+
698
+ declare namespace blockNumberBefore {
699
+ type Options = {
700
+ /** Destination chain whose block history is searched. */
701
+ chain: Chain.Chain
702
+ /** Fetch implementation used for RPC requests. */
703
+ fetch: FundingProvider.Fetch
704
+ /** Optional request cancellation signal. */
705
+ signal?: AbortSignal | undefined
706
+ /** Exclusive ISO 8601 timestamp upper bound. */
707
+ timestamp: string
708
+ }
709
+ }
710
+
711
+ function createSendParam(options: createSendParam.Options): SendParam {
712
+ const amount = BigInt(options.amount)
713
+ return {
714
+ amountLD: amount,
715
+ composeMsg: '0x',
716
+ dstEid: destinationEid,
717
+ extraOptions: '0x',
718
+ minAmountLD: amount,
719
+ oftCmd: '0x',
720
+ to: recipientBytes32(options.recipient),
721
+ }
162
722
  }
163
723
 
164
- declare namespace selectQuote {
724
+ declare namespace createSendParam {
165
725
  type Options = {
166
726
  amount: string
167
- now: Date
168
- sourceChainKey: string
727
+ recipient: string
728
+ }
729
+ }
730
+
731
+ function isExactQuote(options: isExactQuote.Options) {
732
+ const amount = BigInt(options.amount)
733
+ return (
734
+ amount >= options.limit.minAmountLD &&
735
+ amount <= options.limit.maxAmountLD &&
736
+ options.feeDetails.every((fee) => fee.feeAmountLD >= 0n) &&
737
+ options.receipt.amountSentLD === amount &&
738
+ options.receipt.amountReceivedLD === amount
739
+ )
740
+ }
741
+
742
+ declare namespace isExactQuote {
743
+ type Options = {
744
+ amount: string
745
+ feeDetails: readonly { feeAmountLD: bigint }[]
746
+ limit: { maxAmountLD: bigint; minAmountLD: bigint }
747
+ receipt: { amountReceivedLD: bigint; amountSentLD: bigint }
748
+ }
749
+ }
750
+
751
+ function nativeToken(sourceChainId: string) {
752
+ return {
753
+ address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
754
+ currency: 'ETH',
755
+ decimals: 18,
756
+ name: 'Ether',
757
+ standard: 'native',
758
+ symbol: 'ETH',
759
+ tokenKey: `${sourceChainId}/slip44:60`,
760
+ verified: true,
761
+ }
762
+ }
763
+
764
+ function quality() {
765
+ return {
766
+ liquiditySource: 'onchain',
767
+ sourceDetail: 'stargate:oft-v2:taxi',
768
+ tier: 'liquid' as const,
769
+ }
770
+ }
771
+
772
+ function recipientBytes32(recipient: string) {
773
+ if (!Address.validate(recipient, { strict: false }))
774
+ throw new FundingProvider.ProviderConfigurationError()
775
+ return Hex.padLeft(recipient as Hex.Hex, 32)
776
+ }
777
+
778
+ function validateSend(options: validateSend.Options) {
779
+ const [sendParam, messagingFee, refundAddress] = AbiFunction.decodeData(sendToken, options.data)
780
+ const amount = BigInt(options.sourceAmount)
781
+ if (
782
+ !Address.isEqual(options.to, options.pool) ||
783
+ sendParam.amountLD !== amount ||
784
+ sendParam.composeMsg !== '0x' ||
785
+ sendParam.dstEid !== destinationEid ||
786
+ sendParam.extraOptions !== '0x' ||
787
+ sendParam.minAmountLD !== amount ||
788
+ sendParam.oftCmd !== '0x' ||
789
+ !Hex.isEqual(sendParam.to, recipientBytes32(options.recipient)) ||
790
+ messagingFee.lzTokenFee !== 0n ||
791
+ messagingFee.nativeFee !== options.nativeValue ||
792
+ !Address.isEqual(refundAddress, options.sender as Address.Address)
793
+ )
794
+ throw new FundingProvider.ProviderPayloadError()
795
+ }
796
+
797
+ declare namespace validateSend {
798
+ type Options = {
799
+ data: Hex.Hex
800
+ nativeValue: bigint
801
+ pool: Address.Address
802
+ recipient: string
803
+ sender: string
804
+ sourceAmount: string
805
+ to: Address.Address
806
+ }
807
+ }
808
+
809
+ function resolveDeployment(options: resolveDeployment.Options): Deployment | undefined {
810
+ const parsed = schema.Configuration.safeParse(options.configuration)
811
+ const sourceEid =
812
+ parsed.success &&
813
+ (parsed.data.sourceEid ?? sourceEids[options.sourceChainId as keyof typeof sourceEids])
814
+ if (
815
+ !parsed.success ||
816
+ !sourceEid ||
817
+ options.destinationChainId !== destinationChainId ||
818
+ !options.sourceChainId.startsWith('eip155:') ||
819
+ !Address.validate(options.destinationTokenAddress, { strict: false }) ||
820
+ !Address.validate(options.sourceTokenAddress, { strict: false }) ||
821
+ !Address.isEqual(options.destinationTokenAddress as Address.Address, destinationToken)
822
+ )
823
+ return undefined
824
+ return {
825
+ destinationPool: Address.checksum(parsed.data.destinationPoolAddress ?? destinationPool),
826
+ pool: Address.checksum(parsed.data.poolAddress),
827
+ sourceEid,
828
+ sourceToken: Address.checksum(options.sourceTokenAddress),
169
829
  }
170
830
  }
171
831
 
172
- function isStargateType(type: string): type is keyof typeof sourceDetails {
173
- return Object.hasOwn(sourceDetails, type)
832
+ declare namespace resolveDeployment {
833
+ type Options = {
834
+ configuration: unknown
835
+ destinationChainId: string
836
+ destinationTokenAddress: string
837
+ sourceChainId: string
838
+ sourceTokenAddress: string
839
+ }
174
840
  }
175
841
 
176
- function estimatedSeconds(milliseconds: string | null | undefined) {
177
- if (milliseconds === undefined || milliseconds === null) return undefined
178
- const [whole = '0', fraction] = milliseconds.split('.')
179
- const roundedMilliseconds =
180
- BigInt(whole) + (fraction !== undefined && /[1-9]/.test(fraction) ? 1n : 0n)
181
- const seconds = (roundedMilliseconds + 999n) / 1_000n
182
- if (seconds > BigInt(Number.MAX_SAFE_INTEGER)) throw new FundingProvider.ProviderPayloadError()
183
- return Number(seconds)
842
+ function unavailable(options: unavailable.Options) {
843
+ return FundingProvider.unavailableResult({
844
+ detail: 'stargate:oft-v2',
845
+ message: options.message,
846
+ now: options.now,
847
+ source: 'onchain',
848
+ })
849
+ }
850
+
851
+ declare namespace unavailable {
852
+ type Options = {
853
+ message: string
854
+ now: Date
855
+ }
184
856
  }