tapimo 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1143) hide show
  1. package/README.md +7 -4
  2. package/dist/ApiKey.js +1 -1
  3. package/dist/ApiKey.js.map +1 -1
  4. package/dist/ApiKeys.d.ts +95 -14
  5. package/dist/ApiKeys.d.ts.map +1 -1
  6. package/dist/ApiKeys.js +360 -49
  7. package/dist/ApiKeys.js.map +1 -1
  8. package/dist/App.d.ts +21383 -18056
  9. package/dist/App.d.ts.map +1 -1
  10. package/dist/App.js +301 -22
  11. package/dist/App.js.map +1 -1
  12. package/dist/Assets.d.ts.map +1 -1
  13. package/dist/Assets.js +2 -1
  14. package/dist/Assets.js.map +1 -1
  15. package/dist/Handler.d.ts +0 -1
  16. package/dist/Handler.d.ts.map +1 -1
  17. package/dist/Handler.js +0 -1
  18. package/dist/Handler.js.map +1 -1
  19. package/dist/Scope.d.ts +13 -5
  20. package/dist/Scope.d.ts.map +1 -1
  21. package/dist/Scope.js +16 -5
  22. package/dist/Scope.js.map +1 -1
  23. package/dist/Webhooks.js +2 -2
  24. package/dist/Webhooks.js.map +1 -1
  25. package/dist/admin/App.d.ts +3936 -1337
  26. package/dist/admin/App.d.ts.map +1 -1
  27. package/dist/admin/App.js +26 -5
  28. package/dist/admin/App.js.map +1 -1
  29. package/dist/admin/apps/api-keys.d.ts +159 -7
  30. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  31. package/dist/admin/apps/api-keys.js +95 -43
  32. package/dist/admin/apps/api-keys.js.map +1 -1
  33. package/dist/admin/apps/earn-vaults.d.ts +1446 -0
  34. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  35. package/dist/admin/apps/earn-vaults.js +465 -8
  36. package/dist/admin/apps/earn-vaults.js.map +1 -1
  37. package/dist/admin/apps/organizations.d.ts +344 -3
  38. package/dist/admin/apps/organizations.d.ts.map +1 -1
  39. package/dist/admin/apps/organizations.js +288 -10
  40. package/dist/admin/apps/organizations.js.map +1 -1
  41. package/dist/admin/apps/routes-support.d.ts +95 -0
  42. package/dist/admin/apps/routes-support.d.ts.map +1 -0
  43. package/dist/admin/apps/routes-support.js +151 -0
  44. package/dist/admin/apps/routes-support.js.map +1 -0
  45. package/dist/admin/apps/routes.d.ts +2764 -0
  46. package/dist/admin/apps/routes.d.ts.map +1 -0
  47. package/dist/admin/apps/routes.js +728 -0
  48. package/dist/admin/apps/routes.js.map +1 -0
  49. package/dist/admin/apps/subsidies.d.ts +501 -0
  50. package/dist/admin/apps/subsidies.d.ts.map +1 -0
  51. package/dist/admin/apps/subsidies.js +384 -0
  52. package/dist/admin/apps/subsidies.js.map +1 -0
  53. package/dist/admin/apps/verified-tokens.d.ts +16 -16
  54. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  55. package/dist/admin/apps/verified-tokens.js +3 -3
  56. package/dist/admin/apps/verified-tokens.js.map +1 -1
  57. package/dist/admin/ui.generated.d.ts +1 -1
  58. package/dist/admin/ui.generated.d.ts.map +1 -1
  59. package/dist/admin/ui.generated.js +129 -69
  60. package/dist/admin/ui.generated.js.map +1 -1
  61. package/dist/analytics/Analytics.d.ts +5 -2
  62. package/dist/analytics/Analytics.d.ts.map +1 -1
  63. package/dist/analytics/Analytics.js +31 -1
  64. package/dist/analytics/Analytics.js.map +1 -1
  65. package/dist/analytics/tables/requestEvents.d.ts +2 -1
  66. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  67. package/dist/analytics/tables/requestEvents.js +10 -1
  68. package/dist/analytics/tables/requestEvents.js.map +1 -1
  69. package/dist/analytics/tables/routesLifecycleIntervals.d.ts +72 -0
  70. package/dist/analytics/tables/routesLifecycleIntervals.d.ts.map +1 -0
  71. package/dist/analytics/tables/routesLifecycleIntervals.js +56 -0
  72. package/dist/analytics/tables/routesLifecycleIntervals.js.map +1 -0
  73. package/dist/apps/Zones.d.ts +21 -12526
  74. package/dist/apps/Zones.d.ts.map +1 -1
  75. package/dist/apps/Zones.js +40 -8
  76. package/dist/apps/Zones.js.map +1 -1
  77. package/dist/apps/data/App.d.ts +4310 -2548
  78. package/dist/apps/data/App.d.ts.map +1 -1
  79. package/dist/apps/data/App.js +86 -26
  80. package/dist/apps/data/App.js.map +1 -1
  81. package/dist/apps/data/routes/activities.d.ts +364 -34
  82. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  83. package/dist/apps/data/routes/activities.js +495 -190
  84. package/dist/apps/data/routes/activities.js.map +1 -1
  85. package/dist/apps/data/routes/addresses.d.ts +94 -22
  86. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  87. package/dist/apps/data/routes/blocks.d.ts +37 -37
  88. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  89. package/dist/apps/data/routes/coingecko.js +47 -21
  90. package/dist/apps/data/routes/coingecko.js.map +1 -1
  91. package/dist/apps/data/routes/earn.d.ts +1641 -352
  92. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  93. package/dist/apps/data/routes/earn.js +1561 -120
  94. package/dist/apps/data/routes/earn.js.map +1 -1
  95. package/dist/apps/data/routes/exchanges.d.ts +15 -0
  96. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  97. package/dist/apps/data/routes/exchanges.js +180 -114
  98. package/dist/apps/data/routes/exchanges.js.map +1 -1
  99. package/dist/apps/data/routes/fee-amm.d.ts +5 -0
  100. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  101. package/dist/apps/data/routes/fee-amm.js +49 -38
  102. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  103. package/dist/apps/data/routes/indexer.d.ts +125 -0
  104. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  105. package/dist/apps/data/routes/indexer.js +83 -27
  106. package/dist/apps/data/routes/indexer.js.map +1 -1
  107. package/dist/apps/data/routes/rpc.d.ts +507 -33
  108. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  109. package/dist/apps/data/routes/rpc.js +266 -156
  110. package/dist/apps/data/routes/rpc.js.map +1 -1
  111. package/dist/apps/data/routes/tokenlist.d.ts +47 -15
  112. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  113. package/dist/apps/data/routes/tokenlist.js +68 -11
  114. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  115. package/dist/apps/data/routes/tokens.d.ts +95 -93
  116. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  117. package/dist/apps/data/routes/tokens.js +26 -14
  118. package/dist/apps/data/routes/tokens.js.map +1 -1
  119. package/dist/apps/data/routes/transfers.d.ts +16 -0
  120. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  121. package/dist/apps/data/routes/transfers.js +9 -7
  122. package/dist/apps/data/routes/transfers.js.map +1 -1
  123. package/dist/apps/data/routes/verified-tokens.d.ts +4 -4
  124. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  125. package/dist/apps/data/routes/verified-tokens.js +64 -20
  126. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  127. package/dist/apps/data/routes/webhooks.d.ts +1210 -70
  128. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  129. package/dist/apps/data/routes/webhooks.js +301 -163
  130. package/dist/apps/data/routes/webhooks.js.map +1 -1
  131. package/dist/apps/data/routes/zones.d.ts.map +1 -1
  132. package/dist/apps/data/routes/zones.js +20 -12
  133. package/dist/apps/data/routes/zones.js.map +1 -1
  134. package/dist/apps/docs/reference.js +2 -2
  135. package/dist/apps/docs/reference.js.map +1 -1
  136. package/dist/apps/index.d.ts +1 -1
  137. package/dist/apps/index.d.ts.map +1 -1
  138. package/dist/apps/index.js +1 -1
  139. package/dist/apps/index.js.map +1 -1
  140. package/dist/apps/management/App.d.ts +4119 -3297
  141. package/dist/apps/management/App.d.ts.map +1 -1
  142. package/dist/apps/management/App.js +5 -14
  143. package/dist/apps/management/App.js.map +1 -1
  144. package/dist/apps/management/Billing.d.ts +49 -8
  145. package/dist/apps/management/Billing.d.ts.map +1 -1
  146. package/dist/apps/management/Billing.js +221 -26
  147. package/dist/apps/management/Billing.js.map +1 -1
  148. package/dist/apps/management/routes/api-keys.d.ts +174 -72
  149. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  150. package/dist/apps/management/routes/api-keys.js +43 -15
  151. package/dist/apps/management/routes/api-keys.js.map +1 -1
  152. package/dist/apps/management/routes/billing.d.ts +364 -78
  153. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  154. package/dist/apps/management/routes/billing.js +274 -49
  155. package/dist/apps/management/routes/billing.js.map +1 -1
  156. package/dist/apps/management/routes/faucet.d.ts +6 -6
  157. package/dist/apps/management/routes/faucet.js +1 -1
  158. package/dist/apps/management/routes/faucet.js.map +1 -1
  159. package/dist/apps/management/routes/invitations.d.ts +99 -24
  160. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  161. package/dist/apps/management/routes/invitations.js +117 -13
  162. package/dist/apps/management/routes/invitations.js.map +1 -1
  163. package/dist/apps/management/routes/invite-links.d.ts +42 -42
  164. package/dist/apps/management/routes/me.d.ts.map +1 -1
  165. package/dist/apps/management/routes/me.js +6 -2
  166. package/dist/apps/management/routes/me.js.map +1 -1
  167. package/dist/apps/management/routes/members.d.ts +16 -16
  168. package/dist/apps/management/routes/orgs.d.ts +36 -20
  169. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  170. package/dist/apps/management/routes/orgs.js +52 -12
  171. package/dist/apps/management/routes/orgs.js.map +1 -1
  172. package/dist/apps/management/routes/projects.d.ts +40 -40
  173. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  174. package/dist/apps/management/routes/projects.js +6 -1
  175. package/dist/apps/management/routes/projects.js.map +1 -1
  176. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  177. package/dist/apps/management/routes/scopes.js +4 -1
  178. package/dist/apps/management/routes/scopes.js.map +1 -1
  179. package/dist/apps/management/routes/usage.d.ts +8 -8
  180. package/dist/apps/management/routes/usage.js +21 -4
  181. package/dist/apps/management/routes/usage.js.map +1 -1
  182. package/dist/apps/management/routes/verified-token-requests.d.ts +16 -16
  183. package/dist/apps/management/routes/webhooks.d.ts +931 -215
  184. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  185. package/dist/apps/management/routes/webhooks.js +71 -25
  186. package/dist/apps/management/routes/webhooks.js.map +1 -1
  187. package/dist/apps/mcp.d.ts +1 -1
  188. package/dist/apps/mcp.js +7 -7
  189. package/dist/apps/mcp.js.map +1 -1
  190. package/dist/apps/mpp/App.d.ts.map +1 -1
  191. package/dist/apps/mpp/App.js +35 -7
  192. package/dist/apps/mpp/App.js.map +1 -1
  193. package/dist/apps/mpp/internal/Adapter.d.ts +2 -0
  194. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
  195. package/dist/apps/mpp/internal/Adapter.js +30 -1
  196. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  197. package/dist/apps/mpp/internal/Policy.d.ts +22 -4
  198. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
  199. package/dist/apps/mpp/internal/Policy.js +24 -6
  200. package/dist/apps/mpp/internal/Policy.js.map +1 -1
  201. package/dist/apps/mpp/internal/Sponsorship.d.ts +3 -0
  202. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
  203. package/dist/apps/mpp/internal/Sponsorship.js +6 -3
  204. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
  205. package/dist/apps/relay/App.d.ts +2 -0
  206. package/dist/apps/relay/App.d.ts.map +1 -1
  207. package/dist/apps/relay/App.js +177 -26
  208. package/dist/apps/relay/App.js.map +1 -1
  209. package/dist/apps/relay/Sponsorships.d.ts +5 -3
  210. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  211. package/dist/apps/relay/Sponsorships.js +232 -25
  212. package/dist/apps/relay/Sponsorships.js.map +1 -1
  213. package/dist/apps/{funding → routes}/Access.d.ts +4 -4
  214. package/dist/apps/routes/Access.d.ts.map +1 -0
  215. package/dist/apps/{funding → routes}/Access.js +4 -4
  216. package/dist/apps/routes/Access.js.map +1 -0
  217. package/dist/apps/{funding → routes}/App.d.ts +841 -136
  218. package/dist/apps/{funding → routes}/App.d.ts.map +1 -1
  219. package/dist/apps/{funding → routes}/App.js +17 -17
  220. package/dist/apps/routes/App.js.map +1 -0
  221. package/dist/apps/routes/Inventory.d.ts.map +1 -0
  222. package/dist/apps/{funding → routes}/Inventory.js +2 -2
  223. package/dist/apps/routes/Inventory.js.map +1 -0
  224. package/dist/apps/routes/index.d.ts +2 -0
  225. package/dist/apps/routes/index.d.ts.map +1 -0
  226. package/dist/apps/routes/index.js +2 -0
  227. package/dist/apps/routes/index.js.map +1 -0
  228. package/dist/apps/{funding → routes}/routes/chains.d.ts +7 -7
  229. package/dist/apps/routes/routes/chains.d.ts.map +1 -0
  230. package/dist/apps/{funding → routes}/routes/chains.js +25 -25
  231. package/dist/apps/routes/routes/chains.js.map +1 -0
  232. package/dist/apps/{funding → routes}/routes/deposit-addresses.d.ts +123 -59
  233. package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -0
  234. package/dist/apps/{funding → routes}/routes/deposit-addresses.js +498 -223
  235. package/dist/apps/routes/routes/deposit-addresses.js.map +1 -0
  236. package/dist/apps/{funding → routes}/routes/providers.d.ts +8 -8
  237. package/dist/apps/routes/routes/providers.d.ts.map +1 -0
  238. package/dist/apps/routes/routes/providers.js +57 -0
  239. package/dist/apps/routes/routes/providers.js.map +1 -0
  240. package/dist/apps/{funding → routes}/routes/quotes.d.ts +19 -16
  241. package/dist/apps/routes/routes/quotes.d.ts.map +1 -0
  242. package/dist/apps/{funding → routes}/routes/quotes.js +55 -38
  243. package/dist/apps/routes/routes/quotes.js.map +1 -0
  244. package/dist/apps/{funding → routes}/routes/transfers.d.ts +957 -71
  245. package/dist/apps/routes/routes/transfers.d.ts.map +1 -0
  246. package/dist/apps/routes/routes/transfers.js +1140 -0
  247. package/dist/apps/routes/routes/transfers.js.map +1 -0
  248. package/dist/cli/Cli.d.ts +30 -0
  249. package/dist/cli/Cli.d.ts.map +1 -1
  250. package/dist/cli/commands/admin.d.ts +36 -0
  251. package/dist/cli/commands/admin.d.ts.map +1 -1
  252. package/dist/cli/commands/admin.js +273 -0
  253. package/dist/cli/commands/admin.js.map +1 -1
  254. package/dist/cloudflare.d.ts +16 -14
  255. package/dist/cloudflare.d.ts.map +1 -1
  256. package/dist/cloudflare.js +26 -21
  257. package/dist/cloudflare.js.map +1 -1
  258. package/dist/db/Db.d.ts +63 -21
  259. package/dist/db/Db.d.ts.map +1 -1
  260. package/dist/db/Db.js +1052 -15
  261. package/dist/db/Db.js.map +1 -1
  262. package/dist/db/Schema.d.ts +785 -133
  263. package/dist/db/Schema.d.ts.map +1 -1
  264. package/dist/db/Schema.js +590 -162
  265. package/dist/db/Schema.js.map +1 -1
  266. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts +4 -0
  267. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts.map +1 -0
  268. package/dist/db/tables/apiKeyAdmissionBootstraps.js +2 -0
  269. package/dist/db/tables/apiKeyAdmissionBootstraps.js.map +1 -0
  270. package/dist/db/tables/apiKeyAdmissions.d.ts +73 -0
  271. package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -0
  272. package/dist/db/tables/apiKeyAdmissions.js +142 -0
  273. package/dist/db/tables/apiKeyAdmissions.js.map +1 -0
  274. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts +4 -0
  275. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts.map +1 -0
  276. package/dist/db/tables/apiKeyOwnerAdmissions.js +2 -0
  277. package/dist/db/tables/apiKeyOwnerAdmissions.js.map +1 -0
  278. package/dist/db/tables/apiKeyOwnerTombstones.d.ts +20 -0
  279. package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -0
  280. package/dist/db/tables/apiKeyOwnerTombstones.js +57 -0
  281. package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -0
  282. package/dist/db/tables/apiKeyRevocations.d.ts +21 -0
  283. package/dist/db/tables/apiKeyRevocations.d.ts.map +1 -0
  284. package/dist/db/tables/apiKeyRevocations.js +98 -0
  285. package/dist/db/tables/apiKeyRevocations.js.map +1 -0
  286. package/dist/db/tables/authAccounts.d.ts +47 -0
  287. package/dist/db/tables/authAccounts.d.ts.map +1 -0
  288. package/dist/db/tables/authAccounts.js +72 -0
  289. package/dist/db/tables/authAccounts.js.map +1 -0
  290. package/dist/db/tables/authSessions.d.ts +12 -0
  291. package/dist/db/tables/authSessions.d.ts.map +1 -0
  292. package/dist/db/tables/authSessions.js +2 -0
  293. package/dist/db/tables/authSessions.js.map +1 -0
  294. package/dist/db/tables/authVerifications.d.ts +12 -0
  295. package/dist/db/tables/authVerifications.d.ts.map +1 -0
  296. package/dist/db/tables/authVerifications.js +2 -0
  297. package/dist/db/tables/authVerifications.js.map +1 -0
  298. package/dist/db/tables/invitations.d.ts +17 -1
  299. package/dist/db/tables/invitations.d.ts.map +1 -1
  300. package/dist/db/tables/invitations.js +123 -43
  301. package/dist/db/tables/invitations.js.map +1 -1
  302. package/dist/db/tables/memberships.d.ts +8 -0
  303. package/dist/db/tables/memberships.d.ts.map +1 -1
  304. package/dist/db/tables/memberships.js +51 -2
  305. package/dist/db/tables/memberships.js.map +1 -1
  306. package/dist/db/tables/organizations.d.ts +51 -5
  307. package/dist/db/tables/organizations.d.ts.map +1 -1
  308. package/dist/db/tables/organizations.js +135 -47
  309. package/dist/db/tables/organizations.js.map +1 -1
  310. package/dist/db/tables/projects.d.ts +27 -1
  311. package/dist/db/tables/projects.d.ts.map +1 -1
  312. package/dist/db/tables/projects.js +38 -3
  313. package/dist/db/tables/projects.js.map +1 -1
  314. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  315. package/dist/db/tables/requestUsage.js.map +1 -1
  316. package/dist/db/tables/rewardAccounts.d.ts +86 -0
  317. package/dist/db/tables/rewardAccounts.d.ts.map +1 -0
  318. package/dist/db/tables/rewardAccounts.js +88 -0
  319. package/dist/db/tables/rewardAccounts.js.map +1 -0
  320. package/dist/db/tables/rewardCampaignDistributors.d.ts +36 -0
  321. package/dist/db/tables/rewardCampaignDistributors.d.ts.map +1 -0
  322. package/dist/db/tables/rewardCampaignDistributors.js +30 -0
  323. package/dist/db/tables/rewardCampaignDistributors.js.map +1 -0
  324. package/dist/db/tables/rewardCampaigns.d.ts +171 -0
  325. package/dist/db/tables/rewardCampaigns.d.ts.map +1 -0
  326. package/dist/db/tables/rewardCampaigns.js +392 -0
  327. package/dist/db/tables/rewardCampaigns.js.map +1 -0
  328. package/dist/db/tables/rewardCredits.d.ts +53 -0
  329. package/dist/db/tables/rewardCredits.d.ts.map +1 -0
  330. package/dist/db/tables/rewardCredits.js +63 -0
  331. package/dist/db/tables/rewardCredits.js.map +1 -0
  332. package/dist/db/tables/rewardEligibilityAssociations.d.ts +61 -0
  333. package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -0
  334. package/dist/db/tables/rewardEligibilityAssociations.js +103 -0
  335. package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -0
  336. package/dist/db/tables/rewardRuns.d.ts +188 -0
  337. package/dist/db/tables/rewardRuns.d.ts.map +1 -0
  338. package/dist/db/tables/rewardRuns.js +278 -0
  339. package/dist/db/tables/rewardRuns.js.map +1 -0
  340. package/dist/db/tables/rewardTransactionAttempts.d.ts +100 -0
  341. package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -0
  342. package/dist/db/tables/rewardTransactionAttempts.js +163 -0
  343. package/dist/db/tables/rewardTransactionAttempts.js.map +1 -0
  344. package/dist/db/tables/routesCatalog.d.ts +58 -0
  345. package/dist/db/tables/routesCatalog.d.ts.map +1 -0
  346. package/dist/db/tables/{fundingCatalog.js → routesCatalog.js} +19 -19
  347. package/dist/db/tables/routesCatalog.js.map +1 -0
  348. package/dist/db/tables/routesDepositAddresses.d.ts +476 -0
  349. package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -0
  350. package/dist/db/tables/routesDepositAddresses.js +587 -0
  351. package/dist/db/tables/routesDepositAddresses.js.map +1 -0
  352. package/dist/db/tables/{fundingDepositRequestObservations.d.ts → routesDepositRequestObservations.d.ts} +6 -6
  353. package/dist/db/tables/routesDepositRequestObservations.d.ts.map +1 -0
  354. package/dist/db/tables/{fundingDepositRequestObservations.js → routesDepositRequestObservations.js} +6 -6
  355. package/dist/db/tables/routesDepositRequestObservations.js.map +1 -0
  356. package/dist/db/tables/{fundingDeposits.d.ts → routesDeposits.d.ts} +210 -22
  357. package/dist/db/tables/routesDeposits.d.ts.map +1 -0
  358. package/dist/db/tables/routesDeposits.js +576 -0
  359. package/dist/db/tables/routesDeposits.js.map +1 -0
  360. package/dist/db/tables/routesIdempotency.d.ts +166 -0
  361. package/dist/db/tables/routesIdempotency.d.ts.map +1 -0
  362. package/dist/db/tables/routesIdempotency.js +330 -0
  363. package/dist/db/tables/routesIdempotency.js.map +1 -0
  364. package/dist/db/tables/routesSubsidies.d.ts +27 -0
  365. package/dist/db/tables/routesSubsidies.d.ts.map +1 -0
  366. package/dist/db/tables/routesSubsidies.js +45 -0
  367. package/dist/db/tables/routesSubsidies.js.map +1 -0
  368. package/dist/db/tables/{fundingTransferEvents.d.ts → routesTransferEvents.d.ts} +3 -3
  369. package/dist/db/tables/routesTransferEvents.d.ts.map +1 -0
  370. package/dist/db/tables/{fundingTransferEvents.js → routesTransferEvents.js} +3 -3
  371. package/dist/db/tables/routesTransferEvents.js.map +1 -0
  372. package/dist/db/tables/routesTransferSubsidies.d.ts +19 -0
  373. package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -0
  374. package/dist/db/tables/routesTransferSubsidies.js +17 -0
  375. package/dist/db/tables/routesTransferSubsidies.js.map +1 -0
  376. package/dist/db/tables/routesTransferSubsidyNonces.d.ts +51 -0
  377. package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -0
  378. package/dist/db/tables/routesTransferSubsidyNonces.js +82 -0
  379. package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -0
  380. package/dist/db/tables/routesTransferTransactions.d.ts +77 -0
  381. package/dist/db/tables/routesTransferTransactions.d.ts.map +1 -0
  382. package/dist/db/tables/routesTransferTransactions.js +86 -0
  383. package/dist/db/tables/routesTransferTransactions.js.map +1 -0
  384. package/dist/db/tables/routesTransfers.d.ts +319 -0
  385. package/dist/db/tables/routesTransfers.d.ts.map +1 -0
  386. package/dist/db/tables/routesTransfers.js +372 -0
  387. package/dist/db/tables/routesTransfers.js.map +1 -0
  388. package/dist/db/tables/sponsoredTransactions.d.ts +112 -13
  389. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  390. package/dist/db/tables/sponsoredTransactions.js +241 -38
  391. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  392. package/dist/db/tables/sponsorshipAttributions.d.ts +69 -0
  393. package/dist/db/tables/sponsorshipAttributions.d.ts.map +1 -0
  394. package/dist/db/tables/sponsorshipAttributions.js +77 -0
  395. package/dist/db/tables/sponsorshipAttributions.js.map +1 -0
  396. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts +22 -0
  397. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts.map +1 -0
  398. package/dist/db/tables/sponsorshipReconciliationCursors.js +45 -0
  399. package/dist/db/tables/sponsorshipReconciliationCursors.js.map +1 -0
  400. package/dist/db/tables/stripeCustomers.d.ts +11 -0
  401. package/dist/db/tables/stripeCustomers.d.ts.map +1 -1
  402. package/dist/db/tables/stripeCustomers.js +13 -0
  403. package/dist/db/tables/stripeCustomers.js.map +1 -1
  404. package/dist/db/tables/users.d.ts +17 -1
  405. package/dist/db/tables/users.d.ts.map +1 -1
  406. package/dist/db/tables/users.js +47 -2
  407. package/dist/db/tables/users.js.map +1 -1
  408. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  409. package/dist/db/tables/webhookQueueEvents.js +5 -1
  410. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  411. package/dist/db/tables/webhookSubscriptions.d.ts +22 -10
  412. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  413. package/dist/db/tables/webhookSubscriptions.js +50 -15
  414. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  415. package/dist/handlers/internal/executionError.d.ts +2 -2
  416. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  417. package/dist/handlers/internal/executionError.js +1 -2
  418. package/dist/handlers/internal/executionError.js.map +1 -1
  419. package/dist/handlers/internal/multisig.d.ts +4 -232
  420. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  421. package/dist/handlers/internal/multisig.js +11 -537
  422. package/dist/handlers/internal/multisig.js.map +1 -1
  423. package/dist/handlers/internal/sponsorship.d.ts +19 -8
  424. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  425. package/dist/handlers/internal/sponsorship.js +13 -9
  426. package/dist/handlers/internal/sponsorship.js.map +1 -1
  427. package/dist/handlers/relay.d.ts +9 -12
  428. package/dist/handlers/relay.d.ts.map +1 -1
  429. package/dist/handlers/relay.js +81 -85
  430. package/dist/handlers/relay.js.map +1 -1
  431. package/dist/index.d.ts +0 -17
  432. package/dist/index.d.ts.map +1 -1
  433. package/dist/index.js +0 -17
  434. package/dist/index.js.map +1 -1
  435. package/dist/internal/AssetResponse.d.ts +12 -0
  436. package/dist/internal/AssetResponse.d.ts.map +1 -0
  437. package/dist/internal/AssetResponse.js +21 -0
  438. package/dist/internal/AssetResponse.js.map +1 -0
  439. package/dist/internal/Auth.d.ts +48 -12
  440. package/dist/internal/Auth.d.ts.map +1 -1
  441. package/dist/internal/Auth.js +603 -175
  442. package/dist/internal/Auth.js.map +1 -1
  443. package/dist/internal/BetterAuth.d.ts +1488 -0
  444. package/dist/internal/BetterAuth.d.ts.map +1 -0
  445. package/dist/internal/BetterAuth.js +253 -0
  446. package/dist/internal/BetterAuth.js.map +1 -0
  447. package/dist/internal/Cache.d.ts +2 -0
  448. package/dist/internal/Cache.d.ts.map +1 -1
  449. package/dist/internal/Cache.js +2 -0
  450. package/dist/internal/Cache.js.map +1 -1
  451. package/dist/internal/EarnRates.d.ts +26 -3
  452. package/dist/internal/EarnRates.d.ts.map +1 -1
  453. package/dist/internal/EarnRates.js +84 -19
  454. package/dist/internal/EarnRates.js.map +1 -1
  455. package/dist/internal/EarnSharePrices.d.ts +120 -0
  456. package/dist/internal/EarnSharePrices.d.ts.map +1 -0
  457. package/dist/internal/EarnSharePrices.js +179 -0
  458. package/dist/internal/EarnSharePrices.js.map +1 -0
  459. package/dist/internal/EarnVaults.d.ts +4 -0
  460. package/dist/internal/EarnVaults.d.ts.map +1 -1
  461. package/dist/internal/EarnVaults.js +31 -14
  462. package/dist/internal/EarnVaults.js.map +1 -1
  463. package/dist/internal/Email.d.ts +37 -0
  464. package/dist/internal/Email.d.ts.map +1 -0
  465. package/dist/internal/Email.js +16 -0
  466. package/dist/internal/Email.js.map +1 -0
  467. package/dist/internal/Log.d.ts +32 -28
  468. package/dist/internal/Log.d.ts.map +1 -1
  469. package/dist/internal/Log.js +18 -11
  470. package/dist/internal/Log.js.map +1 -1
  471. package/dist/internal/MetricSink.d.ts +3 -0
  472. package/dist/internal/MetricSink.d.ts.map +1 -1
  473. package/dist/internal/MetricSink.js +67 -8
  474. package/dist/internal/MetricSink.js.map +1 -1
  475. package/dist/internal/Mpp.d.ts.map +1 -1
  476. package/dist/internal/Mpp.js +20 -25
  477. package/dist/internal/Mpp.js.map +1 -1
  478. package/dist/internal/OpenApi.d.ts +26 -3
  479. package/dist/internal/OpenApi.d.ts.map +1 -1
  480. package/dist/internal/OpenApi.js +25 -10
  481. package/dist/internal/OpenApi.js.map +1 -1
  482. package/dist/internal/Provider.d.ts +1 -1
  483. package/dist/internal/Provider.d.ts.map +1 -1
  484. package/dist/internal/RateLimit.d.ts.map +1 -1
  485. package/dist/internal/RateLimit.js +13 -1
  486. package/dist/internal/RateLimit.js.map +1 -1
  487. package/dist/internal/Store.d.ts +16 -0
  488. package/dist/internal/Store.d.ts.map +1 -1
  489. package/dist/internal/Store.js +59 -8
  490. package/dist/internal/Store.js.map +1 -1
  491. package/dist/internal/Tidx.d.ts +9 -2
  492. package/dist/internal/Tidx.d.ts.map +1 -1
  493. package/dist/internal/Tidx.js +78 -11
  494. package/dist/internal/Tidx.js.map +1 -1
  495. package/dist/internal/Timing.d.ts +26 -0
  496. package/dist/internal/Timing.d.ts.map +1 -1
  497. package/dist/internal/Timing.js +63 -5
  498. package/dist/internal/Timing.js.map +1 -1
  499. package/dist/internal/VercelOidc.d.ts +94 -0
  500. package/dist/internal/VercelOidc.d.ts.map +1 -0
  501. package/dist/internal/VercelOidc.js +128 -0
  502. package/dist/internal/VercelOidc.js.map +1 -0
  503. package/dist/internal/VerifiedTokens.d.ts +2 -8
  504. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  505. package/dist/internal/VerifiedTokens.js +24 -24
  506. package/dist/internal/VerifiedTokens.js.map +1 -1
  507. package/dist/internal/Viem.d.ts +46 -2068
  508. package/dist/internal/Viem.d.ts.map +1 -1
  509. package/dist/internal/Viem.js +171 -11
  510. package/dist/internal/Viem.js.map +1 -1
  511. package/dist/internal/Webhooks.d.ts +12 -4
  512. package/dist/internal/Webhooks.d.ts.map +1 -1
  513. package/dist/internal/Webhooks.js +9 -3
  514. package/dist/internal/Webhooks.js.map +1 -1
  515. package/dist/internal/ZoneRpc.d.ts +7 -0
  516. package/dist/internal/ZoneRpc.d.ts.map +1 -0
  517. package/dist/internal/ZoneRpc.js +49 -0
  518. package/dist/internal/ZoneRpc.js.map +1 -0
  519. package/dist/internal/index.d.ts +3 -1
  520. package/dist/internal/index.d.ts.map +1 -1
  521. package/dist/internal/index.js +3 -1
  522. package/dist/internal/index.js.map +1 -1
  523. package/dist/internal/rewards/Audit.d.ts +88 -0
  524. package/dist/internal/rewards/Audit.d.ts.map +1 -0
  525. package/dist/internal/rewards/Audit.js +134 -0
  526. package/dist/internal/rewards/Audit.js.map +1 -0
  527. package/dist/internal/rewards/Campaigns.d.ts +572 -0
  528. package/dist/internal/rewards/Campaigns.d.ts.map +1 -0
  529. package/dist/internal/rewards/Campaigns.js +531 -0
  530. package/dist/internal/rewards/Campaigns.js.map +1 -0
  531. package/dist/internal/rewards/Credits.d.ts +76 -0
  532. package/dist/internal/rewards/Credits.d.ts.map +1 -0
  533. package/dist/internal/rewards/Credits.js +95 -0
  534. package/dist/internal/rewards/Credits.js.map +1 -0
  535. package/dist/internal/rewards/Events.d.ts +100 -0
  536. package/dist/internal/rewards/Events.d.ts.map +1 -0
  537. package/dist/internal/rewards/Events.js +340 -0
  538. package/dist/internal/rewards/Events.js.map +1 -0
  539. package/dist/internal/rewards/Projection.d.ts +237 -0
  540. package/dist/internal/rewards/Projection.d.ts.map +1 -0
  541. package/dist/internal/rewards/Projection.js +616 -0
  542. package/dist/internal/rewards/Projection.js.map +1 -0
  543. package/dist/internal/rewards/Rates.d.ts +44 -0
  544. package/dist/internal/rewards/Rates.d.ts.map +1 -0
  545. package/dist/internal/rewards/Rates.js +60 -0
  546. package/dist/internal/rewards/Rates.js.map +1 -0
  547. package/dist/internal/rewards/Read.d.ts +158 -0
  548. package/dist/internal/rewards/Read.d.ts.map +1 -0
  549. package/dist/internal/rewards/Read.js +292 -0
  550. package/dist/internal/rewards/Read.js.map +1 -0
  551. package/dist/internal/rewards/Runner.d.ts +164 -0
  552. package/dist/internal/rewards/Runner.d.ts.map +1 -0
  553. package/dist/internal/rewards/Runner.js +1778 -0
  554. package/dist/internal/rewards/Runner.js.map +1 -0
  555. package/dist/internal/rewards/Signer.d.ts +224 -0
  556. package/dist/internal/rewards/Signer.d.ts.map +1 -0
  557. package/dist/internal/rewards/Signer.js +135 -0
  558. package/dist/internal/rewards/Signer.js.map +1 -0
  559. package/dist/internal/rewards/index.d.ts +15 -0
  560. package/dist/internal/rewards/index.d.ts.map +1 -0
  561. package/dist/internal/rewards/index.js +15 -0
  562. package/dist/internal/rewards/index.js.map +1 -0
  563. package/dist/internal/{funding → routes}/Action.d.ts +12 -1
  564. package/dist/internal/routes/Action.d.ts.map +1 -0
  565. package/dist/internal/{funding → routes}/Action.js +17 -9
  566. package/dist/internal/routes/Action.js.map +1 -0
  567. package/dist/internal/{funding → routes}/Catalog.d.ts +20 -20
  568. package/dist/internal/routes/Catalog.d.ts.map +1 -0
  569. package/dist/internal/{funding → routes}/Catalog.js +29 -29
  570. package/dist/internal/routes/Catalog.js.map +1 -0
  571. package/dist/internal/{funding → routes}/Chain.d.ts +10 -10
  572. package/dist/internal/routes/Chain.d.ts.map +1 -0
  573. package/dist/internal/{funding → routes}/Chain.js +3 -3
  574. package/dist/internal/routes/Chain.js.map +1 -0
  575. package/dist/internal/{funding → routes}/Deposit.d.ts +45 -23
  576. package/dist/internal/routes/Deposit.d.ts.map +1 -0
  577. package/dist/internal/{funding → routes}/Deposit.js +60 -27
  578. package/dist/internal/routes/Deposit.js.map +1 -0
  579. package/dist/internal/{funding → routes}/DepositAddress.d.ts +56 -27
  580. package/dist/internal/routes/DepositAddress.d.ts.map +1 -0
  581. package/dist/internal/{funding → routes}/DepositAddress.js +95 -33
  582. package/dist/internal/routes/DepositAddress.js.map +1 -0
  583. package/dist/internal/{funding → routes}/DepositWebhook.d.ts +4 -4
  584. package/dist/internal/routes/DepositWebhook.d.ts.map +1 -0
  585. package/dist/internal/{funding → routes}/DepositWebhook.js +4 -3
  586. package/dist/internal/routes/DepositWebhook.js.map +1 -0
  587. package/dist/internal/routes/Economics.d.ts +25 -0
  588. package/dist/internal/routes/Economics.d.ts.map +1 -0
  589. package/dist/internal/routes/Economics.js +28 -0
  590. package/dist/internal/routes/Economics.js.map +1 -0
  591. package/dist/internal/{funding → routes}/Evidence.d.ts +3 -3
  592. package/dist/internal/routes/Evidence.d.ts.map +1 -0
  593. package/dist/internal/{funding → routes}/Evidence.js +9 -17
  594. package/dist/internal/routes/Evidence.js.map +1 -0
  595. package/dist/internal/routes/Observability.d.ts +154 -0
  596. package/dist/internal/routes/Observability.d.ts.map +1 -0
  597. package/dist/internal/routes/Observability.js +389 -0
  598. package/dist/internal/routes/Observability.js.map +1 -0
  599. package/dist/internal/{funding → routes}/Provider.d.ts +350 -104
  600. package/dist/internal/routes/Provider.d.ts.map +1 -0
  601. package/dist/internal/{funding → routes}/Provider.js +233 -97
  602. package/dist/internal/routes/Provider.js.map +1 -0
  603. package/dist/internal/{funding → routes}/Reconciliation.d.ts +11 -6
  604. package/dist/internal/routes/Reconciliation.d.ts.map +1 -0
  605. package/dist/internal/{funding → routes}/Reconciliation.js +408 -158
  606. package/dist/internal/routes/Reconciliation.js.map +1 -0
  607. package/dist/internal/routes/Route.d.ts +21 -0
  608. package/dist/internal/routes/Route.d.ts.map +1 -0
  609. package/dist/internal/{funding → routes}/Route.js +1 -1
  610. package/dist/internal/routes/Route.js.map +1 -0
  611. package/dist/internal/{funding → routes}/SourceObservation.d.ts +6 -6
  612. package/dist/internal/routes/SourceObservation.d.ts.map +1 -0
  613. package/dist/internal/{funding → routes}/SourceObservation.js +13 -15
  614. package/dist/internal/routes/SourceObservation.js.map +1 -0
  615. package/dist/internal/{funding → routes}/Subsidy.d.ts +48 -3
  616. package/dist/internal/routes/Subsidy.d.ts.map +1 -0
  617. package/dist/internal/{funding → routes}/Subsidy.js +43 -3
  618. package/dist/internal/routes/Subsidy.js.map +1 -0
  619. package/dist/internal/{funding → routes}/Token.d.ts +4 -4
  620. package/dist/internal/routes/Token.d.ts.map +1 -0
  621. package/dist/internal/{funding → routes}/Token.js +3 -3
  622. package/dist/internal/routes/Token.js.map +1 -0
  623. package/dist/internal/{funding → routes}/Transfer.d.ts +324 -34
  624. package/dist/internal/routes/Transfer.d.ts.map +1 -0
  625. package/dist/internal/routes/Transfer.js +728 -0
  626. package/dist/internal/routes/Transfer.js.map +1 -0
  627. package/dist/internal/routes/TransferReconciliation.d.ts +55 -0
  628. package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -0
  629. package/dist/internal/routes/TransferReconciliation.js +422 -0
  630. package/dist/internal/routes/TransferReconciliation.js.map +1 -0
  631. package/dist/internal/routes/TransferSubsidy.d.ts +201 -0
  632. package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -0
  633. package/dist/internal/routes/TransferSubsidy.js +256 -0
  634. package/dist/internal/routes/TransferSubsidy.js.map +1 -0
  635. package/dist/internal/routes/TransferWebhook.d.ts +21 -0
  636. package/dist/internal/routes/TransferWebhook.d.ts.map +1 -0
  637. package/dist/internal/routes/TransferWebhook.js +43 -0
  638. package/dist/internal/routes/TransferWebhook.js.map +1 -0
  639. package/dist/internal/{funding → routes}/index.d.ts +3 -0
  640. package/dist/internal/routes/index.d.ts.map +1 -0
  641. package/dist/internal/{funding → routes}/index.js +3 -0
  642. package/dist/internal/routes/index.js.map +1 -0
  643. package/dist/internal/{funding → routes}/providers/across.d.ts +4 -4
  644. package/dist/internal/routes/providers/across.d.ts.map +1 -0
  645. package/dist/internal/{funding → routes}/providers/across.js +13 -13
  646. package/dist/internal/routes/providers/across.js.map +1 -0
  647. package/dist/internal/{funding → routes}/providers/relay.d.ts +21 -12
  648. package/dist/internal/routes/providers/relay.d.ts.map +1 -0
  649. package/dist/internal/{funding → routes}/providers/relay.js +197 -122
  650. package/dist/internal/routes/providers/relay.js.map +1 -0
  651. package/dist/internal/{funding → routes}/providers/rhino.d.ts +4 -4
  652. package/dist/internal/routes/providers/rhino.d.ts.map +1 -0
  653. package/dist/internal/{funding → routes}/providers/rhino.js +10 -10
  654. package/dist/internal/routes/providers/rhino.js.map +1 -0
  655. package/dist/internal/{funding → routes}/providers/squid.d.ts +4 -4
  656. package/dist/internal/routes/providers/squid.d.ts.map +1 -0
  657. package/dist/internal/{funding → routes}/providers/squid.js +14 -14
  658. package/dist/internal/routes/providers/squid.js.map +1 -0
  659. package/dist/internal/routes/providers/stargate.d.ts +165 -0
  660. package/dist/internal/routes/providers/stargate.d.ts.map +1 -0
  661. package/dist/internal/routes/providers/stargate.js +898 -0
  662. package/dist/internal/routes/providers/stargate.js.map +1 -0
  663. package/dist/internal/{funding → routes}/providers/symbiosis.d.ts +4 -4
  664. package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -0
  665. package/dist/internal/{funding → routes}/providers/symbiosis.js +30 -20
  666. package/dist/internal/routes/providers/symbiosis.js.map +1 -0
  667. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
  668. package/dist/internal/webhookDestinations/slack.js +22 -6
  669. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  670. package/dist/providers/index.d.ts +12 -12
  671. package/dist/providers/index.d.ts.map +1 -1
  672. package/dist/providers/index.js +12 -12
  673. package/dist/providers/index.js.map +1 -1
  674. package/dist/server.d.ts +19 -0
  675. package/dist/server.d.ts.map +1 -0
  676. package/dist/server.js +19 -0
  677. package/dist/server.js.map +1 -0
  678. package/package.json +15 -4
  679. package/src/ApiKey.ts +1 -1
  680. package/src/ApiKeys.test.ts +1033 -3
  681. package/src/ApiKeys.ts +489 -48
  682. package/src/App.test-d.ts +1 -1
  683. package/src/App.test.ts +216 -25
  684. package/src/App.ts +335 -31
  685. package/src/Assets.test.ts +2 -2
  686. package/src/Assets.ts +3 -1
  687. package/src/Auth.test-d.ts +1 -1
  688. package/src/Client.test-d.ts +611 -101
  689. package/src/Handler.ts +0 -1
  690. package/src/OpenApi.contract.test.ts +195 -0
  691. package/src/Scope.ts +17 -5
  692. package/src/Webhooks.ts +2 -2
  693. package/src/admin/App.test.ts +30 -2
  694. package/src/admin/App.ts +42 -6
  695. package/src/admin/apps/api-keys.test.ts +267 -5
  696. package/src/admin/apps/api-keys.ts +100 -50
  697. package/src/admin/apps/earn-vaults.test.ts +158 -0
  698. package/src/admin/apps/earn-vaults.ts +681 -7
  699. package/src/admin/apps/organizations.test.ts +342 -1
  700. package/src/admin/apps/organizations.ts +368 -8
  701. package/src/admin/apps/routes-support.test.ts +200 -0
  702. package/src/admin/apps/routes-support.ts +201 -0
  703. package/src/admin/apps/routes.test.ts +450 -0
  704. package/src/admin/apps/routes.ts +896 -0
  705. package/src/admin/apps/scopes.test.ts +15 -5
  706. package/src/admin/apps/subsidies.test.ts +306 -0
  707. package/src/admin/apps/subsidies.ts +457 -0
  708. package/src/admin/apps/verified-tokens.test.ts +8 -3
  709. package/src/admin/apps/verified-tokens.ts +3 -3
  710. package/src/admin/ui/plugin.ts +1 -2
  711. package/src/admin/ui/src/components/RewardCampaignForm.tsx +1009 -0
  712. package/src/admin/ui/src/lib/config.ts +11 -2
  713. package/src/admin/ui/src/lib/earn-vaults.ts +2 -2
  714. package/src/admin/ui/src/lib/organizations.test.ts +147 -3
  715. package/src/admin/ui/src/lib/organizations.ts +136 -0
  716. package/src/admin/ui/src/lib/query-client.ts +1 -1
  717. package/src/admin/ui/src/lib/reward-campaigns.ts +136 -0
  718. package/src/admin/ui/src/lib/routes.test.ts +88 -0
  719. package/src/admin/ui/src/lib/routes.ts +304 -0
  720. package/src/admin/ui/src/lib/subsidies.ts +75 -0
  721. package/src/admin/ui/src/routeTree.gen.ts +63 -0
  722. package/src/admin/ui/src/routes/__root.tsx +12 -1
  723. package/src/admin/ui/src/routes/api-keys.tsx +196 -84
  724. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +105 -70
  725. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +334 -2
  726. package/src/admin/ui/src/routes/routes.tsx +1615 -0
  727. package/src/admin/ui/src/routes/routes_.support.tsx +248 -0
  728. package/src/admin/ui/src/routes/subsidies.tsx +799 -0
  729. package/src/admin/ui.generated.ts +144 -69
  730. package/src/analytics/Analytics.test.ts +108 -18
  731. package/src/analytics/Analytics.ts +38 -6
  732. package/src/analytics/tables/requestEvents.test.ts +100 -0
  733. package/src/analytics/tables/requestEvents.ts +10 -1
  734. package/src/analytics/tables/routesLifecycleIntervals.ts +140 -0
  735. package/src/apps/Zones.test.ts +18 -3
  736. package/src/apps/Zones.ts +55 -8
  737. package/src/apps/composition.test.ts +68 -59
  738. package/src/apps/data/App.test.ts +154 -2
  739. package/src/apps/data/App.ts +102 -33
  740. package/src/apps/data/routes/activities.test.ts +419 -7
  741. package/src/apps/data/routes/activities.ts +683 -216
  742. package/src/apps/data/routes/balances.test.ts +2 -2
  743. package/src/apps/data/routes/blocks.test.ts +46 -1
  744. package/src/apps/data/routes/coingecko.test.ts +419 -1
  745. package/src/apps/data/routes/coingecko.ts +309 -253
  746. package/src/apps/data/routes/earn.discovery.test.ts +274 -0
  747. package/src/apps/data/routes/earn.test.ts +1661 -29
  748. package/src/apps/data/routes/earn.ts +2314 -328
  749. package/src/apps/data/routes/exchanges.test.ts +752 -30
  750. package/src/apps/data/routes/exchanges.ts +954 -753
  751. package/src/apps/data/routes/fee-amm.test.ts +224 -0
  752. package/src/apps/data/routes/fee-amm.ts +208 -178
  753. package/src/apps/data/routes/indexer.test.ts +389 -0
  754. package/src/apps/data/routes/indexer.ts +165 -69
  755. package/src/apps/data/routes/receipts.test.ts +1 -1
  756. package/src/apps/data/routes/rpc.test.ts +352 -8
  757. package/src/apps/data/routes/rpc.ts +407 -219
  758. package/src/apps/data/routes/rpc.zone.test.ts +340 -0
  759. package/src/apps/data/routes/tokenlist.test.ts +285 -0
  760. package/src/apps/data/routes/tokenlist.ts +173 -88
  761. package/src/apps/data/routes/tokens.test.ts +93 -3
  762. package/src/apps/data/routes/tokens.ts +39 -23
  763. package/src/apps/data/routes/transactions.test.ts +1 -1
  764. package/src/apps/data/routes/transfers.test.ts +56 -24
  765. package/src/apps/data/routes/transfers.ts +19 -8
  766. package/src/apps/data/routes/valuation.test.ts +1 -1
  767. package/src/apps/data/routes/verified-tokens.test.ts +345 -0
  768. package/src/apps/data/routes/verified-tokens.ts +99 -29
  769. package/src/apps/data/routes/webhooks.test.ts +659 -81
  770. package/src/apps/data/routes/webhooks.ts +1104 -817
  771. package/src/apps/data/routes/zones.test.ts +148 -71
  772. package/src/apps/data/routes/zones.ts +71 -55
  773. package/src/apps/docs/reference.ts +2 -2
  774. package/src/apps/index.ts +1 -1
  775. package/src/apps/management/App.ts +9 -51
  776. package/src/apps/management/Billing.test.ts +375 -14
  777. package/src/apps/management/Billing.ts +283 -34
  778. package/src/apps/management/routes/api-keys.test.ts +269 -7
  779. package/src/apps/management/routes/api-keys.ts +61 -20
  780. package/src/apps/management/routes/billing.test.ts +477 -15
  781. package/src/apps/management/routes/billing.ts +377 -50
  782. package/src/apps/management/routes/faucet.test.ts +1 -1
  783. package/src/apps/management/routes/faucet.ts +1 -1
  784. package/src/apps/management/routes/invitations.test.ts +270 -3
  785. package/src/apps/management/routes/invitations.ts +185 -15
  786. package/src/apps/management/routes/invoicePreview.test.ts +549 -0
  787. package/src/apps/management/routes/me.test.ts +246 -5
  788. package/src/apps/management/routes/me.ts +7 -2
  789. package/src/apps/management/routes/members.test.ts +108 -0
  790. package/src/apps/management/routes/orgs.test.ts +191 -3
  791. package/src/apps/management/routes/orgs.ts +58 -11
  792. package/src/apps/management/routes/projects.test.ts +33 -3
  793. package/src/apps/management/routes/projects.ts +6 -1
  794. package/src/apps/management/routes/scopes.test.ts +30 -2
  795. package/src/apps/management/routes/scopes.ts +4 -1
  796. package/src/apps/management/routes/usage.test.ts +32 -0
  797. package/src/apps/management/routes/usage.ts +22 -4
  798. package/src/apps/management/routes/webhooks.test.ts +49 -26
  799. package/src/apps/management/routes/webhooks.ts +93 -38
  800. package/src/apps/mcp.test.ts +6 -6
  801. package/src/apps/mcp.ts +7 -7
  802. package/src/apps/mpp/App.ts +37 -7
  803. package/src/apps/mpp/Mpp.test.ts +49 -8
  804. package/src/apps/mpp/Mpp.zone.test.ts +219 -0
  805. package/src/apps/mpp/internal/Adapter.ts +34 -0
  806. package/src/apps/mpp/internal/Policy.test.ts +86 -31
  807. package/src/apps/mpp/internal/Policy.ts +49 -7
  808. package/src/apps/mpp/internal/Sponsorship.test.ts +207 -0
  809. package/src/apps/mpp/internal/Sponsorship.ts +9 -3
  810. package/src/apps/relay/App.test.ts +1024 -38
  811. package/src/apps/relay/App.ts +228 -32
  812. package/src/apps/relay/App.zone-billing.test.ts +257 -0
  813. package/src/apps/relay/Sponsorships.test.ts +67 -4
  814. package/src/apps/relay/Sponsorships.ts +253 -29
  815. package/src/apps/relay/Sponsorships.zone.test.ts +102 -0
  816. package/src/apps/{funding → routes}/Access.ts +6 -6
  817. package/src/apps/routes/App.test.ts +165 -0
  818. package/src/apps/routes/App.ts +96 -0
  819. package/src/apps/{funding → routes}/Inventory.ts +2 -2
  820. package/src/apps/routes/index.ts +1 -0
  821. package/src/apps/{funding → routes}/routes/chains.test.ts +66 -66
  822. package/src/apps/{funding → routes}/routes/chains.ts +23 -23
  823. package/src/apps/{funding → routes}/routes/deposit-addresses.test.ts +1362 -462
  824. package/src/apps/{funding → routes}/routes/deposit-addresses.ts +568 -248
  825. package/src/apps/{funding → routes}/routes/providers.test.ts +27 -33
  826. package/src/apps/{funding → routes}/routes/providers.ts +23 -23
  827. package/src/apps/{funding → routes}/routes/quotes.test.ts +298 -41
  828. package/src/apps/{funding → routes}/routes/quotes.ts +57 -40
  829. package/src/apps/routes/routes/transfers.test.ts +2876 -0
  830. package/src/apps/routes/routes/transfers.ts +1332 -0
  831. package/src/cli/Cli.test.ts +1 -1
  832. package/src/cli/commands/admin.test.ts +748 -1
  833. package/src/cli/commands/admin.ts +278 -0
  834. package/src/cloudflare.ts +52 -47
  835. package/src/db/Db.test.ts +245 -2
  836. package/src/db/Db.ts +1511 -34
  837. package/src/db/Schema.ts +744 -228
  838. package/src/db/tables/apiKeyAdmissionBootstraps.ts +4 -0
  839. package/src/db/tables/apiKeyAdmissions.ts +218 -0
  840. package/src/db/tables/apiKeyOwnerAdmissions.ts +4 -0
  841. package/src/db/tables/apiKeyOwnerTombstones.ts +81 -0
  842. package/src/db/tables/apiKeyRevocations.test.ts +74 -0
  843. package/src/db/tables/apiKeyRevocations.ts +121 -0
  844. package/src/db/tables/authAccounts.ts +105 -0
  845. package/src/db/tables/authSessions.ts +13 -0
  846. package/src/db/tables/authVerifications.ts +13 -0
  847. package/src/db/tables/invitations.ts +142 -45
  848. package/src/db/tables/memberships.ts +79 -3
  849. package/src/db/tables/organizations.test.ts +725 -7
  850. package/src/db/tables/organizations.ts +182 -49
  851. package/src/db/tables/projects.test.ts +71 -0
  852. package/src/db/tables/projects.ts +59 -4
  853. package/src/db/tables/requestUsage.ts +4 -1
  854. package/src/db/tables/rewardAccounts.ts +178 -0
  855. package/src/db/tables/rewardCampaignDistributors.ts +68 -0
  856. package/src/db/tables/rewardCampaigns.test.ts +1388 -0
  857. package/src/db/tables/rewardCampaigns.ts +647 -0
  858. package/src/db/tables/rewardCredits.ts +110 -0
  859. package/src/db/tables/rewardEligibilityAssociations.test.ts +271 -0
  860. package/src/db/tables/rewardEligibilityAssociations.ts +184 -0
  861. package/src/db/tables/rewardRuns.ts +514 -0
  862. package/src/db/tables/rewardTransactionAttempts.ts +272 -0
  863. package/src/db/tables/routesCatalog.test.ts +185 -0
  864. package/src/db/tables/routesCatalog.ts +163 -0
  865. package/src/db/tables/routesDepositAddresses.test.ts +1048 -0
  866. package/src/db/tables/routesDepositAddresses.ts +1008 -0
  867. package/src/db/tables/{fundingDepositRequestObservations.test.ts → routesDepositRequestObservations.test.ts} +13 -14
  868. package/src/db/tables/{fundingDepositRequestObservations.ts → routesDepositRequestObservations.ts} +10 -10
  869. package/src/db/tables/{fundingDeposits.test.ts → routesDeposits.test.ts} +316 -46
  870. package/src/db/tables/routesDeposits.ts +1062 -0
  871. package/src/db/tables/routesIdempotency.test.ts +796 -0
  872. package/src/db/tables/routesIdempotency.ts +496 -0
  873. package/src/db/tables/routesSubsidies.test.ts +120 -0
  874. package/src/db/tables/routesSubsidies.ts +71 -0
  875. package/src/db/tables/{fundingTransferEvents.ts → routesTransferEvents.ts} +4 -4
  876. package/src/db/tables/routesTransferSubsidies.ts +38 -0
  877. package/src/db/tables/routesTransferSubsidyNonces.ts +124 -0
  878. package/src/db/tables/routesTransferTransactions.test.ts +195 -0
  879. package/src/db/tables/routesTransferTransactions.ts +158 -0
  880. package/src/db/tables/routesTransfers.test.ts +338 -0
  881. package/src/db/tables/routesTransfers.ts +662 -0
  882. package/src/db/tables/sponsoredTransactions.test.ts +563 -1
  883. package/src/db/tables/sponsoredTransactions.ts +386 -42
  884. package/src/db/tables/sponsorshipAttributions.test.ts +160 -0
  885. package/src/db/tables/sponsorshipAttributions.ts +168 -0
  886. package/src/db/tables/sponsorshipReconciliationCursors.test.ts +115 -0
  887. package/src/db/tables/sponsorshipReconciliationCursors.ts +67 -0
  888. package/src/db/tables/stripeCustomers.test.ts +85 -0
  889. package/src/db/tables/stripeCustomers.ts +24 -0
  890. package/src/db/tables/users.test.ts +129 -0
  891. package/src/db/tables/users.ts +49 -3
  892. package/src/db/tables/webhookQueueEvents.ts +8 -1
  893. package/src/db/tables/webhookSubscriptions.ts +103 -21
  894. package/src/handlers/internal/executionError.ts +3 -4
  895. package/src/handlers/internal/multisig.ts +13 -816
  896. package/src/handlers/internal/sponsorship.ts +26 -10
  897. package/src/handlers/relay.test-d.ts +9 -1
  898. package/src/handlers/relay.test.ts +823 -3
  899. package/src/handlers/relay.ts +127 -107
  900. package/src/index.test-d.ts +7 -0
  901. package/src/index.ts +0 -17
  902. package/src/internal/AssetResponse.ts +26 -0
  903. package/src/internal/Auth.test.ts +1564 -9
  904. package/src/internal/Auth.ts +833 -239
  905. package/src/internal/BetterAuth.test.ts +242 -0
  906. package/src/internal/BetterAuth.ts +349 -0
  907. package/src/internal/Cache.ts +2 -0
  908. package/src/internal/EarnRates.test.ts +17 -0
  909. package/src/internal/EarnRates.ts +124 -31
  910. package/src/internal/EarnSharePrices.test.ts +182 -0
  911. package/src/internal/EarnSharePrices.ts +313 -0
  912. package/src/internal/EarnVaults.test.ts +21 -0
  913. package/src/internal/EarnVaults.ts +37 -15
  914. package/src/internal/Email.ts +54 -0
  915. package/src/internal/Idempotency.test.ts +2 -2
  916. package/src/internal/Log.test-d.ts +10 -7
  917. package/src/internal/Log.test.ts +114 -16
  918. package/src/internal/Log.ts +55 -40
  919. package/src/internal/MetricSink.test.ts +313 -26
  920. package/src/internal/MetricSink.ts +73 -8
  921. package/src/internal/Mpp.test.ts +31 -0
  922. package/src/internal/Mpp.ts +21 -24
  923. package/src/internal/OpenApi.ts +45 -12
  924. package/src/internal/Provider.test.ts +1 -1
  925. package/src/internal/Provider.ts +1 -1
  926. package/src/internal/RateLimit.test.ts +21 -3
  927. package/src/internal/RateLimit.ts +12 -1
  928. package/src/internal/Store.test-d.ts +1 -1
  929. package/src/internal/Store.test.ts +151 -8
  930. package/src/internal/Store.ts +76 -9
  931. package/src/internal/Tidx.test.ts +59 -8
  932. package/src/internal/Tidx.ts +86 -14
  933. package/src/internal/Timing.test.ts +77 -0
  934. package/src/internal/Timing.ts +83 -6
  935. package/src/internal/VercelOidc.test.ts +188 -0
  936. package/src/internal/VercelOidc.ts +193 -0
  937. package/src/internal/VerifiedTokens.test.ts +60 -6
  938. package/src/internal/VerifiedTokens.ts +21 -24
  939. package/src/internal/Viem.batch.test.ts +80 -0
  940. package/src/internal/Viem.test.ts +201 -2
  941. package/src/internal/Viem.ts +249 -17
  942. package/src/internal/Webhooks.test.ts +125 -1
  943. package/src/internal/Webhooks.ts +22 -4
  944. package/src/internal/ZoneRpc.ts +50 -0
  945. package/src/internal/exchange/Provider.test.ts +2 -2
  946. package/src/internal/index.test.ts +3 -1
  947. package/src/internal/index.ts +3 -1
  948. package/src/internal/rewards/Audit.ts +231 -0
  949. package/src/internal/rewards/Campaigns.test.ts +1234 -0
  950. package/src/internal/rewards/Campaigns.ts +1205 -0
  951. package/src/internal/rewards/Credits.ts +165 -0
  952. package/src/internal/rewards/Events.local.test.ts +113 -0
  953. package/src/internal/rewards/Events.test.ts +152 -0
  954. package/src/internal/rewards/Events.ts +468 -0
  955. package/src/internal/rewards/Projection.test.ts +681 -0
  956. package/src/internal/rewards/Projection.ts +987 -0
  957. package/src/internal/rewards/Rates.test.ts +171 -0
  958. package/src/internal/rewards/Rates.ts +110 -0
  959. package/src/internal/rewards/Read.test.ts +501 -0
  960. package/src/internal/rewards/Read.ts +476 -0
  961. package/src/internal/rewards/Runner.checkpoint.test.ts +80 -0
  962. package/src/internal/rewards/Runner.states.test.ts +188 -0
  963. package/src/internal/rewards/Runner.test.ts +90 -0
  964. package/src/internal/rewards/Runner.ts +2229 -0
  965. package/src/internal/rewards/Signer.test.ts +79 -0
  966. package/src/internal/rewards/Signer.ts +180 -0
  967. package/src/internal/rewards/index.ts +14 -0
  968. package/src/internal/{funding → routes}/Action.test.ts +63 -14
  969. package/src/internal/{funding → routes}/Action.ts +35 -8
  970. package/src/internal/{funding → routes}/Catalog.test.ts +3 -3
  971. package/src/internal/{funding → routes}/Catalog.ts +39 -39
  972. package/src/internal/{funding → routes}/Chain.test.ts +1 -1
  973. package/src/internal/{funding → routes}/Chain.ts +11 -11
  974. package/src/internal/{funding → routes}/Deposit.test.ts +1 -1
  975. package/src/internal/{funding → routes}/Deposit.ts +93 -45
  976. package/src/internal/{funding → routes}/DepositAddress.test.ts +11 -3
  977. package/src/internal/{funding → routes}/DepositAddress.ts +132 -53
  978. package/src/internal/{funding → routes}/DepositWebhook.test.ts +61 -18
  979. package/src/internal/{funding → routes}/DepositWebhook.ts +9 -8
  980. package/src/internal/routes/Economics.test.ts +42 -0
  981. package/src/internal/routes/Economics.ts +47 -0
  982. package/src/internal/{funding → routes}/Evidence.test.ts +6 -6
  983. package/src/internal/{funding → routes}/Evidence.ts +11 -22
  984. package/src/internal/routes/Observability.test.ts +636 -0
  985. package/src/internal/routes/Observability.ts +668 -0
  986. package/src/internal/{funding → routes}/Provider.test.ts +489 -153
  987. package/src/internal/{funding → routes}/Provider.ts +531 -185
  988. package/src/internal/{funding → routes}/Reconciliation.local.test.ts +84 -12
  989. package/src/internal/{funding → routes}/Reconciliation.test.ts +1213 -279
  990. package/src/internal/{funding → routes}/Reconciliation.ts +501 -203
  991. package/src/internal/routes/Reconciliation.unattributed.test.ts +250 -0
  992. package/src/internal/{funding → routes}/Route.ts +7 -7
  993. package/src/internal/{funding → routes}/SourceObservation.test.ts +13 -14
  994. package/src/internal/{funding → routes}/SourceObservation.ts +18 -22
  995. package/src/internal/{funding → routes}/Subsidy.local.test.ts +7 -7
  996. package/src/internal/routes/Subsidy.test.ts +169 -0
  997. package/src/internal/{funding → routes}/Subsidy.ts +86 -6
  998. package/src/internal/{funding → routes}/Token.test.ts +1 -1
  999. package/src/internal/{funding → routes}/Token.ts +5 -5
  1000. package/src/internal/routes/Transfer.test.ts +1199 -0
  1001. package/src/internal/routes/Transfer.ts +1206 -0
  1002. package/src/internal/routes/TransferReconciliation.test.ts +731 -0
  1003. package/src/internal/routes/TransferReconciliation.ts +527 -0
  1004. package/src/internal/routes/TransferSubsidy.test.ts +172 -0
  1005. package/src/internal/routes/TransferSubsidy.ts +447 -0
  1006. package/src/internal/routes/TransferWebhook.test.ts +128 -0
  1007. package/src/internal/routes/TransferWebhook.ts +67 -0
  1008. package/src/internal/{funding → routes}/index.test.ts +6 -3
  1009. package/src/internal/{funding → routes}/index.ts +3 -0
  1010. package/src/internal/{funding → routes}/providers/across.ts +16 -16
  1011. package/src/internal/{funding → routes}/providers/index.test.ts +157 -44
  1012. package/src/internal/{funding → routes}/providers/relay.test.ts +165 -32
  1013. package/src/internal/{funding → routes}/providers/relay.ts +231 -136
  1014. package/src/internal/{funding → routes}/providers/rhino.ts +16 -16
  1015. package/src/internal/{funding → routes}/providers/squid.ts +17 -17
  1016. package/src/internal/routes/providers/stargate.test.ts +728 -0
  1017. package/src/internal/routes/providers/stargate.ts +1276 -0
  1018. package/src/internal/{funding → routes}/providers/symbiosis.ts +64 -24
  1019. package/src/internal/webhookDestinations/slack.ts +20 -5
  1020. package/src/providers/index.ts +13 -13
  1021. package/src/server.ts +18 -0
  1022. package/dist/apps/funding/Access.d.ts.map +0 -1
  1023. package/dist/apps/funding/Access.js.map +0 -1
  1024. package/dist/apps/funding/App.js.map +0 -1
  1025. package/dist/apps/funding/Inventory.d.ts.map +0 -1
  1026. package/dist/apps/funding/Inventory.js.map +0 -1
  1027. package/dist/apps/funding/ProviderAttempts.d.ts +0 -5
  1028. package/dist/apps/funding/ProviderAttempts.d.ts.map +0 -1
  1029. package/dist/apps/funding/ProviderAttempts.js +0 -24
  1030. package/dist/apps/funding/ProviderAttempts.js.map +0 -1
  1031. package/dist/apps/funding/index.d.ts +0 -2
  1032. package/dist/apps/funding/index.d.ts.map +0 -1
  1033. package/dist/apps/funding/index.js +0 -2
  1034. package/dist/apps/funding/index.js.map +0 -1
  1035. package/dist/apps/funding/routes/chains.d.ts.map +0 -1
  1036. package/dist/apps/funding/routes/chains.js.map +0 -1
  1037. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +0 -1
  1038. package/dist/apps/funding/routes/deposit-addresses.js.map +0 -1
  1039. package/dist/apps/funding/routes/providers.d.ts.map +0 -1
  1040. package/dist/apps/funding/routes/providers.js +0 -57
  1041. package/dist/apps/funding/routes/providers.js.map +0 -1
  1042. package/dist/apps/funding/routes/quotes.d.ts.map +0 -1
  1043. package/dist/apps/funding/routes/quotes.js.map +0 -1
  1044. package/dist/apps/funding/routes/transfers.d.ts.map +0 -1
  1045. package/dist/apps/funding/routes/transfers.js +0 -622
  1046. package/dist/apps/funding/routes/transfers.js.map +0 -1
  1047. package/dist/db/tables/fundingCatalog.d.ts +0 -58
  1048. package/dist/db/tables/fundingCatalog.d.ts.map +0 -1
  1049. package/dist/db/tables/fundingCatalog.js.map +0 -1
  1050. package/dist/db/tables/fundingDepositAddresses.d.ts +0 -192
  1051. package/dist/db/tables/fundingDepositAddresses.d.ts.map +0 -1
  1052. package/dist/db/tables/fundingDepositAddresses.js +0 -235
  1053. package/dist/db/tables/fundingDepositAddresses.js.map +0 -1
  1054. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +0 -1
  1055. package/dist/db/tables/fundingDepositRequestObservations.js.map +0 -1
  1056. package/dist/db/tables/fundingDeposits.d.ts.map +0 -1
  1057. package/dist/db/tables/fundingDeposits.js +0 -295
  1058. package/dist/db/tables/fundingDeposits.js.map +0 -1
  1059. package/dist/db/tables/fundingIdempotency.d.ts +0 -91
  1060. package/dist/db/tables/fundingIdempotency.d.ts.map +0 -1
  1061. package/dist/db/tables/fundingIdempotency.js +0 -103
  1062. package/dist/db/tables/fundingIdempotency.js.map +0 -1
  1063. package/dist/db/tables/fundingTransferEvents.d.ts.map +0 -1
  1064. package/dist/db/tables/fundingTransferEvents.js.map +0 -1
  1065. package/dist/db/tables/fundingTransferTransactions.d.ts +0 -15
  1066. package/dist/db/tables/fundingTransferTransactions.d.ts.map +0 -1
  1067. package/dist/db/tables/fundingTransferTransactions.js +0 -27
  1068. package/dist/db/tables/fundingTransferTransactions.js.map +0 -1
  1069. package/dist/db/tables/fundingTransfers.d.ts +0 -82
  1070. package/dist/db/tables/fundingTransfers.d.ts.map +0 -1
  1071. package/dist/db/tables/fundingTransfers.js +0 -89
  1072. package/dist/db/tables/fundingTransfers.js.map +0 -1
  1073. package/dist/internal/funding/Action.d.ts.map +0 -1
  1074. package/dist/internal/funding/Action.js.map +0 -1
  1075. package/dist/internal/funding/Catalog.d.ts.map +0 -1
  1076. package/dist/internal/funding/Catalog.js.map +0 -1
  1077. package/dist/internal/funding/Chain.d.ts.map +0 -1
  1078. package/dist/internal/funding/Chain.js.map +0 -1
  1079. package/dist/internal/funding/Deposit.d.ts.map +0 -1
  1080. package/dist/internal/funding/Deposit.js.map +0 -1
  1081. package/dist/internal/funding/DepositAddress.d.ts.map +0 -1
  1082. package/dist/internal/funding/DepositAddress.js.map +0 -1
  1083. package/dist/internal/funding/DepositWebhook.d.ts.map +0 -1
  1084. package/dist/internal/funding/DepositWebhook.js.map +0 -1
  1085. package/dist/internal/funding/Evidence.d.ts.map +0 -1
  1086. package/dist/internal/funding/Evidence.js.map +0 -1
  1087. package/dist/internal/funding/Provider.d.ts.map +0 -1
  1088. package/dist/internal/funding/Provider.js.map +0 -1
  1089. package/dist/internal/funding/Reconciliation.d.ts.map +0 -1
  1090. package/dist/internal/funding/Reconciliation.js.map +0 -1
  1091. package/dist/internal/funding/Route.d.ts +0 -21
  1092. package/dist/internal/funding/Route.d.ts.map +0 -1
  1093. package/dist/internal/funding/Route.js.map +0 -1
  1094. package/dist/internal/funding/SourceObservation.d.ts.map +0 -1
  1095. package/dist/internal/funding/SourceObservation.js.map +0 -1
  1096. package/dist/internal/funding/Subsidy.d.ts.map +0 -1
  1097. package/dist/internal/funding/Subsidy.js.map +0 -1
  1098. package/dist/internal/funding/Token.d.ts.map +0 -1
  1099. package/dist/internal/funding/Token.js.map +0 -1
  1100. package/dist/internal/funding/Transfer.d.ts.map +0 -1
  1101. package/dist/internal/funding/Transfer.js +0 -334
  1102. package/dist/internal/funding/Transfer.js.map +0 -1
  1103. package/dist/internal/funding/index.d.ts.map +0 -1
  1104. package/dist/internal/funding/index.js.map +0 -1
  1105. package/dist/internal/funding/providers/across.d.ts.map +0 -1
  1106. package/dist/internal/funding/providers/across.js.map +0 -1
  1107. package/dist/internal/funding/providers/relay.d.ts.map +0 -1
  1108. package/dist/internal/funding/providers/relay.js.map +0 -1
  1109. package/dist/internal/funding/providers/rhino.d.ts.map +0 -1
  1110. package/dist/internal/funding/providers/rhino.js.map +0 -1
  1111. package/dist/internal/funding/providers/squid.d.ts.map +0 -1
  1112. package/dist/internal/funding/providers/squid.js.map +0 -1
  1113. package/dist/internal/funding/providers/stargate.d.ts +0 -38
  1114. package/dist/internal/funding/providers/stargate.d.ts.map +0 -1
  1115. package/dist/internal/funding/providers/stargate.js +0 -277
  1116. package/dist/internal/funding/providers/stargate.js.map +0 -1
  1117. package/dist/internal/funding/providers/symbiosis.d.ts.map +0 -1
  1118. package/dist/internal/funding/providers/symbiosis.js.map +0 -1
  1119. package/src/apps/funding/App.test.ts +0 -78
  1120. package/src/apps/funding/App.ts +0 -78
  1121. package/src/apps/funding/ProviderAttempts.ts +0 -26
  1122. package/src/apps/funding/index.ts +0 -1
  1123. package/src/apps/funding/routes/transfers.test.ts +0 -1638
  1124. package/src/apps/funding/routes/transfers.ts +0 -721
  1125. package/src/db/tables/fundingCatalog.test.ts +0 -122
  1126. package/src/db/tables/fundingCatalog.ts +0 -163
  1127. package/src/db/tables/fundingDepositAddresses.test.ts +0 -608
  1128. package/src/db/tables/fundingDepositAddresses.ts +0 -443
  1129. package/src/db/tables/fundingDeposits.ts +0 -572
  1130. package/src/db/tables/fundingIdempotency.test.ts +0 -182
  1131. package/src/db/tables/fundingIdempotency.ts +0 -180
  1132. package/src/db/tables/fundingTransferTransactions.test.ts +0 -69
  1133. package/src/db/tables/fundingTransferTransactions.ts +0 -37
  1134. package/src/db/tables/fundingTransfers.test.ts +0 -89
  1135. package/src/db/tables/fundingTransfers.ts +0 -153
  1136. package/src/handlers/internal/multisig.test.ts +0 -818
  1137. package/src/internal/funding/Subsidy.test.ts +0 -75
  1138. package/src/internal/funding/Transfer.test.ts +0 -369
  1139. package/src/internal/funding/Transfer.ts +0 -545
  1140. package/src/internal/funding/providers/stargate.test.ts +0 -163
  1141. package/src/internal/funding/providers/stargate.ts +0 -409
  1142. /package/dist/apps/{funding → routes}/Inventory.d.ts +0 -0
  1143. /package/src/internal/{funding → routes}/Route.test.ts +0 -0
@@ -1,11 +1,14 @@
1
1
  import { type Context, Hono } from 'hono'
2
2
  import { Value as core_Value } from 'ox'
3
- import type { Actions } from 'viem/tempo'
3
+ import { Abis, type Actions } from 'viem/tempo'
4
4
  import {
5
5
  type Chain,
6
6
  ContractFunctionExecutionError,
7
7
  ContractFunctionRevertedError,
8
8
  ContractFunctionZeroDataError,
9
+ isAddress,
10
+ parseAbi,
11
+ parseUnits,
9
12
  zeroAddress,
10
13
  } from 'viem'
11
14
  import * as z from 'zod/mini'
@@ -14,12 +17,18 @@ import type * as App from '../../../App.js'
14
17
  import * as Scope from '../../../Scope.js'
15
18
  import * as Db from '../../../db/Db.js'
16
19
  import * as core_EarnVaults from '../../../db/tables/earnVaults.js'
20
+ import * as RewardCampaignDistributors from '../../../db/tables/rewardCampaignDistributors.js'
21
+ import * as RewardCampaigns from '../../../db/tables/rewardCampaigns.js'
22
+ import * as RewardEligibilityAssociations from '../../../db/tables/rewardEligibilityAssociations.js'
17
23
  import * as Auth from '../../../internal/Auth.js'
18
24
  import * as Cache from '../../../internal/Cache.js'
19
25
  import * as Cursor from '../../../internal/Cursor.js'
20
26
  import * as EarnRates from '../../../internal/EarnRates.js'
27
+ import * as EarnSharePrices from '../../../internal/EarnSharePrices.js'
21
28
  import * as EarnVaults from '../../../internal/EarnVaults.js'
29
+ import type * as Log from '../../../internal/Log.js'
22
30
  import * as OpenApi from '../../../internal/OpenApi.js'
31
+ import * as RewardRead from '../../../internal/rewards/Read.js'
23
32
  import * as Response from '../../../internal/Response.js'
24
33
  import * as Schema from '../../../internal/Schema.js'
25
34
  import * as Store from '../../../internal/Store.js'
@@ -28,6 +37,7 @@ import * as Timing from '../../../internal/Timing.js'
28
37
  import * as Ttl from '../../../internal/Ttl.js'
29
38
  import * as Value from '../../../internal/Value.js'
30
39
  import * as VerifiedTokens from '../../../internal/VerifiedTokens.js'
40
+ import * as Viem from '../../../internal/Viem.js'
31
41
  import * as FxOracle from '../FxOracle.js'
32
42
  import * as Tokens from './tokens.js'
33
43
  import * as Valuation from './valuation.js'
@@ -35,6 +45,27 @@ import * as Valuation from './valuation.js'
35
45
  const deployedSignature =
36
46
  'event EarnStackDeployed(address indexed earnVault,address indexed earnShare,address indexed earnFees,address engine,address asset,address owner,bytes32 deploymentId,address emergencyGuardian,address asyncJanitor,uint8 migrationMode,bytes32 earnShareSalt,bytes32 controlConfigHash,bytes32 feeConfigHash,bytes32 earnFeesSalt)'
37
47
 
48
+ // Historical factories for this signature, verified against upstream deployments
49
+ // and compiled EarnFactory releases. See docs/earn-discovery.md before updating.
50
+ const deploymentFactories: Record<number, readonly string[]> = {
51
+ 4217: [
52
+ '0x5b972d2490d06e436982fab77612fd9987f097d0',
53
+ '0x75b20eafdbd3dd974d00b573fd72852ab0237afd',
54
+ ],
55
+ 42431: [
56
+ '0x0f9ccbc97563a179f5f0886f89ee51c9185f6ec4',
57
+ '0x2aa26b7e24e6f1fb2d539d527b8f1bb5cacfa288',
58
+ '0x4990c74ae874f2b0fd4938662e2cdb8ca4c27887',
59
+ '0x518d4fbd5334bb7ff67c6ebcb67a7d60e1ef8d5d',
60
+ '0x6742345070be5a20ce05f53b6d08d071162c0ef8',
61
+ '0x846b8479d64691ff61c05137dc580a1ecdc188c5',
62
+ '0xb4944264260a6412351d64653f65d2c27d9cb038',
63
+ '0xb4ecb65a57e3585c3699c1617c2abb5de35a697d',
64
+ '0xb5e53c6b17c6f3a380ad94f5417c5e541dfeefac',
65
+ '0xe8fd5b183021ba3f6d3197c5b4238c93561b1b7a',
66
+ ],
67
+ }
68
+
38
69
  const depositedSignature =
39
70
  'event Deposited(address indexed caller,address indexed receiver,uint256 assets,uint256 earnShares)'
40
71
 
@@ -56,12 +87,19 @@ const redeemRequestedSignature =
56
87
  const venueSharesDepositedSignature =
57
88
  'event VenueSharesDeposited(address indexed caller,address indexed receiver,uint256 requestedVenueShares,uint256 receivedVenueShares,uint256 earnShares)'
58
89
 
90
+ const unspentReturnedSignature =
91
+ 'event UnspentReturned(address indexed treasury,uint256 earnShares)'
92
+
59
93
  const maxScannedDeployments = 250
60
94
  const scanLimit = 50
61
95
 
62
96
  // Concurrent metadata reads in one macrotask collapse into one deployless
63
97
  // multicall; an await between small chunks keeps each call under the RPC limit.
64
98
  const tokenReadChunkSize = 20
99
+ const tempoStateAddress = '0x1c00000000000000000000000000000000000000'
100
+ const tempoStateAbi = parseAbi(['function tempoBlockNumber() view returns (uint64)'])
101
+ const zonePortalAbi = parseAbi(['function lastSyncedTempoBlockNumber() view returns (uint64)'])
102
+ const tokenPositionAbi = parseAbi(['function balanceOf(address account) view returns (uint256)'])
65
103
  /** Block boundary that includes all indexed history. */
66
104
  const beforeFirstBlockNumber = -1
67
105
 
@@ -71,6 +109,13 @@ const tvlDenomination = 'USD'
71
109
  /** `Valuation.valuationFor` floors to 6 dp, so 6 is the precision a valued amount carries. */
72
110
  const tvlDecimals = 6
73
111
 
112
+ function toRewardEligibilityAssociation(record: RewardEligibilityAssociations.Record) {
113
+ return {
114
+ ...record,
115
+ id: `${record.vaultAddress}-${record.walletAddress}`,
116
+ }
117
+ }
118
+
74
119
  /** Returns the smallest useful live-discovery batch. */
75
120
  export function discoveryBatchLimit(options: discoveryBatchLimit.Options): number {
76
121
  const needed = options.limit + 1 - options.matches
@@ -109,6 +154,118 @@ export declare namespace hasZoneAccess {
109
154
 
110
155
  /** Zod schemas owned by the public Earn-vault resource. */
111
156
  export namespace schema {
157
+ /** Schemas for the registerRewardEligibility operation. */
158
+ export namespace registerRewardEligibility {
159
+ /** Wallet and vault association to register for reward eligibility. */
160
+ export const Body = z
161
+ .strictObject({
162
+ chainId: Schema.ChainId.check(
163
+ z.describe('Chain containing the Earn vault.'),
164
+ z.meta({ examples: [4217] }),
165
+ ),
166
+ transactionHash: z.optional(Schema.hash(`0x${'33'.repeat(32)}`)),
167
+ vaultAddress: Schema.Address.check(
168
+ z.describe('Earn vault contract address.'),
169
+ z.meta({ examples: ['0x59d27987bc7cc1521bba5f688a170c65840cbf43'] }),
170
+ ),
171
+ walletAddress: Schema.Address.check(
172
+ z.describe('Wallet to register for rewards.'),
173
+ z.meta({ examples: ['0x8117e0ba6239b9695f780deb010f72a2fa4bdfb6'] }),
174
+ ),
175
+ })
176
+ .check(z.describe('Reward eligibility registration.'))
177
+
178
+ /** Persisted reward eligibility association. */
179
+ export const Association = z
180
+ .strictObject({
181
+ chainId: Schema.ChainId,
182
+ firstRegisteredAt: z.iso
183
+ .datetime()
184
+ .check(
185
+ z.describe('When the association was first registered.'),
186
+ z.meta({ examples: ['2026-08-25T20:00:00.000Z'] }),
187
+ ),
188
+ id: z.string().check(
189
+ z.describe('Stable reward eligibility association identifier.'),
190
+ z.meta({
191
+ examples: [
192
+ '0x59d27987bc7cc1521bba5f688a170c65840cbf43-0x8117e0ba6239b9695f780deb010f72a2fa4bdfb6',
193
+ ],
194
+ }),
195
+ ),
196
+ latestRegisteredAt: z.iso
197
+ .datetime()
198
+ .check(
199
+ z.describe('When the association was most recently registered.'),
200
+ z.meta({ examples: ['2026-08-25T20:00:00.000Z'] }),
201
+ ),
202
+ registrationOrder: z
203
+ .string()
204
+ .check(
205
+ z.regex(/^\d+$/),
206
+ z.describe('Monotonic registration order.'),
207
+ z.meta({ examples: ['42'] }),
208
+ ),
209
+ transactionHash: z.optional(Schema.hash(`0x${'33'.repeat(32)}`)),
210
+ vaultAddress: Schema.Address,
211
+ walletAddress: Schema.Address,
212
+ })
213
+ .check(z.describe('Persisted reward eligibility association.'))
214
+
215
+ /** Persisted reward eligibility registration result. */
216
+ export const Response = z
217
+ .strictObject({
218
+ association: Association,
219
+ created: z
220
+ .boolean()
221
+ .check(
222
+ z.describe('Whether this request created the association.'),
223
+ z.meta({ examples: [true] }),
224
+ ),
225
+ })
226
+ .check(z.describe('Persisted reward eligibility registration result.'))
227
+ }
228
+
229
+ /** Schemas for the getRewardEligibility operation. */
230
+ export namespace getRewardEligibility {
231
+ const Limit = z
232
+ ._default(
233
+ z.pipe(
234
+ z.coerce.number().check(z.int(), z.gte(Schema.minLimit)),
235
+ z.transform((value) => Math.min(value, 100)),
236
+ ),
237
+ 10,
238
+ )
239
+ .check(
240
+ z.describe(
241
+ 'How many items to return per page (5 to 100, default 10). Use `nextCursor` to fetch more.',
242
+ ),
243
+ z.meta({ examples: [10] }),
244
+ )
245
+
246
+ /** Selects one chain and vault eligibility snapshot page. */
247
+ export const Query = z
248
+ .strictObject({
249
+ chainId: Schema.ChainId.check(z.describe('Chain containing the Earn vault.')),
250
+ cursor: Schema.Cursor,
251
+ limit: Limit,
252
+ vaultAddress: Schema.Address.check(z.describe('Earn vault contract address.')),
253
+ })
254
+ .check(z.describe('Reward eligibility page selector.'))
255
+
256
+ /** One page of eligible wallets at the cursor's fixed checkpoint. */
257
+ export const Response = z
258
+ .strictObject({
259
+ chainId: Schema.ChainId,
260
+ data: z
261
+ .array(Schema.Address)
262
+ .check(z.describe('Eligible wallet addresses in registration order.')),
263
+ nextCursor: Schema.NextCursor,
264
+ vaultAddress: Schema.Address,
265
+ })
266
+ .check(z.describe('A fixed-snapshot page of eligible wallet addresses.'))
267
+ }
268
+
112
269
  const capability = z
113
270
  .enum([
114
271
  'asyncRedeem',
@@ -126,25 +283,28 @@ export namespace schema {
126
283
  .enum(['erc4626', 'veda'])
127
284
  .check(z.describe('Inferred vault engine type.'), z.meta({ examples: ['erc4626'] }))
128
285
 
129
- const engine = Schema.describe(
130
- z.strictObject({
131
- address: Schema.Address.check(
132
- z.describe('Current vault engine contract address.'),
133
- z.meta({ examples: ['0xd85fc943333a6cd7c0e3391acd54fc4e572f0baf'] }),
134
- ),
135
- type: z
136
- .nullable(engineType)
137
- .check(z.describe('Inferred engine type, or null when it is unknown.')),
138
- venue: z
139
- .nullable(
140
- Schema.Address.check(
141
- z.describe('Yield venue contract bound to the engine.'),
142
- z.meta({ examples: ['0xa8b4f0e69cd4b0e56b676343f02acd8c3b355322'] }),
143
- ),
144
- )
145
- .check(z.describe('Yield venue, or null when the engine does not expose one.')),
146
- }),
147
- 'Current vault engine.',
286
+ const engine = OpenApi.component(
287
+ Schema.describe(
288
+ z.strictObject({
289
+ address: Schema.Address.check(
290
+ z.describe('Current vault engine contract address.'),
291
+ z.meta({ examples: ['0x59d27987bc7cc1521bba5f688a170c65840cbf43'] }),
292
+ ),
293
+ type: z
294
+ .nullable(engineType)
295
+ .check(z.describe('Inferred engine type, or null when it is unknown.')),
296
+ venue: z
297
+ .nullable(
298
+ Schema.Address.check(
299
+ z.describe('Yield venue contract bound to the engine.'),
300
+ z.meta({ examples: ['0x9a044ae05e5e6290dcf56afd69548565e957a626'] }),
301
+ ),
302
+ )
303
+ .check(z.describe('Yield venue, or null when the engine does not expose one.')),
304
+ }),
305
+ 'Current vault engine.',
306
+ ),
307
+ 'EarnVaultEngine',
148
308
  )
149
309
 
150
310
  const include = z
@@ -156,6 +316,85 @@ export namespace schema {
156
316
  'Comma-separated vault fields to include, such as `apy,tvl`.',
157
317
  )
158
318
 
319
+ const aprRatio = z
320
+ .string()
321
+ .check(
322
+ z.regex(/^\d+(\.\d+)?$/),
323
+ z.describe('Annual percentage rate as a non-negative decimal ratio.'),
324
+ z.meta({ examples: ['0.0195'] }),
325
+ )
326
+
327
+ const reward = z.strictObject({
328
+ apr: z
329
+ .nullable(aprRatio)
330
+ .check(z.describe('Current reward APR, or null before its first calculation.')),
331
+ asset: z
332
+ .strictObject({
333
+ address: Schema.Address,
334
+ chainId: Schema.ChainId,
335
+ decimals: z
336
+ .number()
337
+ .check(
338
+ z.int(),
339
+ z.nonnegative(),
340
+ z.describe('Reward asset decimal places.'),
341
+ z.meta({ examples: [18] }),
342
+ ),
343
+ })
344
+ .check(z.describe('EarnShare token distributed by the reward campaign.')),
345
+ calculatedThrough: z
346
+ .optional(z.number().check(z.int(), z.nonnegative()))
347
+ .check(
348
+ z.describe('Unix timestamp through which a calculated reward APR applies.'),
349
+ z.meta({ examples: [1788134700] }),
350
+ ),
351
+ campaignId: z
352
+ .string()
353
+ .check(
354
+ z.describe('EarnVault address that identifies the reward campaign.'),
355
+ z.meta({ examples: ['0x1000000000000000000000000000000000000001'] }),
356
+ ),
357
+ endsAt: z
358
+ .number()
359
+ .check(
360
+ z.int(),
361
+ z.nonnegative(),
362
+ z.describe('Exclusive campaign end as a Unix timestamp.'),
363
+ z.meta({ examples: [1790812800] }),
364
+ ),
365
+ startsAt: z
366
+ .number()
367
+ .check(
368
+ z.int(),
369
+ z.nonnegative(),
370
+ z.describe('Inclusive campaign start as a Unix timestamp.'),
371
+ z.meta({ examples: [1788134400] }),
372
+ ),
373
+ targetTotalApr: aprRatio.check(
374
+ z.describe('Configured target for combined observed vault yield and Merkle rewards.'),
375
+ z.meta({ examples: ['0.0700'] }),
376
+ ),
377
+ })
378
+
379
+ const apy = z.strictObject({
380
+ asOf: EarnRates.schema.Apy.shape.asOf,
381
+ methodology: EarnRates.schema.Apy.shape.methodology,
382
+ net: EarnRates.schema.Apy.shape.net.check(
383
+ z.describe('Higher of observed vault APY and configured reward campaign targets.'),
384
+ ),
385
+ rewards: z
386
+ .optional(z.array(reward))
387
+ .check(
388
+ z.describe(
389
+ 'Enabled, registered Merkle rewards. Omitted when reward enrichment is unavailable.',
390
+ ),
391
+ ),
392
+ vault: EarnRates.schema.Apy.shape.net.check(
393
+ z.describe('Observed fee-aware vault APY before reward APR.'),
394
+ ),
395
+ window: EarnRates.schema.Apy.shape.window,
396
+ })
397
+
159
398
  // `apy` and `tvl` need indexer and FX access, so surfaces lacking either
160
399
  // select from this reduced set instead.
161
400
  const baseInclude = z
@@ -167,65 +406,71 @@ export namespace schema {
167
406
  'Comma-separated vault fields to include, such as `access,zone`.',
168
407
  )
169
408
 
170
- const zone = z
171
- .strictObject({
172
- chainId: Schema.ChainId.check(
173
- z.describe('Private Zone chain id.'),
174
- z.meta({ examples: [1424310003] }),
175
- ),
176
- inputTokens: z
177
- .array(Schema.Address)
178
- .check(
179
- z.describe('Tokens accepted by private deposits.'),
180
- z.meta({ examples: [['0x20c0000000000000000000002139993d5f625d59']] }),
181
- ),
182
- name: z
183
- .string()
184
- .check(z.describe('Private Zone name.'), z.meta({ examples: ['zone-moderato-internal'] })),
185
- outputTokens: z
186
- .array(Schema.Address)
187
- .check(
188
- z.describe('Tokens supported by private redemptions.'),
189
- z.meta({ examples: [['0x20c0000000000000000000002139993d5f625d59']] }),
409
+ const zone = OpenApi.component(
410
+ z
411
+ .strictObject({
412
+ chainId: Schema.ChainId.check(
413
+ z.describe('Private Zone chain id.'),
414
+ z.meta({ examples: [421700001] }),
190
415
  ),
191
- })
192
- .check(z.describe('Private Zone route configured for a verified vault.'))
416
+ inputTokens: z
417
+ .array(Schema.Address)
418
+ .check(
419
+ z.describe('Tokens accepted by private deposits.'),
420
+ z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
421
+ ),
422
+ name: z
423
+ .string()
424
+ .check(z.describe('Private Zone name.'), z.meta({ examples: ['zone-mainnet-internal'] })),
425
+ outputTokens: z
426
+ .array(Schema.Address)
427
+ .check(
428
+ z.describe('Tokens supported by private redemptions.'),
429
+ z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
430
+ ),
431
+ })
432
+ .check(z.describe('Private Zone route configured for a verified vault.')),
433
+ 'EarnVaultZone',
434
+ )
193
435
 
194
- const zoneRoute = z
195
- .strictObject({
196
- chainId: Schema.ChainId.check(
197
- z.describe('Private Zone chain id.'),
198
- z.meta({ examples: [1424310003] }),
199
- ),
200
- deploymentBlock: z
201
- .number()
202
- .check(
203
- z.int(),
204
- z.nonnegative(),
205
- z.describe('Parent-chain block that deployed the Earn router.'),
206
- z.meta({ examples: [31730087] }),
207
- ),
208
- earnRouter: Schema.Address.check(
209
- z.describe('Parent-chain router for this Zone and Earn vault.'),
210
- z.meta({ examples: ['0xebedc74ffeead186acc6cac05eb82fb9e093a7c7'] }),
211
- ),
212
- inputTokens: z
213
- .array(Schema.Address)
214
- .check(
215
- z.describe('Tokens accepted by private deposits.'),
216
- z.meta({ examples: [['0x20c0000000000000000000002139993d5f625d59']] }),
436
+ const zoneRoute = OpenApi.component(
437
+ z
438
+ .strictObject({
439
+ chainId: Schema.ChainId.check(
440
+ z.describe('Private Zone chain id.'),
441
+ z.meta({ examples: [421700001] }),
217
442
  ),
218
- name: z
219
- .string()
220
- .check(z.describe('Private Zone name.'), z.meta({ examples: ['zone-moderato-internal'] })),
221
- outputTokens: z
222
- .array(Schema.Address)
223
- .check(
224
- z.describe('Tokens returned by private redemptions.'),
225
- z.meta({ examples: [['0x20c0000000000000000000002139993d5f625d59']] }),
443
+ deploymentBlock: z
444
+ .number()
445
+ .check(
446
+ z.int(),
447
+ z.nonnegative(),
448
+ z.describe('Parent-chain block that deployed the Earn router.'),
449
+ z.meta({ examples: [35822949] }),
450
+ ),
451
+ earnRouter: Schema.Address.check(
452
+ z.describe('Parent-chain router for this Zone and Earn vault.'),
453
+ z.meta({ examples: ['0x8117e0ba6239b9695f780deb010f72a2fa4bdfb6'] }),
226
454
  ),
227
- })
228
- .check(z.describe('Verified Earn route through one private Zone.'))
455
+ inputTokens: z
456
+ .array(Schema.Address)
457
+ .check(
458
+ z.describe('Tokens accepted by private deposits.'),
459
+ z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
460
+ ),
461
+ name: z
462
+ .string()
463
+ .check(z.describe('Private Zone name.'), z.meta({ examples: ['zone-mainnet-internal'] })),
464
+ outputTokens: z
465
+ .array(Schema.Address)
466
+ .check(
467
+ z.describe('Tokens returned by private redemptions.'),
468
+ z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
469
+ ),
470
+ })
471
+ .check(z.describe('Verified Earn route through one private Zone.')),
472
+ 'EarnVaultZoneRoute',
473
+ )
229
474
 
230
475
  const value = z.strictObject({
231
476
  amount: Schema.DecimalString.check(
@@ -257,13 +502,28 @@ export namespace schema {
257
502
  .optional(EarnVaults.schema.Discovery.shape.access)
258
503
  .check(z.describe('Vault share-token access, when requested.')),
259
504
  apy: z
260
- .optional(z.nullable(EarnRates.schema.Apy))
505
+ .optional(z.nullable(apy))
261
506
  .check(
262
507
  z.describe(
263
- 'Annualized share-price growth over the selected window, when requested via `include=apy` or by selecting `apy.window`. Null when the rate could not be measured: the vault predates the window, a boundary or historical quote is unavailable, the engine changed inside the window, or the growth is too extreme to annualize as a ratio.',
508
+ 'Annualized vault yield over the selected window, plus every available current Merkle reward APR. Null when no component is measurable.',
264
509
  ),
265
510
  ),
266
- assetToken: Tokens.schema.Token.check(z.describe('Asset accepted by the vault.')),
511
+ assetToken: Tokens.schema.Token.check(
512
+ z.describe('Asset accepted by the vault.'),
513
+ z.meta({
514
+ examples: [
515
+ {
516
+ address: '0x20c0000000000000000000000000000000000000',
517
+ currency: 'USD',
518
+ decimals: 6,
519
+ id: '0x20c0000000000000000000000000000000000000',
520
+ name: 'PathUSD',
521
+ symbol: 'pathUSD',
522
+ verified: true,
523
+ },
524
+ ],
525
+ }),
526
+ ),
267
527
  capabilities: z
268
528
  .optional(EarnVaults.schema.Discovery.shape.capabilities)
269
529
  .check(z.describe('Vault capabilities, when requested.')),
@@ -271,21 +531,28 @@ export namespace schema {
271
531
  .nullable(z.string())
272
532
  .check(
273
533
  z.describe('Curated vault description, or null when the vault is not verified.'),
274
- z.meta({ examples: ['Bridge test PATHUSD deposited into Tempo Earn.'] }),
534
+ z.meta({ examples: ['deelUSD deposited into the canonical Tempo Earn deployment.'] }),
275
535
  ),
276
536
  engine,
277
537
  id: Schema.Address.check(
278
538
  z.describe('Stable vault id, equal to the lowercase vault address.'),
279
- z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] }),
539
+ z.meta({ examples: ['0x4f94590b636f5878bce585e82379de81e1ec174f'] }),
280
540
  ),
281
541
  instantLiquidity: EarnVaults.schema.Discovery.shape.instantLiquidity.check(
282
542
  z.describe(
283
543
  'Assets the venue would release right now, capped at the vault backing, in asset base units. Null when the engine interface cannot report a reliable number.',
284
544
  ),
285
545
  ),
546
+ instantLiquidityValue: z
547
+ .optional(z.nullable(value))
548
+ .check(
549
+ z.describe(
550
+ 'Instant liquidity valued in USD, when requested via `include=tvl`, or null when liquidity is unavailable or the asset has no priced display currency.',
551
+ ),
552
+ ),
286
553
  label: z
287
554
  .string()
288
- .check(z.describe('Vault display label.'), z.meta({ examples: ['btPATHUSD Earn'] })),
555
+ .check(z.describe('Vault display label.'), z.meta({ examples: ['deelUSD Earn'] })),
289
556
  sharePrice: z
290
557
  .nullable(value)
291
558
  .check(
@@ -293,12 +560,27 @@ export namespace schema {
293
560
  'Assets returned for one whole earn share, in the asset display currency, or null when the engine cannot quote an exit.',
294
561
  ),
295
562
  ),
296
- shareToken: Tokens.schema.Token.check(z.describe('Token representing vault shares.')),
563
+ shareToken: Tokens.schema.Token.check(
564
+ z.describe('Token representing vault shares.'),
565
+ z.meta({
566
+ examples: [
567
+ {
568
+ address: '0x20c000000000000000000000c3268d803b51d448',
569
+ currency: 'USD',
570
+ decimals: 6,
571
+ id: '0x20c000000000000000000000c3268d803b51d448',
572
+ name: 'Sentora pathUSD (Earn)',
573
+ symbol: 'senpathUSDE',
574
+ verified: false,
575
+ },
576
+ ],
577
+ }),
578
+ ),
297
579
  slug: z
298
580
  .nullable(z.string())
299
581
  .check(
300
582
  z.describe('Curated URL slug, or null when the vault is not verified.'),
301
- z.meta({ examples: ['btpathusd-earn-h8k2m4n6'] }),
583
+ z.meta({ examples: ['deelusd-earn'] }),
302
584
  ),
303
585
  state: EarnVaults.schema.Discovery.shape.state,
304
586
  tvl: z
@@ -310,7 +592,7 @@ export namespace schema {
310
592
  ),
311
593
  vaultAddress: Schema.Address.check(
312
594
  z.describe('Earn vault contract address.'),
313
- z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] }),
595
+ z.meta({ examples: ['0x4f94590b636f5878bce585e82379de81e1ec174f'] }),
314
596
  ),
315
597
  verified: z
316
598
  .boolean()
@@ -327,12 +609,12 @@ export namespace schema {
327
609
  examples: [
328
610
  [
329
611
  {
330
- chainId: 1424310003,
331
- deploymentBlock: 31730087,
332
- earnRouter: '0xebedc74ffeead186acc6cac05eb82fb9e093a7c7',
333
- inputTokens: ['0x20c0000000000000000000002139993d5f625d59'],
334
- name: 'zone-moderato-internal',
335
- outputTokens: ['0x20c0000000000000000000002139993d5f625d59'],
612
+ chainId: 421700001,
613
+ deploymentBlock: 35822949,
614
+ earnRouter: '0x8117e0ba6239b9695f780deb010f72a2fa4bdfb6',
615
+ inputTokens: ['0x20c0000000000000000000006fd9a167923ba194'],
616
+ name: 'zone-mainnet-internal',
617
+ outputTokens: ['0x20c0000000000000000000006fd9a167923ba194'],
336
618
  },
337
619
  ],
338
620
  ],
@@ -341,26 +623,32 @@ export namespace schema {
341
623
  })
342
624
 
343
625
  /** One compatible Earn vault resolved from current chain state. */
344
- export const Vault = Schema.describe(vault, 'A compatible earn vault resolved from chain state.')
626
+ export const Vault = OpenApi.component(
627
+ Schema.describe(vault, 'A compatible earn vault resolved from chain state.'),
628
+ 'EarnVault',
629
+ )
345
630
 
346
631
  /** One registry-curated Earn vault. */
347
- export const VerifiedVault = Schema.describe(
348
- z.extend(vault, {
349
- engine: z.extend(engine, { type: engineType }),
350
- slug: z
351
- .string()
352
- .check(
353
- z.describe('Stable curated vault slug.'),
354
- z.meta({ examples: ['btpathusd-earn-h8k2m4n6'] }),
355
- ),
356
- verified: z
357
- .literal(true)
358
- .check(
359
- z.describe('This vault is in the curated earn registry.'),
360
- z.meta({ examples: [true] }),
361
- ),
362
- }),
363
- 'A registry-curated earn vault resolved from chain state.',
632
+ export const VerifiedVault = OpenApi.component(
633
+ Schema.describe(
634
+ z.extend(vault, {
635
+ engine: z.extend(engine, { type: engineType }),
636
+ slug: z
637
+ .string()
638
+ .check(
639
+ z.describe('Stable curated vault slug.'),
640
+ z.meta({ examples: ['btpathusd-earn-h8k2m4n6'] }),
641
+ ),
642
+ verified: z
643
+ .literal(true)
644
+ .check(
645
+ z.describe('This vault is in the curated earn registry.'),
646
+ z.meta({ examples: [true] }),
647
+ ),
648
+ }),
649
+ 'A registry-curated earn vault resolved from chain state.',
650
+ ),
651
+ 'VerifiedEarnVault',
364
652
  )
365
653
 
366
654
  const position = z.strictObject({
@@ -369,26 +657,34 @@ export namespace schema {
369
657
  z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] }),
370
658
  ),
371
659
  assetAllowance: Schema.DecimalString.check(
372
- z.describe('Assets the vault may spend from this account, in asset base units.'),
660
+ z.describe(
661
+ 'Assets the vault may spend from a direct account, in asset base units. Always zero for Zone-routed positions, which use Zone withdrawals instead of approvals.',
662
+ ),
373
663
  z.meta({ examples: ['0'] }),
374
664
  ),
375
665
  assetBalance: Schema.DecimalString.check(
376
- z.describe('Assets this account currently holds, in asset base units.'),
666
+ z.describe('Assets held by this account at the observation block, in asset base units.'),
377
667
  z.meta({ examples: ['250000000'] }),
378
668
  ),
379
669
  assetToken: Schema.tokenAddress('0x20c000000000000000000000b9537d11c60e8b50').check(
380
- z.describe('TIP-20 asset token accepted by the vault.'),
670
+ z.describe('TIP-20 asset token accepted by the vault or its selected Zone route.'),
671
+ ),
672
+ chainId: Schema.ChainId.check(
673
+ z.describe('Chain containing the account position.'),
674
+ z.meta({ examples: [42431] }),
381
675
  ),
382
676
  id: Schema.Address.check(
383
677
  z.describe('Stable resource ID for this vault position, equal to the account address.'),
384
678
  z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] }),
385
679
  ),
386
680
  shareAllowance: Schema.DecimalString.check(
387
- z.describe('Earn shares the vault may spend from this account, in share base units.'),
681
+ z.describe(
682
+ 'Earn shares the vault may spend from a direct account, in share base units. Always zero for Zone-routed positions, which use Zone withdrawals instead of approvals.',
683
+ ),
388
684
  z.meta({ examples: ['0'] }),
389
685
  ),
390
686
  shareBalance: Schema.DecimalString.check(
391
- z.describe('Earn shares this account currently holds, in share base units.'),
687
+ z.describe('Earn shares held by this account at the observation block, in share base units.'),
392
688
  z.meta({ examples: ['99500000'] }),
393
689
  ),
394
690
  shareToken: Schema.tokenAddress('0x20c000000000000000000000300e14ab91a10769').check(
@@ -396,44 +692,165 @@ export namespace schema {
396
692
  ),
397
693
  value: Schema.DecimalString.check(
398
694
  z.describe(
399
- 'Current asset value of this account’s earn shares, including fees, in asset base units.',
695
+ 'Asset value of this account’s earn shares at the observation block, including fees, in `valueToken` base units.',
400
696
  ),
401
697
  z.meta({ examples: ['100000000'] }),
402
698
  ),
699
+ valueToken: Schema.tokenAddress('0x20c000000000000000000000b9537d11c60e8b50').check(
700
+ z.describe('TIP-20 asset denominating `value` on the vault’s parent chain.'),
701
+ ),
403
702
  })
404
703
 
405
- const addressPosition = z.strictObject({
406
- assetAmount: value.check(
407
- z.describe('Current asset value of the held shares, including fees, in `assetToken` units.'),
408
- ),
409
- assetToken: Tokens.schema.Token.check(z.describe('Asset accepted by the vault.')),
410
- id: Schema.Address.check(
411
- z.describe('Stable resource ID for this position, equal to the vault address.'),
412
- z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] }),
413
- ),
414
- shareAmount: value.check(
415
- z.describe('Earn share balance this account holds, in `shareToken` units.'),
416
- ),
417
- shareToken: Tokens.schema.Token.check(z.describe('Token representing vault shares.')),
418
- valuation: z
419
- .optional(z.nullable(Valuation.schema.Value))
420
- .check(
421
- z.describe(
422
- 'The position’s nominal value when `valuation.currency` is requested, or `null` when ' +
423
- 'the asset token is unverified, its display currency has no rate, or rates were unavailable.',
704
+ const positionApy = z
705
+ .strictObject({
706
+ breakdown: z
707
+ .array(
708
+ z
709
+ .strictObject({
710
+ assetAmount: z
711
+ .strictObject({
712
+ baseUnits: Schema.DecimalString.check(
713
+ z.describe('Position amount in vault asset base units.'),
714
+ z.meta({ examples: ['25000000000'] }),
715
+ ),
716
+ decimals: z
717
+ .number()
718
+ .check(
719
+ z.int(),
720
+ z.nonnegative(),
721
+ z.describe('Vault asset decimal places.'),
722
+ z.meta({ examples: [6] }),
723
+ ),
724
+ formatted: z
725
+ .string()
726
+ .check(
727
+ z.regex(/^\d+(\.\d+)?$/),
728
+ z.describe('Position amount rendered in whole vault asset units.'),
729
+ z.meta({ examples: ['25000'] }),
730
+ ),
731
+ })
732
+ .check(
733
+ z.describe('Vault asset amount receiving this annual rate.'),
734
+ z.meta({
735
+ examples: [{ baseUnits: '25000000000', decimals: 6, formatted: '25000' }],
736
+ }),
737
+ ),
738
+ net: EarnRates.schema.Apy.shape.net.check(
739
+ z.describe('Annual rate applied to this position amount.'),
740
+ z.meta({ examples: ['0.07'] }),
741
+ ),
742
+ type: z
743
+ .enum(['base', 'boost'])
744
+ .check(
745
+ z.describe('Base vault yield or qualified depositor boost.'),
746
+ z.meta({ examples: ['boost'] }),
747
+ ),
748
+ })
749
+ .check(z.describe('Position component receiving one annual rate.')),
750
+ )
751
+ .check(
752
+ z.describe('Position amounts grouped by the annual rate they receive.'),
753
+ z.meta({
754
+ examples: [
755
+ [
756
+ {
757
+ assetAmount: {
758
+ baseUnits: '25000000000',
759
+ decimals: 6,
760
+ formatted: '25000',
761
+ },
762
+ net: '0.07',
763
+ type: 'boost',
764
+ },
765
+ ],
766
+ ],
767
+ }),
424
768
  ),
769
+ net: EarnRates.schema.Apy.shape.net.check(
770
+ z.describe('Balance-weighted annual rate for this wallet position.'),
771
+ z.meta({ examples: ['0.0616666666666667'] }),
425
772
  ),
426
- vaultAddress: Schema.Address.check(
427
- z.describe('Earn vault contract address holding this position.'),
428
- z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] }),
429
- ),
430
- verified: z
431
- .boolean()
432
- .check(
433
- z.describe('Whether the vault is in the curated earn registry.'),
434
- z.meta({ examples: [true] }),
773
+ })
774
+ .check(
775
+ z.describe(
776
+ 'Wallet-specific effective APY combining live position amounts with qualification from the latest delivered reward snapshot.',
435
777
  ),
436
- })
778
+ )
779
+
780
+ const lifetimeCashFlows = z.union([
781
+ z.strictObject({
782
+ status: z
783
+ .literal('complete')
784
+ .check(
785
+ z.describe('Cash flows are complete through the indexed boundary.'),
786
+ z.meta({ examples: ['complete'] }),
787
+ ),
788
+ totalDeposited: Schema.DecimalString.check(
789
+ z.describe('Assets deposited with this account as receiver over indexed vault history.'),
790
+ ),
791
+ totalWithdrawn: Schema.DecimalString.check(
792
+ z.describe('Assets returned by completed withdrawals over indexed vault history.'),
793
+ ),
794
+ }),
795
+ z.strictObject({
796
+ status: z
797
+ .literal('incomplete_history')
798
+ .check(
799
+ z.describe('Cash flows are omitted because asset deposit history is incomplete.'),
800
+ z.meta({ examples: ['incomplete_history'] }),
801
+ ),
802
+ }),
803
+ ])
804
+
805
+ const addressPosition = OpenApi.component(
806
+ z
807
+ .strictObject({
808
+ assetAmount: value.check(
809
+ z.describe(
810
+ 'Current asset value of the held shares, including fees, in `valueToken` units.',
811
+ ),
812
+ ),
813
+ assetToken: Tokens.schema.Token.check(z.describe('Asset accepted by the vault.')),
814
+ chainId: Schema.ChainId.check(
815
+ z.describe('Chain containing the held shares.'),
816
+ z.meta({ examples: [42431] }),
817
+ ),
818
+ id: Schema.Address.check(
819
+ z.describe('Stable resource ID for this position, equal to the vault address.'),
820
+ z.meta({ examples: ['0x4f94590b636f5878bce585e82379de81e1ec174f'] }),
821
+ ),
822
+ lifetimeCashFlows: z
823
+ .optional(lifetimeCashFlows)
824
+ .check(z.describe('Lifetime deposits and completed withdrawals, when requested.')),
825
+ shareAmount: value.check(
826
+ z.describe('Earn share balance this account holds, in `shareToken` units.'),
827
+ ),
828
+ shareToken: Tokens.schema.Token.check(z.describe('Token representing vault shares.')),
829
+ valuation: z
830
+ .optional(z.nullable(Valuation.schema.Value))
831
+ .check(
832
+ z.describe(
833
+ 'The position’s nominal value when `valuation.currency` is requested, or `null` when ' +
834
+ 'the asset token is unverified, its display currency has no rate, or rates were unavailable.',
835
+ ),
836
+ ),
837
+ valueToken: Tokens.schema.Token.check(
838
+ z.describe('Parent-chain asset denominating `assetAmount`.'),
839
+ ),
840
+ vaultAddress: Schema.Address.check(
841
+ z.describe('Earn vault contract address holding this position.'),
842
+ z.meta({ examples: ['0x4f94590b636f5878bce585e82379de81e1ec174f'] }),
843
+ ),
844
+ verified: z
845
+ .boolean()
846
+ .check(
847
+ z.describe('Whether the vault is in the curated earn registry.'),
848
+ z.meta({ examples: [true] }),
849
+ ),
850
+ })
851
+ .check(z.describe('An account’s position in one earn vault.')),
852
+ 'EarnAddressPosition',
853
+ )
437
854
 
438
855
  const earningsBase = z.strictObject({
439
856
  account: Schema.Address.check(
@@ -476,6 +893,18 @@ export namespace schema {
476
893
  ),
477
894
  z.meta({ examples: ['0'] }),
478
895
  ),
896
+ totalDeposited: Schema.DecimalString.check(
897
+ z.describe(
898
+ 'Assets deposited with this account as receiver over complete indexed vault history.',
899
+ ),
900
+ z.meta({ examples: ['1000000'] }),
901
+ ),
902
+ totalWithdrawn: Schema.DecimalString.check(
903
+ z.describe(
904
+ 'Assets returned by completed redemptions and exact withdrawals initiated by this account over complete indexed vault history.',
905
+ ),
906
+ z.meta({ examples: ['1000000'] }),
907
+ ),
479
908
  period: z
480
909
  .literal('lifetime')
481
910
  .check(
@@ -548,9 +977,23 @@ export namespace schema {
548
977
  }),
549
978
  z.extend(earningsBase, {
550
979
  period: z
551
- .enum(['30d', 'lifetime'])
980
+ .literal('30d')
552
981
  .check(
553
- z.describe('Earnings period with an unresolved async redemption.'),
982
+ z.describe('Trailing period with an unresolved async redemption.'),
983
+ z.meta({ examples: ['30d'] }),
984
+ ),
985
+ status: z
986
+ .literal('pending_redemption')
987
+ .check(
988
+ z.describe('Earnings are unavailable while an async redemption remains open.'),
989
+ z.meta({ examples: ['pending_redemption'] }),
990
+ ),
991
+ }),
992
+ z.extend(earningsBase, {
993
+ period: z
994
+ .literal('lifetime')
995
+ .check(
996
+ z.describe('Lifetime period with an unresolved async redemption.'),
554
997
  z.meta({ examples: ['lifetime'] }),
555
998
  ),
556
999
  status: z
@@ -559,6 +1002,12 @@ export namespace schema {
559
1002
  z.describe('Earnings are unavailable while an async redemption remains open.'),
560
1003
  z.meta({ examples: ['pending_redemption'] }),
561
1004
  ),
1005
+ totalDeposited: Schema.DecimalString.check(
1006
+ z.describe('Assets deposited over complete indexed vault history.'),
1007
+ ),
1008
+ totalWithdrawn: Schema.DecimalString.check(
1009
+ z.describe('Assets withdrawn over complete indexed vault history.'),
1010
+ ),
562
1011
  }),
563
1012
  ])
564
1013
 
@@ -606,15 +1055,18 @@ export namespace schema {
606
1055
  export const Query = collectionQuery
607
1056
 
608
1057
  /** Paginated list of compatible deployed Earn vaults. */
609
- export const Response = z
610
- .strictObject({
611
- data: z
612
- .array(Vault)
613
- .check(z.describe('Compatible deployed earn vaults in deployment order.')),
614
- meta: z.optional(meta),
615
- nextCursor: Schema.NextCursor,
616
- })
617
- .check(z.describe('A page of compatible deployed earn vaults.'))
1058
+ export const Response = OpenApi.component(
1059
+ z
1060
+ .strictObject({
1061
+ data: z
1062
+ .array(Vault)
1063
+ .check(z.describe('Compatible deployed earn vaults in deployment order.')),
1064
+ meta: z.optional(meta),
1065
+ nextCursor: Schema.NextCursor,
1066
+ })
1067
+ .check(z.describe('A page of compatible deployed earn vaults.')),
1068
+ 'EarnVaultList',
1069
+ )
618
1070
  }
619
1071
 
620
1072
  /** Schemas for the getVerifiedEarnVaults operation. */
@@ -629,15 +1081,18 @@ export namespace schema {
629
1081
  export const Query = collectionQuery
630
1082
 
631
1083
  /** Paginated list of registry-curated Earn vaults. */
632
- export const Response = z
633
- .strictObject({
634
- data: z
635
- .array(VerifiedVault)
636
- .check(z.describe('Registry-curated earn vaults ordered by vault address.')),
637
- meta: z.optional(meta),
638
- nextCursor: Schema.NextCursor,
639
- })
640
- .check(z.describe('A page of registry-curated earn vaults.'))
1084
+ export const Response = OpenApi.component(
1085
+ z
1086
+ .strictObject({
1087
+ data: z
1088
+ .array(VerifiedVault)
1089
+ .check(z.describe('Registry-curated earn vaults ordered by vault address.')),
1090
+ meta: z.optional(meta),
1091
+ nextCursor: Schema.NextCursor,
1092
+ })
1093
+ .check(z.describe('A page of registry-curated earn vaults.')),
1094
+ 'VerifiedEarnVaultList',
1095
+ )
641
1096
  }
642
1097
 
643
1098
  /** Schemas for the getEarnAddressPositions operation. */
@@ -654,6 +1109,10 @@ export namespace schema {
654
1109
  .strictObject({
655
1110
  chainId: Schema.ChainIdQuery,
656
1111
  cursor: Schema.Cursor,
1112
+ include: Schema.includeQuery(
1113
+ z.enum(['earnings']),
1114
+ 'Comma-separated position fields to include, such as `earnings`. Earnings are unavailable when selecting a private Zone.',
1115
+ ),
657
1116
  limit: Schema.Limit,
658
1117
  'valuation.currency': Schema.Denomination.check(
659
1118
  z.describe(
@@ -687,15 +1146,18 @@ export namespace schema {
687
1146
  .check(z.describe('Page-level valuation rate provenance.'))
688
1147
 
689
1148
  /** Paginated list of the account's earn vault positions. */
690
- export const Response = z
691
- .strictObject({
692
- data: z
693
- .array(addressPosition)
694
- .check(z.describe('Vaults where the account holds earn shares.')),
695
- meta: z.optional(Meta),
696
- nextCursor: Schema.NextCursor,
697
- })
698
- .check(z.describe('A page of the account’s earn vault positions.'))
1149
+ export const Response = OpenApi.component(
1150
+ z
1151
+ .strictObject({
1152
+ data: z
1153
+ .array(addressPosition)
1154
+ .check(z.describe('Vaults where the account holds earn shares.')),
1155
+ meta: z.optional(Meta),
1156
+ nextCursor: Schema.NextCursor,
1157
+ })
1158
+ .check(z.describe('A page of the account’s earn vault positions.')),
1159
+ 'EarnAddressPositionList',
1160
+ )
699
1161
  }
700
1162
 
701
1163
  /** Schemas for the getEarnVault operation. */
@@ -719,19 +1181,115 @@ export namespace schema {
719
1181
  .check(z.describe('Query parameters selecting one earn vault.'))
720
1182
 
721
1183
  /** One compatible Earn vault resolved directly from current chain state. */
722
- export const Response = Schema.describe(
723
- z.extend(vault, { meta: z.optional(meta) }),
724
- 'A compatible earn vault resolved from chain state, with rate provenance for its valued fields.',
1184
+ export const Response = OpenApi.component(
1185
+ Schema.describe(
1186
+ z.extend(vault, { meta: z.optional(meta) }),
1187
+ 'A compatible earn vault resolved from chain state, with rate provenance for its valued fields.',
1188
+ ),
1189
+ 'EarnVaultDetail',
1190
+ )
1191
+ }
1192
+
1193
+ /** Schemas for the getEarnVaultSharePrices operation. */
1194
+ export namespace getEarnVaultSharePrices {
1195
+ const timestamp = z.iso.datetime({ offset: true })
1196
+
1197
+ /** Path parameters identifying one Earn vault. */
1198
+ export const Params = z
1199
+ .strictObject({
1200
+ vaultId: Schema.address('0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a').check(
1201
+ z.describe('Earn vault contract address.'),
1202
+ ),
1203
+ })
1204
+ .check(z.describe('Path parameters identifying one Earn vault share-price series.'))
1205
+
1206
+ /** Query parameters selecting a daily historical share-price range. */
1207
+ export const Query = z
1208
+ .strictObject({
1209
+ chainId: Schema.ChainIdQuery,
1210
+ from: timestamp.check(
1211
+ z.describe('First daily share-price observation, as an ISO 8601 timestamp.'),
1212
+ z.meta({ examples: ['2026-07-01T00:00:00.000Z'] }),
1213
+ ),
1214
+ interval: z
1215
+ ._default(z.literal(EarnSharePrices.interval), EarnSharePrices.interval)
1216
+ .check(
1217
+ z.describe('Time between share-price observations.'),
1218
+ z.meta({ examples: [EarnSharePrices.interval] }),
1219
+ ),
1220
+ to: timestamp.check(
1221
+ z.describe('Last possible daily share-price observation, as an ISO 8601 timestamp.'),
1222
+ z.meta({ examples: ['2026-07-31T00:00:00.000Z'] }),
1223
+ ),
1224
+ })
1225
+ .check(
1226
+ z.refine((query) => Date.parse(query.from) <= Date.parse(query.to), {
1227
+ error: '`from` must be before or equal to `to`.',
1228
+ path: ['from'],
1229
+ }),
1230
+ z.refine(
1231
+ (query) =>
1232
+ Math.floor((Date.parse(query.to) - Date.parse(query.from)) / Ttl.days(1)) + 1 <=
1233
+ EarnSharePrices.maxPoints,
1234
+ {
1235
+ error: `Share-price series are limited to ${EarnSharePrices.maxPoints} daily observations.`,
1236
+ path: ['to'],
1237
+ },
1238
+ ),
1239
+ z.describe('Query parameters selecting an Earn vault share-price series.'),
1240
+ )
1241
+
1242
+ const Point = z
1243
+ .strictObject({
1244
+ sharePrice: value.check(
1245
+ z.describe('Assets returned for one whole Earn share at the observation block.'),
1246
+ ),
1247
+ timestamp: z.iso
1248
+ .datetime()
1249
+ .check(
1250
+ z.describe('Requested daily observation timestamp.'),
1251
+ z.meta({ examples: ['2026-07-01T00:00:00.000Z'] }),
1252
+ ),
1253
+ })
1254
+ .check(z.describe('One historical Earn vault share price.'))
1255
+
1256
+ /** Daily share prices for one Earn vault. */
1257
+ export const Response = OpenApi.component(
1258
+ z
1259
+ .strictObject({
1260
+ data: z.array(Point).check(
1261
+ z.describe('Daily Earn vault share prices.'),
1262
+ z.meta({
1263
+ examples: [
1264
+ [
1265
+ {
1266
+ sharePrice: {
1267
+ amount: '1000000',
1268
+ currency: 'USD',
1269
+ decimals: 6,
1270
+ formatted: '1',
1271
+ },
1272
+ timestamp: '2026-07-01T00:00:00.000Z',
1273
+ },
1274
+ ],
1275
+ ],
1276
+ }),
1277
+ ),
1278
+ })
1279
+ .check(z.describe('Daily historical share prices for one Earn vault.')),
1280
+ 'EarnVaultSharePriceList',
725
1281
  )
726
1282
  }
727
1283
 
728
1284
  /** Schemas for the getEarnVaultPosition operation. */
729
1285
  export namespace getEarnVaultPosition {
1286
+ const asOf = z.iso.datetime({ offset: true })
1287
+
730
1288
  /** Path parameters identifying the Earn vault and account. */
731
1289
  export const Params = z
732
1290
  .strictObject({
733
1291
  address: Schema.Address.check(z.describe('Account whose earn position to read.')),
734
- vaultId: Schema.address('0xdb48b00fb28d9a8db881fcd671c3fb1bf8c486b6').check(
1292
+ vaultId: Schema.address('0x4f94590b636f5878bce585e82379de81e1ec174f').check(
735
1293
  z.describe('Earn vault contract address.'),
736
1294
  ),
737
1295
  })
@@ -739,13 +1297,116 @@ export namespace schema {
739
1297
 
740
1298
  /** Query parameters selecting the chain containing the vault. */
741
1299
  export const Query = z
742
- .strictObject({ chainId: Schema.ChainIdQuery })
1300
+ .strictObject({
1301
+ asOf: z
1302
+ .optional(asOf)
1303
+ .check(
1304
+ z.describe(
1305
+ 'ISO 8601 timestamp at which to read the position. Resolves the latest indexed block at or before this time.',
1306
+ ),
1307
+ z.meta({ examples: ['2026-07-21T09:00:00.000Z'] }),
1308
+ ),
1309
+ chainId: Schema.ChainIdQuery,
1310
+ include: Schema.includeQuery(
1311
+ z.enum(['apy', 'rewards', 'rewards.proof']),
1312
+ 'Include wallet-specific APY or current reward state, optionally with the latest recovery proof.',
1313
+ ),
1314
+ })
743
1315
  .check(z.describe('Query parameters selecting an earn vault position.'))
744
1316
 
745
- /** Current asset and Earn-share state for one account in one vault. */
746
- export const Response = Schema.describe(
747
- position,
748
- 'Current asset and earn-share balances, allowances, and asset value for an account in one earn vault.',
1317
+ const Block = z
1318
+ .strictObject({
1319
+ number: z
1320
+ .number()
1321
+ .check(
1322
+ z.int(),
1323
+ z.nonnegative(),
1324
+ z.describe('Indexed observation block.'),
1325
+ z.meta({ examples: [31039238] }),
1326
+ ),
1327
+ timestamp: z.iso
1328
+ .datetime()
1329
+ .check(
1330
+ z.describe('Timestamp of the indexed observation block.'),
1331
+ z.meta({ examples: ['2026-07-21T09:00:00.000Z'] }),
1332
+ ),
1333
+ })
1334
+ .check(z.describe('Indexed block used for a historical Earn position observation.'))
1335
+
1336
+ /** Current or historical asset and Earn-share state for one account in one vault. */
1337
+ const Rewards = z
1338
+ .strictObject({
1339
+ cumulativeEntitlement: Schema.DecimalString.check(
1340
+ z.describe('Cumulative EarnShare entitlement.'),
1341
+ ),
1342
+ cumulativePaid: Schema.DecimalString.check(z.describe('Cumulative EarnShare paid.')),
1343
+ deferral: z
1344
+ .optional(z.string())
1345
+ .check(
1346
+ z.describe('Current payout delivery exception.'),
1347
+ z.meta({ examples: ['transfer_unavailable'] }),
1348
+ ),
1349
+ pending: Schema.DecimalString.check(z.describe('EarnShare still owed.')),
1350
+ proof: z
1351
+ .optional(
1352
+ z.strictObject({
1353
+ cumulativeAmount: Schema.DecimalString,
1354
+ distributorAddress: Schema.Address,
1355
+ proof: z.array(Schema.Hash),
1356
+ rootVersion: Schema.DecimalString,
1357
+ statementHash: Schema.Hash,
1358
+ }),
1359
+ )
1360
+ .check(z.describe('Permissionless recovery proof against the confirmed root.')),
1361
+ })
1362
+ .check(z.describe('Current Earn reward state.'))
1363
+
1364
+ export const Response = OpenApi.component(
1365
+ Schema.describe(
1366
+ z.extend(position, {
1367
+ apy: z.optional(z.nullable(positionApy)).check(
1368
+ z.describe(
1369
+ 'Wallet-specific effective APY when requested, or null when unavailable. Qualification comes from the latest delivered reward snapshot and may lag recent deposits or transfers.',
1370
+ ),
1371
+ z.meta({
1372
+ examples: [
1373
+ {
1374
+ breakdown: [
1375
+ {
1376
+ assetAmount: {
1377
+ baseUnits: '25000000000',
1378
+ decimals: 6,
1379
+ formatted: '25000',
1380
+ },
1381
+ net: '0.07',
1382
+ type: 'boost',
1383
+ },
1384
+ ],
1385
+ net: '0.07',
1386
+ },
1387
+ ],
1388
+ }),
1389
+ ),
1390
+ asOf: z
1391
+ .optional(asOf)
1392
+ .check(
1393
+ z.describe('Requested historical observation time, when supplied.'),
1394
+ z.meta({ examples: ['2026-07-21T09:00:00.000Z'] }),
1395
+ ),
1396
+ block: z
1397
+ .optional(Block)
1398
+ .check(
1399
+ z.describe(
1400
+ 'Indexed block used for a historical Earn position observation, when requested.',
1401
+ ),
1402
+ ),
1403
+ rewards: z
1404
+ .optional(Rewards)
1405
+ .check(z.describe('Current reward state when requested through `include`.')),
1406
+ }),
1407
+ 'Current or historical asset and earn-share balances, allowances, and asset value for an account in one earn vault.',
1408
+ ),
1409
+ 'EarnVaultPosition',
749
1410
  )
750
1411
  }
751
1412
 
@@ -770,9 +1431,12 @@ export namespace schema {
770
1431
  .check(z.describe('Query parameters selecting an earn vault account.'))
771
1432
 
772
1433
  /** Earnings for one account and period in one Earn vault. */
773
- export const Response = Schema.describe(
774
- earnings,
775
- 'Indexed asset value and cost-basis-aware earnings for an account in one earn vault.',
1434
+ export const Response = OpenApi.component(
1435
+ Schema.describe(
1436
+ earnings,
1437
+ 'Indexed asset value and cost-basis-aware earnings for an account in one earn vault.',
1438
+ ),
1439
+ 'EarnVaultEarnings',
776
1440
  )
777
1441
  }
778
1442
  }
@@ -781,6 +1445,113 @@ export namespace schema {
781
1445
  export function earn(options: earn.Options = {}) {
782
1446
  const oracle = options.fx?.oracle ?? FxOracle.ecb()
783
1447
  return new Hono<App.Environment>()
1448
+ .post(
1449
+ '/v1/earn/rewards/eligibility',
1450
+ Auth.policy({ apiKey: { scopes: [Scope.wildcard] } }),
1451
+ OpenApi.validate('json', schema.registerRewardEligibility.Body, {
1452
+ code: 'body_invalid',
1453
+ message: 'Invalid reward eligibility registration',
1454
+ }),
1455
+ OpenApi.describeRoute({
1456
+ description: 'Registers a wallet and Earn vault association for reward eligibility.',
1457
+ hide: true,
1458
+ operationId: 'registerRewardEligibility',
1459
+ responses: OpenApi.responses({
1460
+ errors: {
1461
+ 400: { codes: ['body_invalid', 'chain_id_unsupported'] },
1462
+ },
1463
+ success: {
1464
+ description: 'The persisted reward eligibility association.',
1465
+ schema: schema.registerRewardEligibility.Response,
1466
+ },
1467
+ }),
1468
+ summary: 'Register reward eligibility',
1469
+ tags: ['Earn'],
1470
+ }),
1471
+ async (c) => {
1472
+ if (Auth.narrowAccess) return Auth.accessError(c)
1473
+ if (OpenApi.narrowValidation)
1474
+ return OpenApi.validationError(c, {
1475
+ code: 'body_invalid',
1476
+ message: 'Invalid reward eligibility registration',
1477
+ })
1478
+
1479
+ const body = c.req.valid('json')
1480
+ if (!c.get('supportedChainIds').has(body.chainId))
1481
+ return Response.unsupportedChainId(c, body.chainId, c.get('supportedChainIds'))
1482
+ c.set('chainId', body.chainId)
1483
+ c.header('Cache-Control', Cache.policies.noStore)
1484
+ const result = await RewardEligibilityAssociations.upsert(Db.get(c.get('db')), {
1485
+ ...body,
1486
+ })
1487
+ return c.json(
1488
+ Response.validated(schema.registerRewardEligibility.Response, {
1489
+ association: toRewardEligibilityAssociation(result.record),
1490
+ created: result.created,
1491
+ }),
1492
+ 200,
1493
+ )
1494
+ },
1495
+ )
1496
+ .get(
1497
+ '/v1/earn/rewards/eligibility',
1498
+ Auth.policy({ apiKey: { scopes: ['rewards:read'] } }),
1499
+ OpenApi.validate('query', schema.getRewardEligibility.Query, {
1500
+ code: 'query_invalid',
1501
+ message: 'Invalid reward eligibility selector',
1502
+ }),
1503
+ OpenApi.describeRoute({
1504
+ description:
1505
+ 'Lists reward eligibility associations for one Earn vault at a fixed high-water checkpoint.',
1506
+ hide: true,
1507
+ operationId: 'getRewardEligibility',
1508
+ responses: OpenApi.responses({
1509
+ success: {
1510
+ description: 'A checkpointed page of reward eligibility associations.',
1511
+ schema: schema.getRewardEligibility.Response,
1512
+ },
1513
+ }),
1514
+ summary: 'List reward eligibility',
1515
+ tags: ['Earn'],
1516
+ }),
1517
+ async (c) => {
1518
+ if (Auth.narrowAccess) return Auth.accessError(c)
1519
+ if (OpenApi.narrowValidation)
1520
+ return OpenApi.validationError(c, {
1521
+ code: 'query_invalid',
1522
+ message: 'Invalid reward eligibility selector',
1523
+ })
1524
+
1525
+ const query = c.req.valid('query')
1526
+ const zone = c.get('zones').get(query.chainId)
1527
+ const sourceChainId = zone ? zone.sourceId : query.chainId
1528
+ const principal = Auth.getPrincipal(c)
1529
+ if (
1530
+ principal?.type === 'api_key' &&
1531
+ principal.environment === 'sandbox' &&
1532
+ (sourceChainId === undefined || Viem.isMainnet(sourceChainId))
1533
+ )
1534
+ return Response.error(c, {
1535
+ code: 'api_key_forbidden',
1536
+ message: 'Sandbox API keys only support testnet.',
1537
+ status: 403,
1538
+ })
1539
+
1540
+ c.header('Cache-Control', Cache.policies.noStore)
1541
+ const page = await RewardEligibilityAssociations.list(Db.get(c.get('db')), {
1542
+ ...query,
1543
+ })
1544
+ return c.json(
1545
+ Response.validated(schema.getRewardEligibility.Response, {
1546
+ chainId: query.chainId,
1547
+ data: page.data.map((association) => association.walletAddress),
1548
+ nextCursor: page.nextCursor,
1549
+ vaultAddress: query.vaultAddress,
1550
+ }),
1551
+ 200,
1552
+ )
1553
+ },
1554
+ )
784
1555
  .get(
785
1556
  '/v1/earn/vaults',
786
1557
  Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
@@ -793,7 +1564,12 @@ export function earn(options: earn.Options = {}) {
793
1564
  'Lists compatible earn vaults discovered from deployment events and resolved from current chain state.',
794
1565
  operationId: 'getEarnVaults',
795
1566
  responses: OpenApi.responses({
796
- errors: { 502: 'Could not read earn deployment or vault data.' },
1567
+ errors: {
1568
+ 400: {
1569
+ codes: ['chain_id_invalid', 'chain_id_unsupported', 'query_invalid'],
1570
+ },
1571
+ 502: 'Could not read earn deployment or vault data.',
1572
+ },
797
1573
  success: {
798
1574
  description: 'A page of compatible deployed earn vaults.',
799
1575
  schema: schema.getEarnVaults.Response,
@@ -807,10 +1583,22 @@ export function earn(options: earn.Options = {}) {
807
1583
  Cache.response<App.Environment>({
808
1584
  cacheControl: Cache.policies.state,
809
1585
  key: (c) => vaultCacheKey(c, schema.getEarnVaults.Query),
810
- name: 'tempo-api:earn-vaults:v3',
1586
+ name: 'tempo-api:earn-vaults:v5',
811
1587
  }),
812
1588
  async (c) => {
813
1589
  if (Auth.narrowAccess) return Auth.paidAccessError(c)
1590
+ if (OpenApi.narrowValidation)
1591
+ return Response.error(c, {
1592
+ code: 'chain_id_invalid',
1593
+ message: 'Invalid chain id',
1594
+ status: 400,
1595
+ })
1596
+ if (OpenApi.narrowValidation)
1597
+ return Response.error(c, {
1598
+ code: 'chain_id_unsupported',
1599
+ message: 'Unsupported chain id',
1600
+ status: 400,
1601
+ })
814
1602
  if (OpenApi.narrowValidation)
815
1603
  return OpenApi.validationError(c, {
816
1604
  code: 'query_invalid',
@@ -839,7 +1627,12 @@ export function earn(options: earn.Options = {}) {
839
1627
  'Lists registry-curated earn vaults after resolving their current onchain configuration.',
840
1628
  operationId: 'getVerifiedEarnVaults',
841
1629
  responses: OpenApi.responses({
842
- errors: { 502: 'Could not read curated earn registry or vault data.' },
1630
+ errors: {
1631
+ 400: {
1632
+ codes: ['chain_id_invalid', 'chain_id_unsupported', 'query_invalid'],
1633
+ },
1634
+ 502: 'Could not read curated earn registry or vault data.',
1635
+ },
843
1636
  success: {
844
1637
  description: 'A page of registry-curated earn vaults.',
845
1638
  schema: schema.getVerifiedEarnVaults.Response,
@@ -851,67 +1644,166 @@ export function earn(options: earn.Options = {}) {
851
1644
  Cache.response<App.Environment>({
852
1645
  cacheControl: Cache.policies.state,
853
1646
  key: (c) => vaultCacheKey(c, schema.getVerifiedEarnVaults.Query),
854
- name: 'tempo-api:earn-vaults:v3',
1647
+ name: 'tempo-api:earn-vaults:v4',
1648
+ }),
1649
+ async (c) => {
1650
+ if (Auth.narrowAccess) return Auth.paidAccessError(c)
1651
+ if (OpenApi.narrowValidation)
1652
+ return Response.error(c, {
1653
+ code: 'chain_id_invalid',
1654
+ message: 'Invalid chain id',
1655
+ status: 400,
1656
+ })
1657
+ if (OpenApi.narrowValidation)
1658
+ return Response.error(c, {
1659
+ code: 'chain_id_unsupported',
1660
+ message: 'Unsupported chain id',
1661
+ status: 400,
1662
+ })
1663
+ if (OpenApi.narrowValidation)
1664
+ return OpenApi.validationError(c, {
1665
+ code: 'query_invalid',
1666
+ message: 'Invalid query parameters',
1667
+ })
1668
+
1669
+ const query = c.req.valid('query')
1670
+ try {
1671
+ const chainId = query.chainId ?? c.get('chainId')
1672
+ const data = await listVerifiedVaults(c, {
1673
+ ...query,
1674
+ chainId,
1675
+ oracle,
1676
+ })
1677
+ return c.json(Response.validated(schema.getVerifiedEarnVaults.Response, data), 200)
1678
+ } catch (cause) {
1679
+ return Response.upstream(c, cause)
1680
+ }
1681
+ },
1682
+ )
1683
+ .get(
1684
+ '/v1/earn/addresses/:address/positions',
1685
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
1686
+ OpenApi.validate('param', schema.getEarnAddressPositions.Params, {
1687
+ code: 'positions_invalid',
1688
+ message: 'Invalid address positions',
1689
+ }),
1690
+ OpenApi.validate('query', schema.getEarnAddressPositions.Query, {
1691
+ code: 'query_invalid',
1692
+ message: 'Invalid query parameters',
1693
+ }),
1694
+ OpenApi.describeRoute({
1695
+ description:
1696
+ 'Lists every earn vault where an account currently holds shares on the selected Tempo chain or private Zone. Lifetime cash flows are available on Tempo chains.',
1697
+ operationId: 'getEarnAddressPositions',
1698
+ responses: OpenApi.responses({
1699
+ errors: {
1700
+ 400: {
1701
+ codes: [
1702
+ 'chain_id_invalid',
1703
+ 'chain_id_unsupported',
1704
+ 'positions_invalid',
1705
+ 'query_invalid',
1706
+ ],
1707
+ },
1708
+ 502: 'Could not read earn deployment or position data.',
1709
+ },
1710
+ success: {
1711
+ description: 'A page of the account’s earn vault positions.',
1712
+ schema: schema.getEarnAddressPositions.Response,
1713
+ },
1714
+ }),
1715
+ summary: 'List account positions',
1716
+ tags: ['Earn'],
1717
+ }),
1718
+ // `noStore` matches the address balances listing: account holdings must
1719
+ // read fresh immediately after a deposit or redemption.
1720
+ Cache.response<App.Environment>({
1721
+ cacheControl: Cache.policies.noStore,
1722
+ key: (c) => Cache.urlKey(c, schema.getEarnAddressPositions.Query),
1723
+ name: 'tempo-api:earn-address-positions:v2',
855
1724
  }),
856
1725
  async (c) => {
857
1726
  if (Auth.narrowAccess) return Auth.paidAccessError(c)
1727
+ if (OpenApi.narrowValidation)
1728
+ return Response.error(c, {
1729
+ code: 'chain_id_invalid',
1730
+ message: 'Invalid chain id',
1731
+ status: 400,
1732
+ })
1733
+ if (OpenApi.narrowValidation)
1734
+ return Response.error(c, {
1735
+ code: 'chain_id_unsupported',
1736
+ message: 'Unsupported chain id',
1737
+ status: 400,
1738
+ })
1739
+ if (OpenApi.narrowValidation)
1740
+ return OpenApi.validationError(c, {
1741
+ code: 'positions_invalid',
1742
+ message: 'Invalid request parameters',
1743
+ })
858
1744
  if (OpenApi.narrowValidation)
859
1745
  return OpenApi.validationError(c, {
860
1746
  code: 'query_invalid',
861
1747
  message: 'Invalid query parameters',
862
1748
  })
863
1749
 
1750
+ const { address } = c.req.valid('param')
864
1751
  const query = c.req.valid('query')
1752
+ const chainId = query.chainId ?? c.get('chainId')
865
1753
  try {
866
- const chainId = query.chainId ?? c.get('chainId')
867
- const data = await listVerifiedVaults(c, { ...query, chainId, oracle })
868
- return c.json(Response.validated(schema.getVerifiedEarnVaults.Response, data), 200)
1754
+ const data = await listPositions(c, { ...query, address, chainId, oracle })
1755
+ return c.json(Response.validated(schema.getEarnAddressPositions.Response, data), 200)
869
1756
  } catch (cause) {
1757
+ if (cause instanceof Valuation.UnsupportedDenominationError)
1758
+ return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
1759
+ if (cause instanceof ZoneEarningsUnsupportedError)
1760
+ return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
870
1761
  return Response.upstream(c, cause)
871
1762
  }
872
1763
  },
873
1764
  )
874
1765
  .get(
875
- '/v1/earn/addresses/:address/positions',
1766
+ '/v1/earn/vaults/:vaultId/share-prices',
876
1767
  Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
877
- OpenApi.validate('param', schema.getEarnAddressPositions.Params, {
878
- code: 'positions_invalid',
879
- message: 'Invalid address positions',
1768
+ OpenApi.validate('param', schema.getEarnVaultSharePrices.Params, {
1769
+ code: 'vault_id_invalid',
1770
+ message: 'Invalid vault id',
880
1771
  }),
881
- OpenApi.validate('query', schema.getEarnAddressPositions.Query, {
1772
+ OpenApi.validate('query', schema.getEarnVaultSharePrices.Query, {
882
1773
  code: 'query_invalid',
883
1774
  message: 'Invalid query parameters',
884
1775
  }),
885
1776
  OpenApi.describeRoute({
886
- description:
887
- 'Lists every earn vault where an account currently holds shares, with the current balance and value of each position.',
888
- operationId: 'getEarnAddressPositions',
1777
+ description: 'Lists daily historical share prices for one earn vault.',
1778
+ operationId: 'getEarnVaultSharePrices',
889
1779
  responses: OpenApi.responses({
890
1780
  errors: {
891
1781
  400: {
892
1782
  codes: [
893
1783
  'chain_id_invalid',
894
1784
  'chain_id_unsupported',
895
- 'positions_invalid',
896
1785
  'query_invalid',
1786
+ 'vault_id_invalid',
897
1787
  ],
898
1788
  },
899
- 502: 'Could not read earn deployment or position data.',
1789
+ 404: {
1790
+ codes: ['earn_vault_not_found'],
1791
+ description: 'No compatible earn vault was found at the supplied vault address.',
1792
+ },
1793
+ 502: 'Could not read historical earn vault share prices.',
900
1794
  },
901
1795
  success: {
902
- description: 'A page of the account’s earn vault positions.',
903
- schema: schema.getEarnAddressPositions.Response,
1796
+ description: 'Daily historical share prices for one earn vault.',
1797
+ schema: schema.getEarnVaultSharePrices.Response,
904
1798
  },
905
1799
  }),
906
- summary: 'List account positions',
1800
+ summary: 'List share prices',
907
1801
  tags: ['Earn'],
908
1802
  }),
909
- // `noStore` matches the address balances listing: account holdings must
910
- // read fresh immediately after a deposit or redemption.
911
1803
  Cache.response<App.Environment>({
912
- cacheControl: Cache.policies.noStore,
913
- key: (c) => Cache.urlKey(c, schema.getEarnAddressPositions.Query),
914
- name: 'tempo-api:earn-address-positions:v1',
1804
+ cacheControl: Cache.policies.state,
1805
+ key: (c) => Cache.urlKey(c, schema.getEarnVaultSharePrices.Query),
1806
+ name: 'tempo-api:earn-vault-share-prices:v1',
915
1807
  }),
916
1808
  async (c) => {
917
1809
  if (Auth.narrowAccess) return Auth.paidAccessError(c)
@@ -929,8 +1821,8 @@ export function earn(options: earn.Options = {}) {
929
1821
  })
930
1822
  if (OpenApi.narrowValidation)
931
1823
  return OpenApi.validationError(c, {
932
- code: 'positions_invalid',
933
- message: 'Invalid request parameters',
1824
+ code: 'vault_id_invalid',
1825
+ message: 'Invalid vault id',
934
1826
  })
935
1827
  if (OpenApi.narrowValidation)
936
1828
  return OpenApi.validationError(c, {
@@ -938,15 +1830,35 @@ export function earn(options: earn.Options = {}) {
938
1830
  message: 'Invalid query parameters',
939
1831
  })
940
1832
 
941
- const { address } = c.req.valid('param')
1833
+ const { vaultId } = c.req.valid('param')
942
1834
  const query = c.req.valid('query')
943
1835
  const chainId = query.chainId ?? c.get('chainId')
944
1836
  try {
945
- const data = await listPositions(c, { ...query, address, chainId, oracle })
946
- return c.json(Response.validated(schema.getEarnAddressPositions.Response, data), 200)
1837
+ const discovery = await resolveSharePriceVaultDiscovery(c, {
1838
+ chainId,
1839
+ vaultAddress: vaultId,
1840
+ })
1841
+ if (!discovery)
1842
+ return Response.error(c, {
1843
+ code: 'earn_vault_not_found',
1844
+ message: 'Earn vault not found',
1845
+ status: 404,
1846
+ })
1847
+ const data = await getVaultSharePrices(c, {
1848
+ ...query,
1849
+ chainId,
1850
+ discovery,
1851
+ vault: vaultId,
1852
+ })
1853
+ return c.json(Response.validated(schema.getEarnVaultSharePrices.Response, { data }), 200)
947
1854
  } catch (cause) {
948
- if (cause instanceof Valuation.UnsupportedDenominationError)
1855
+ if (
1856
+ cause instanceof EarnSharePrices.IndexedBlockNotFoundError ||
1857
+ cause instanceof EarnSharePrices.IndexedCoverageError
1858
+ )
949
1859
  return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
1860
+ const failure = sharePriceProviderFailure({ cause, chainId })
1861
+ if (failure) c.set('providerFailure', failure)
950
1862
  return Response.upstream(c, cause)
951
1863
  }
952
1864
  },
@@ -964,7 +1876,7 @@ export function earn(options: earn.Options = {}) {
964
1876
  }),
965
1877
  OpenApi.describeRoute({
966
1878
  description:
967
- 'Gets an account’s asset and earn-share balances, allowances, and current asset value for one earn vault.',
1879
+ 'Gets an account’s direct or Zone-routed balances and asset value for one earn vault, optionally at an ISO 8601 timestamp.',
968
1880
  operationId: 'getEarnVaultPosition',
969
1881
  responses: OpenApi.responses({
970
1882
  errors: {
@@ -983,17 +1895,18 @@ export function earn(options: earn.Options = {}) {
983
1895
  502: 'Could not read the earn vault position.',
984
1896
  },
985
1897
  success: {
986
- description: 'Current earn position for one account in one vault.',
1898
+ description: 'Current or historical earn position for one account in one vault.',
987
1899
  schema: schema.getEarnVaultPosition.Response,
988
1900
  },
989
1901
  }),
990
1902
  summary: 'Get account position',
991
1903
  tags: ['Earn'],
992
1904
  }),
1905
+ // Account holdings must read fresh immediately after a deposit or redemption.
993
1906
  Cache.response<App.Environment>({
994
- cacheControl: Cache.policies.state,
1907
+ cacheControl: Cache.policies.noStore,
995
1908
  key: (c) => Cache.urlKey(c, schema.getEarnVaultPosition.Query),
996
- name: 'tempo-api:earn-vault-positions:v1',
1909
+ name: 'tempo-api:earn-vault-positions:v3',
997
1910
  }),
998
1911
  async (c) => {
999
1912
  if (Auth.narrowAccess) return Auth.paidAccessError(c)
@@ -1023,25 +1936,123 @@ export function earn(options: earn.Options = {}) {
1023
1936
  const { address, vaultId } = c.req.valid('param')
1024
1937
  const query = c.req.valid('query')
1025
1938
  const chainId = query.chainId ?? c.get('chainId')
1939
+ if (query.asOf && query.include.length > 0)
1940
+ return Response.error(c, {
1941
+ code: 'query_invalid',
1942
+ message: 'Historical position enrichments are not available.',
1943
+ status: 400,
1944
+ })
1945
+ if (c.get('zones').has(chainId)) Cache.setPolicy(c, Cache.policies.noStore)
1026
1946
  try {
1027
- const position = await c
1028
- .get('getClient')(chainId)
1029
- .earn.getPosition({ account: address, vault: vaultId })
1947
+ const position = await getVaultPosition(c, {
1948
+ account: address,
1949
+ asOf: query.asOf,
1950
+ chainId,
1951
+ vault: vaultId,
1952
+ })
1953
+ const apy = query.include.includes('apy')
1954
+ ? await resolvePositionApy(c, {
1955
+ chainId,
1956
+ recipient: address,
1957
+ shareBalance: position.shareBalance,
1958
+ value: position.value,
1959
+ vault: vaultId,
1960
+ })
1961
+ : undefined
1962
+ const rewards = query.include.some((value) => value.startsWith('rewards'))
1963
+ ? await RewardRead.recipient({
1964
+ chainId,
1965
+ db: c.get('db'),
1966
+ getClient: c.get('getClient'),
1967
+ recipient: address,
1968
+ vaultAddress: vaultId,
1969
+ })
1970
+ : undefined
1030
1971
  return c.json(
1031
1972
  Response.validated(schema.getEarnVaultPosition.Response, {
1032
1973
  account: address,
1974
+ ...(query.include.includes('apy') ? { apy } : {}),
1033
1975
  assetAllowance: position.assetAllowance.toString(),
1034
1976
  assetBalance: position.assetBalance.toString(),
1035
1977
  assetToken: position.assetToken,
1978
+ chainId,
1979
+ asOf: query.asOf,
1980
+ block: position.block,
1036
1981
  id: address,
1982
+ ...(rewards
1983
+ ? {
1984
+ rewards: query.include.includes('rewards.proof')
1985
+ ? rewards
1986
+ : { ...rewards, proof: undefined },
1987
+ }
1988
+ : {}),
1037
1989
  shareAllowance: position.shareAllowance.toString(),
1038
1990
  shareBalance: position.shareBalance.toString(),
1039
1991
  shareToken: position.shareToken,
1040
1992
  value: position.value.toString(),
1993
+ valueToken: position.valueToken,
1041
1994
  }),
1042
1995
  200,
1043
1996
  )
1044
1997
  } catch (cause) {
1998
+ if (cause instanceof IndexedBlockNotFoundError || cause instanceof IndexedCoverageError)
1999
+ return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
2000
+ if (cause instanceof ZoneHistoricalAlignmentError)
2001
+ return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
2002
+ if (cause instanceof EarnVaults.NotFoundError)
2003
+ return Response.error(c, {
2004
+ code: 'earn_vault_not_found',
2005
+ message: 'Earn vault not found',
2006
+ status: 404,
2007
+ })
2008
+ const zone = c.get('zones').get(chainId)
2009
+ if (query.asOf && !zone) {
2010
+ try {
2011
+ const block = await getIndexedBlockAtOrBefore(c.get('getTidx')(chainId), query.asOf)
2012
+ const client = c.get('getClient')(chainId)
2013
+ // These immutable bindings establish historical vault identity
2014
+ // without repeating fallible token or redemption subreads.
2015
+ const [asset, shareToken] = await Promise.all([
2016
+ client.readContract({
2017
+ abi: Abis.earnVault,
2018
+ address: vaultId,
2019
+ blockNumber: BigInt(block.number),
2020
+ functionName: 'asset',
2021
+ }),
2022
+ client.readContract({
2023
+ abi: Abis.earnVault,
2024
+ address: vaultId,
2025
+ blockNumber: BigInt(block.number),
2026
+ functionName: 'earnShare',
2027
+ }),
2028
+ ])
2029
+ if (asset === zeroAddress || shareToken === zeroAddress)
2030
+ return Response.error(c, {
2031
+ code: 'earn_vault_not_found',
2032
+ message: 'Earn vault not found',
2033
+ status: 404,
2034
+ })
2035
+ } catch (identityCause) {
2036
+ if (
2037
+ identityCause instanceof IndexedBlockNotFoundError ||
2038
+ identityCause instanceof IndexedCoverageError
2039
+ )
2040
+ return Response.error(c, {
2041
+ code: 'query_invalid',
2042
+ message: identityCause.message,
2043
+ status: 400,
2044
+ })
2045
+ if (isIncompatible(identityCause))
2046
+ return Response.error(c, {
2047
+ code: 'earn_vault_not_found',
2048
+ message: 'Earn vault not found',
2049
+ status: 404,
2050
+ })
2051
+ return Response.upstream(c, identityCause)
2052
+ }
2053
+ return Response.upstream(c, cause)
2054
+ }
2055
+ if (zone) return Response.upstream(c, cause)
1045
2056
  try {
1046
2057
  await resolveVaultDiscovery(c, { chainId, include: [], vaultAddress: vaultId })
1047
2058
  } catch (vaultCause) {
@@ -1098,7 +2109,7 @@ export function earn(options: earn.Options = {}) {
1098
2109
  Cache.response<App.Environment>({
1099
2110
  cacheControl: Cache.policies.state,
1100
2111
  key: (c) => Cache.urlKey(c, schema.getEarnVaultEarnings.Query),
1101
- name: 'tempo-api:earn-vault-earnings:v3',
2112
+ name: 'tempo-api:earn-vault-earnings:v4',
1102
2113
  }),
1103
2114
  async (c) => {
1104
2115
  if (Auth.narrowAccess) return Auth.paidAccessError(c)
@@ -1168,6 +2179,14 @@ export function earn(options: earn.Options = {}) {
1168
2179
  operationId: 'getEarnVault',
1169
2180
  responses: OpenApi.responses({
1170
2181
  errors: {
2182
+ 400: {
2183
+ codes: [
2184
+ 'chain_id_invalid',
2185
+ 'chain_id_unsupported',
2186
+ 'query_invalid',
2187
+ 'vault_id_invalid',
2188
+ ],
2189
+ },
1171
2190
  404: {
1172
2191
  codes: ['earn_vault_not_found'],
1173
2192
  description: 'No compatible earn vault was found at this address.',
@@ -1185,10 +2204,22 @@ export function earn(options: earn.Options = {}) {
1185
2204
  Cache.response<App.Environment>({
1186
2205
  cacheControl: Cache.policies.state,
1187
2206
  key: (c) => vaultCacheKey(c, schema.getEarnVault.Query),
1188
- name: 'tempo-api:earn-vaults:v3',
2207
+ name: 'tempo-api:earn-vaults:v4',
1189
2208
  }),
1190
2209
  async (c) => {
1191
2210
  if (Auth.narrowAccess) return Auth.paidAccessError(c)
2211
+ if (OpenApi.narrowValidation)
2212
+ return Response.error(c, {
2213
+ code: 'chain_id_invalid',
2214
+ message: 'Invalid chain id',
2215
+ status: 400,
2216
+ })
2217
+ if (OpenApi.narrowValidation)
2218
+ return Response.error(c, {
2219
+ code: 'chain_id_unsupported',
2220
+ message: 'Unsupported chain id',
2221
+ status: 400,
2222
+ })
1192
2223
  if (OpenApi.narrowValidation)
1193
2224
  return OpenApi.validationError(c, {
1194
2225
  code: 'vault_id_invalid',
@@ -1240,7 +2271,10 @@ export async function getVaultEarnings(
1240
2271
  ) {
1241
2272
  const client = c.get('getClient')(options.chainId)
1242
2273
  const tidx = c.get('getTidx')(options.chainId)
1243
- const block = await getLatestIndexedBlock(tidx)
2274
+ const [block, rewardTransfer] = await Promise.all([
2275
+ getLatestIndexedBlock(tidx),
2276
+ getRewardTransferPredicate(c, options),
2277
+ ])
1244
2278
  const blockNumber = block.number
1245
2279
  if (options.period === '30d')
1246
2280
  return getWindowVaultEarnings(c, {
@@ -1248,6 +2282,7 @@ export async function getVaultEarnings(
1248
2282
  blockNumber,
1249
2283
  chainId: options.chainId,
1250
2284
  period: options.period,
2285
+ rewardTransfer,
1251
2286
  timestamp: block.timestamp,
1252
2287
  vault: options.vault,
1253
2288
  })
@@ -1268,6 +2303,7 @@ export async function getVaultEarnings(
1268
2303
  const activeCostBasis = await getActiveCostBasis(tidx, {
1269
2304
  account: options.account,
1270
2305
  blockNumber,
2306
+ rewardTransfer,
1271
2307
  shareBalance: current.shareBalance,
1272
2308
  shareToken: current.shareToken,
1273
2309
  vault: options.vault,
@@ -1291,6 +2327,7 @@ export async function getVaultEarnings(
1291
2327
  const externalShareTransfer = await hasExternalShareTransfer(tidx, {
1292
2328
  account: options.account,
1293
2329
  blockNumber,
2330
+ rewardTransfer,
1294
2331
  shareToken: current.shareToken,
1295
2332
  startBlockNumber: beforeFirstBlockNumber,
1296
2333
  vault: options.vault,
@@ -1304,10 +2341,20 @@ export async function getVaultEarnings(
1304
2341
  }
1305
2342
  if (history.incompleteCostBasis || externalShareTransfer)
1306
2343
  return { ...base, status: 'incomplete_cost_basis' as const }
1307
- if (history.pendingRedemption) return { ...base, status: 'pending_redemption' as const }
1308
- const lifetimeEarnings =
1309
- current.value + history.redeemed + history.withdrewExact + history.finalized - history.deposited
1310
- return { ...base, lifetimeEarnings: lifetimeEarnings.toString(), status: 'complete' as const }
2344
+ const totalWithdrawn = history.redeemed + history.withdrewExact + history.finalized
2345
+ const cashFlows = {
2346
+ totalDeposited: history.deposited.toString(),
2347
+ totalWithdrawn: totalWithdrawn.toString(),
2348
+ }
2349
+ if (history.pendingRedemption)
2350
+ return { ...base, ...cashFlows, status: 'pending_redemption' as const }
2351
+ const lifetimeEarnings = current.value + totalWithdrawn - history.deposited
2352
+ return {
2353
+ ...base,
2354
+ ...cashFlows,
2355
+ lifetimeEarnings: lifetimeEarnings.toString(),
2356
+ status: 'complete' as const,
2357
+ }
1311
2358
  }
1312
2359
 
1313
2360
  /** Reads cash-flow-adjusted earnings over a trailing indexed window. */
@@ -1329,6 +2376,7 @@ async function getWindowVaultEarnings(
1329
2376
  hasExternalShareTransfer(tidx, {
1330
2377
  account: options.account,
1331
2378
  blockNumber: options.blockNumber,
2379
+ rewardTransfer: options.rewardTransfer,
1332
2380
  shareToken: current.shareToken,
1333
2381
  startBlockNumber,
1334
2382
  vault: options.vault,
@@ -1393,6 +2441,8 @@ declare namespace getWindowVaultEarnings {
1393
2441
  chainId: z.output<typeof Schema.ChainId>
1394
2442
  /** Trailing earnings period. */
1395
2443
  period: '30d'
2444
+ /** SQL predicate recognizing paid rewards in the share-transfer history. */
2445
+ rewardTransfer: string
1396
2446
  /** Latest indexed block timestamp. */
1397
2447
  timestamp: string
1398
2448
  /** Earn vault contract address. */
@@ -1453,6 +2503,62 @@ async function getLatestIndexedBlock(tidx: Tidx.Client) {
1453
2503
  return { number, timestamp }
1454
2504
  }
1455
2505
 
2506
+ /** Resolves the latest indexed block at or before a historical position timestamp. */
2507
+ async function getIndexedBlockAtOrBefore(tidx: Tidx.Client, asOf: string) {
2508
+ const timestamp = new Date(asOf).toISOString().replace('T', ' ').replace(/Z$/, '')
2509
+ const result = await tidx.fetch({
2510
+ engine: 'clickhouse',
2511
+ // SAFETY: Zod restricts `asOf` to ISO 8601, so the formatted timestamp cannot alter SQL.
2512
+ query: `
2513
+ SELECT 'boundary' AS kind, num, timestamp
2514
+ FROM (
2515
+ SELECT num, timestamp
2516
+ FROM blocks
2517
+ WHERE timestamp <= '${timestamp}'
2518
+ ORDER BY timestamp DESC, num DESC
2519
+ LIMIT 1
2520
+ )
2521
+ UNION ALL
2522
+ SELECT 'head' AS kind, num, timestamp
2523
+ FROM (
2524
+ SELECT num, timestamp
2525
+ FROM blocks
2526
+ ORDER BY num DESC
2527
+ LIMIT 1
2528
+ )
2529
+ ` as string,
2530
+ })
2531
+ return parseIndexedBlockAtOrBefore({ asOf, rows: result.rows })
2532
+ }
2533
+
2534
+ /** Decodes the indexed head and historical position boundary from TIDX rows. */
2535
+ export function parseIndexedBlockAtOrBefore(options: parseIndexedBlockAtOrBefore.Options) {
2536
+ const head = options.rows.find((row) => row['kind'] === 'head')
2537
+ if (!head) throw new Error('TIDX returned no indexed head for a historical Earn position.')
2538
+ const headNumber = Value.toNumber(head['num'])
2539
+ const headAt = Value.toIsoDateTime(head['timestamp'])
2540
+ if (headNumber === undefined || headAt === undefined)
2541
+ throw new Error('TIDX returned a malformed indexed head for a historical Earn position.')
2542
+ const boundary = options.rows.find((row) => row['kind'] === 'boundary')
2543
+ if (!boundary) throw new IndexedBlockNotFoundError(options.asOf)
2544
+ const number = Value.toNumber(boundary['num'])
2545
+ const indexedAt = Value.toIsoDateTime(boundary['timestamp'])
2546
+ if (number === undefined || indexedAt === undefined)
2547
+ throw new Error('TIDX returned a malformed historical Earn position boundary.')
2548
+ if (Date.parse(headAt) < Date.parse(options.asOf)) throw new IndexedCoverageError(options.asOf)
2549
+ return { number, timestamp: indexedAt }
2550
+ }
2551
+
2552
+ export declare namespace parseIndexedBlockAtOrBefore {
2553
+ /** Inputs returned by the historical boundary query. */
2554
+ type Options = {
2555
+ /** Requested historical observation timestamp. */
2556
+ asOf: string
2557
+ /** Indexed head and optional boundary rows. */
2558
+ rows: readonly Record<string, unknown>[]
2559
+ }
2560
+ }
2561
+
1456
2562
  /** Resolves the last indexed block at or before a trailing-day boundary. */
1457
2563
  async function getWindowStartBlockNumber(
1458
2564
  tidx: Tidx.Client,
@@ -1596,6 +2702,7 @@ async function getVaultEarningsHistory(
1596
2702
  return {
1597
2703
  deposited: BigInt(deposited),
1598
2704
  finalized: BigInt(finalized),
2705
+ incompleteCashFlows: venueDeposits > 0,
1599
2706
  incompleteCostBasis: transferredCancellations > 0 || venueDeposits > 0,
1600
2707
  pendingRedemption: pendingRedemptions > 0,
1601
2708
  redeemed: BigInt(redeemed),
@@ -1717,6 +2824,39 @@ declare namespace getWindowShareState {
1717
2824
  }
1718
2825
  }
1719
2826
 
2827
+ /** Builds a share-transfer predicate for the vault's verified reward distributors. */
2828
+ async function getRewardTransferPredicate(
2829
+ c: Context<App.Environment>,
2830
+ options: Pick<getVaultEarnings.Options, 'chainId' | 'vault'>,
2831
+ ) {
2832
+ const db = Db.get(c.get('db'))
2833
+ const campaign = await RewardCampaigns.get(db, {
2834
+ chainId: options.chainId,
2835
+ vaultAddress: options.vault,
2836
+ })
2837
+ if (!campaign) return '0'
2838
+ const distributors = await RewardCampaignDistributors.list(db, {
2839
+ chainId: options.chainId,
2840
+ vaultAddress: options.vault,
2841
+ })
2842
+ if (distributors.length === 0) return '0'
2843
+ const addresses = distributors
2844
+ .map(({ distributorAddress }) => Schema.Address.parse(distributorAddress))
2845
+ .map((address) => `'${address}'`)
2846
+ .join(', ')
2847
+ const shareToken = Schema.Address.parse(campaign.earnShareAddress)
2848
+
2849
+ // Batch rewards have no per-recipient event; exclude treasury returns instead. Qualify the filter so TIDX does not apply it to other event CTEs.
2850
+ return `
2851
+ token = '${shareToken}'
2852
+ AND "from" IN (${addresses})
2853
+ AND tx_hash NOT IN (
2854
+ SELECT returned.tx_hash FROM UnspentReturned AS returned
2855
+ WHERE returned.address IN (${addresses})
2856
+ )
2857
+ `
2858
+ }
2859
+
1720
2860
  /** Returns whether an account sent or received Earn shares outside known vault flows. */
1721
2861
  async function hasExternalShareTransfer(
1722
2862
  tidx: Tidx.Client,
@@ -1731,6 +2871,7 @@ async function hasExternalShareTransfer(
1731
2871
  SELECT count()
1732
2872
  FROM token_transfers
1733
2873
  WHERE token = '${options.shareToken.toLowerCase()}'
2874
+ AND toUInt256(amount) > 0
1734
2875
  AND block_num > ${options.startBlockNumber}
1735
2876
  AND block_num <= ${options.blockNumber}
1736
2877
  AND (
@@ -1739,6 +2880,7 @@ async function hasExternalShareTransfer(
1739
2880
  "to" = '${options.account}'
1740
2881
  AND "from" != '${zeroAddress}'
1741
2882
  AND "from" != '${options.vault}'
2883
+ AND NOT (${options.rewardTransfer})
1742
2884
  )
1743
2885
  )
1744
2886
  ) + (
@@ -1747,6 +2889,7 @@ async function hasExternalShareTransfer(
1747
2889
  SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
1748
2890
  FROM token_transfers
1749
2891
  WHERE token = '${options.shareToken.toLowerCase()}'
2892
+ AND toUInt256(amount) > 0
1750
2893
  AND "from" = '${options.account}'
1751
2894
  AND "to" = '${options.vault}'
1752
2895
  AND block_num > ${options.startBlockNumber}
@@ -1787,6 +2930,7 @@ async function hasExternalShareTransfer(
1787
2930
  SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
1788
2931
  FROM token_transfers
1789
2932
  WHERE token = '${options.shareToken.toLowerCase()}'
2933
+ AND toUInt256(amount) > 0
1790
2934
  AND "from" = '${options.vault}'
1791
2935
  AND "to" = '${options.account}'
1792
2936
  AND block_num > ${options.startBlockNumber}
@@ -1810,6 +2954,7 @@ async function hasExternalShareTransfer(
1810
2954
  redeemCancelledSignature,
1811
2955
  redeemedSignature,
1812
2956
  redeemRequestedSignature,
2957
+ unspentReturnedSignature,
1813
2958
  withdrewExactSignature,
1814
2959
  ],
1815
2960
  })
@@ -1822,6 +2967,7 @@ declare namespace hasExternalShareTransfer {
1822
2967
  type Options = {
1823
2968
  account: z.output<typeof Schema.Address>
1824
2969
  blockNumber: number
2970
+ rewardTransfer: string
1825
2971
  shareToken: z.output<typeof Schema.Address>
1826
2972
  startBlockNumber: number
1827
2973
  vault: z.output<typeof Schema.Address>
@@ -1847,6 +2993,7 @@ export function calculateActiveCostBasis(options: calculateActiveCostBasis.Optio
1847
2993
  }
1848
2994
 
1849
2995
  for (const action of options.actions) {
2996
+ if (action.shares === 0n) continue
1850
2997
  if (action.kind === 'deposit') {
1851
2998
  if (costBasis !== undefined) costBasis += action.assets
1852
2999
  shares += action.shares
@@ -2009,7 +3156,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2009
3156
  AND block_num <= ${options.blockNumber}
2010
3157
  UNION ALL
2011
3158
  SELECT
2012
- 'unknown' AS kind,
3159
+ if(${options.rewardTransfer}, 'deposit', 'unknown') AS kind,
2013
3160
  block_num,
2014
3161
  log_idx,
2015
3162
  '' AS request_id,
@@ -2017,6 +3164,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2017
3164
  '0' AS assets
2018
3165
  FROM token_transfers
2019
3166
  WHERE token = '${options.shareToken.toLowerCase()}'
3167
+ AND toUInt256(amount) > 0
2020
3168
  AND "to" = '${options.account}'
2021
3169
  AND "from" != '${zeroAddress}'
2022
3170
  AND "from" != '${options.vault}'
@@ -2031,6 +3179,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2031
3179
  '0' AS assets
2032
3180
  FROM token_transfers
2033
3181
  WHERE token = '${options.shareToken.toLowerCase()}'
3182
+ AND toUInt256(amount) > 0
2034
3183
  AND "from" = '${options.account}'
2035
3184
  AND "to" != '${options.vault}'
2036
3185
  AND block_num <= ${options.blockNumber}
@@ -2042,6 +3191,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2042
3191
  redeemCancelledSignature,
2043
3192
  redeemRequestedSignature,
2044
3193
  redeemedSignature,
3194
+ unspentReturnedSignature,
2045
3195
  venueSharesDepositedSignature,
2046
3196
  withdrewExactSignature,
2047
3197
  ],
@@ -2091,6 +3241,7 @@ declare namespace getActiveCostBasis {
2091
3241
  type Options = {
2092
3242
  account: z.output<typeof Schema.Address>
2093
3243
  blockNumber: number
3244
+ rewardTransfer: string
2094
3245
  shareBalance: bigint
2095
3246
  shareToken: z.output<typeof Schema.Address>
2096
3247
  vault: z.output<typeof Schema.Address>
@@ -2194,7 +3345,7 @@ export async function listVaults(c: Context<App.Environment>, options: listVault
2194
3345
  apyByVault.has(vaultAddress.toLowerCase()) ? [] : [vaultAddress],
2195
3346
  )
2196
3347
  : []
2197
- const [missingApy, rates, tokens] = await Promise.all([
3348
+ const [missingApy, rates, rewards, tokens] = await Promise.all([
2198
3349
  missingApyVaults.length > 0
2199
3350
  ? resolvePageApy(c, {
2200
3351
  chainId: options.chainId,
@@ -2204,12 +3355,27 @@ export async function listVaults(c: Context<App.Environment>, options: listVault
2204
3355
  })
2205
3356
  : undefined,
2206
3357
  resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
3358
+ resolvePageRewards(c, {
3359
+ chainId: options.chainId,
3360
+ include,
3361
+ vaults: discoveries.map(({ vaultAddress }) => vaultAddress),
3362
+ }),
2207
3363
  resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
2208
3364
  ])
2209
3365
  for (const [vault, apy] of missingApy ?? []) apyByVault.set(vault, apy)
2210
3366
  const apy = include.includes('apy') ? apyByVault : undefined
2211
3367
  const data = await Promise.all(
2212
- page.map((match) => resolveVault(c, { ...match, apy, include, rates, snapshot, tokens })),
3368
+ page.map((match) =>
3369
+ resolveVault(c, {
3370
+ ...match,
3371
+ apy,
3372
+ include,
3373
+ rates,
3374
+ rewards: rewards?.get(match.discovery.vaultAddress),
3375
+ snapshot,
3376
+ tokens,
3377
+ }),
3378
+ ),
2213
3379
  )
2214
3380
  const last = matches[options.limit - 1]
2215
3381
  const nextPosition =
@@ -2291,60 +3457,429 @@ export async function listVerifiedVaults(
2291
3457
  }
2292
3458
  }
2293
3459
 
2294
- const page = matches.slice(0, options.limit)
2295
- const discoveries = page.map(({ discovery }) => discovery)
2296
- const [apy, rates, tokens] = await Promise.all([
2297
- resolvePageApy(c, {
2298
- chainId: options.chainId,
2299
- include,
2300
- vaults: discoveries.map(({ vaultAddress }) => vaultAddress),
2301
- window: options['apy.window'],
2302
- }),
2303
- resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
2304
- resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
2305
- ])
2306
- const data = await Promise.all(
2307
- page.map(async (match) =>
2308
- Response.validated(
2309
- schema.VerifiedVault,
2310
- await resolveVault(c, { ...match, apy, include, rates, snapshot, tokens }),
2311
- ),
2312
- ),
3460
+ const page = matches.slice(0, options.limit)
3461
+ const discoveries = page.map(({ discovery }) => discovery)
3462
+ const [apy, rates, rewards, tokens] = await Promise.all([
3463
+ resolvePageApy(c, {
3464
+ chainId: options.chainId,
3465
+ include,
3466
+ vaults: discoveries.map(({ vaultAddress }) => vaultAddress),
3467
+ window: options['apy.window'],
3468
+ }),
3469
+ resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
3470
+ resolvePageRewards(c, {
3471
+ chainId: options.chainId,
3472
+ include,
3473
+ vaults: discoveries.map(({ vaultAddress }) => vaultAddress),
3474
+ }),
3475
+ resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
3476
+ ])
3477
+ const data = await Promise.all(
3478
+ page.map(async (match) =>
3479
+ Response.validated(
3480
+ schema.VerifiedVault,
3481
+ await resolveVault(c, {
3482
+ ...match,
3483
+ apy,
3484
+ include,
3485
+ rates,
3486
+ rewards: rewards?.get(match.discovery.vaultAddress),
3487
+ snapshot,
3488
+ tokens,
3489
+ }),
3490
+ ),
3491
+ ),
3492
+ )
3493
+ const last = matches[options.limit - 1]
3494
+ return {
3495
+ data,
3496
+ ...(rates ? { meta: { valuation: Valuation.pricing(rates, options.oracle) } } : {}),
3497
+ nextCursor:
3498
+ matches.length > options.limit && last ? Cursor.encode([last.record.vaultAddress]) : null,
3499
+ }
3500
+ }
3501
+
3502
+ export declare namespace listVerifiedVaults {
3503
+ /** One verified vault and its current onchain state. */
3504
+ type Match = {
3505
+ discovery: EarnVaults.Discovery
3506
+ record: RegistryRecord
3507
+ zoneRoutes: readonly EarnVaults.ResolvedZoneRoute[]
3508
+ }
3509
+
3510
+ /** Options for listing verified Earn vaults. */
3511
+ type Options = z.output<typeof schema.getVerifiedEarnVaults.Query> & {
3512
+ chainId: z.output<typeof Schema.ChainId>
3513
+ /** FX rate oracle backing TVL valuation. */
3514
+ oracle: FxOracle.Oracle
3515
+ }
3516
+ }
3517
+
3518
+ /** Reads daily historical share prices for one Earn vault. */
3519
+ export async function getVaultSharePrices(
3520
+ c: Context<App.Environment>,
3521
+ options: getVaultSharePrices.Options,
3522
+ ) {
3523
+ const prices = await Timing.time(c, 'earn_share_prices', () =>
3524
+ EarnSharePrices.resolve({
3525
+ chainId: options.chainId,
3526
+ from: options.from,
3527
+ // This client is request-local so viem's stateful HTTP batch cannot cross Worker invocations.
3528
+ getClient: (chainId = options.chainId) =>
3529
+ Viem.createBatchClient({
3530
+ chainId,
3531
+ principal: Auth.getPrincipal(c),
3532
+ rpc: c.get('rpc'),
3533
+ zone: c.get('zones').get(chainId),
3534
+ }),
3535
+ getTidx: c.get('getTidx'),
3536
+ shareDecimals: options.discovery.shareToken.decimals,
3537
+ to: options.to,
3538
+ vault: options.vault,
3539
+ }),
3540
+ )
3541
+ return prices.map((price) => ({
3542
+ sharePrice: {
3543
+ amount: price.amount.toString(),
3544
+ currency: options.discovery.assetToken.currency,
3545
+ decimals: options.discovery.assetToken.decimals,
3546
+ formatted: core_Value.format(price.amount, options.discovery.assetToken.decimals),
3547
+ },
3548
+ timestamp: price.timestamp,
3549
+ }))
3550
+ }
3551
+
3552
+ /** Classifies an Earn share-price dependency failure for request metrics. */
3553
+ export function sharePriceProviderFailure(
3554
+ options: sharePriceProviderFailure.Options,
3555
+ ): Log.ProviderFailure | undefined {
3556
+ if (options.cause instanceof EarnSharePrices.MalformedResponseError)
3557
+ return {
3558
+ chainId: options.chainId,
3559
+ failure: 'payload',
3560
+ id: 'tidx',
3561
+ operation: 'query',
3562
+ }
3563
+ if (options.cause instanceof EarnSharePrices.TidxRequestError)
3564
+ return { ...Tidx.providerFailure(options.cause.cause), chainId: options.chainId }
3565
+ const failure = Viem.requestFailure(options.cause)
3566
+ if (failure) return { ...failure, chainId: options.chainId, id: 'rpc', operation: 'eth_call' }
3567
+ return undefined
3568
+ }
3569
+
3570
+ export declare namespace sharePriceProviderFailure {
3571
+ /** Failure and chain used to derive bounded dependency metadata. */
3572
+ type Options = {
3573
+ /** Chain whose dependency call failed. */
3574
+ chainId: number
3575
+ /** Error returned by TIDX parsing or the RPC client. */
3576
+ cause: unknown
3577
+ }
3578
+ }
3579
+
3580
+ export declare namespace getVaultSharePrices {
3581
+ /** Inputs selecting one Earn vault share-price series. */
3582
+ type Options = z.output<typeof schema.getEarnVaultSharePrices.Query> & {
3583
+ /** Chain containing the Earn vault. */
3584
+ chainId: z.output<typeof Schema.ChainId>
3585
+ /** Current compatible vault and token metadata. */
3586
+ discovery: EarnVaults.Discovery
3587
+ /** Earn vault contract address. */
3588
+ vault: z.output<typeof Schema.Address>
3589
+ }
3590
+ }
3591
+
3592
+ async function resolveSharePriceVaultDiscovery(
3593
+ c: Context<App.Environment>,
3594
+ options: resolveSharePriceVaultDiscovery.Options,
3595
+ ): Promise<EarnVaults.Discovery | undefined> {
3596
+ const result = await Store.memoize(
3597
+ async () => {
3598
+ try {
3599
+ return {
3600
+ discovery: await readVaultDiscovery(c, { ...options, include: [] }),
3601
+ status: 'found',
3602
+ } as const
3603
+ } catch (cause) {
3604
+ if (isIncompatible(cause)) return { status: 'not_found' } as const
3605
+ throw cause
3606
+ }
3607
+ },
3608
+ {
3609
+ key: `earn-share-price-vault:v1:${options.chainId}:${options.vaultAddress.toLowerCase()}`,
3610
+ store: c.get('store'),
3611
+ ttl: Ttl.seconds(5),
3612
+ },
3613
+ )
3614
+ return result.status === 'found' ? result.discovery : undefined
3615
+ }
3616
+
3617
+ declare namespace resolveSharePriceVaultDiscovery {
3618
+ type Options = {
3619
+ /** Chain containing the candidate Earn vault. */
3620
+ chainId: z.output<typeof Schema.ChainId>
3621
+ /** Candidate Earn vault contract address. */
3622
+ vaultAddress: z.output<typeof Schema.Address>
3623
+ }
3624
+ }
3625
+
3626
+ /** Reads one direct or Zone-routed account position in a parent-chain Earn vault. */
3627
+ export async function getVaultPosition(
3628
+ c: Context<App.Environment>,
3629
+ options: getVaultPosition.Options,
3630
+ ): Promise<getVaultPosition.ReturnType> {
3631
+ const zone = c.get('zones').get(options.chainId)
3632
+ if (!zone) {
3633
+ const block =
3634
+ options.asOf === undefined
3635
+ ? undefined
3636
+ : await getIndexedBlockAtOrBefore(c.get('getTidx')(options.chainId), options.asOf)
3637
+ const position = await c
3638
+ .get('getClient')(options.chainId)
3639
+ .earn.getPosition({
3640
+ account: options.account,
3641
+ ...(block ? { blockNumber: BigInt(block.number) } : {}),
3642
+ vault: options.vault,
3643
+ })
3644
+ return { ...position, block, valueToken: position.assetToken }
3645
+ }
3646
+
3647
+ const route = await resolveZonePositionRoute(c, {
3648
+ chainId: options.chainId,
3649
+ vault: options.vault,
3650
+ })
3651
+ const block =
3652
+ options.asOf === undefined
3653
+ ? undefined
3654
+ : await getIndexedBlockAtOrBefore(c.get('getTidx')(options.chainId), options.asOf)
3655
+ const zoneClient = c.get('getClient')(options.chainId)
3656
+ const blockNumber = block ? BigInt(block.number) : await zoneClient.getBlockNumber()
3657
+ const [assetBalance, shareBalance, importedParentBlockNumber] = await Promise.all([
3658
+ readZoneBalance(zoneClient, {
3659
+ account: options.account,
3660
+ blockNumber,
3661
+ token: route.privateAsset,
3662
+ }),
3663
+ readZoneBalance(zoneClient, {
3664
+ account: options.account,
3665
+ blockNumber,
3666
+ token: route.shareToken,
3667
+ }),
3668
+ readZoneSourceBlock(c, {
3669
+ blockNumber,
3670
+ historical: block !== undefined,
3671
+ portal: route.portal,
3672
+ sourceChainId: route.sourceChainId,
3673
+ zoneClient,
3674
+ }),
3675
+ ])
3676
+ const sourceBlock = Value.toNumber(importedParentBlockNumber)
3677
+ if (sourceBlock === undefined || sourceBlock < route.deploymentBlock)
3678
+ throw new EarnVaults.NotFoundError()
3679
+ const value = await c
3680
+ .get('getClient')(route.sourceChainId)
3681
+ .earn.getRedeemQuote({
3682
+ blockNumber: BigInt(sourceBlock),
3683
+ shareAmount: shareBalance,
3684
+ vault: options.vault,
3685
+ })
3686
+ return {
3687
+ assetAllowance: 0n,
3688
+ assetBalance,
3689
+ assetToken: route.privateAsset,
3690
+ block,
3691
+ shareAllowance: 0n,
3692
+ shareBalance,
3693
+ shareToken: route.shareToken,
3694
+ value,
3695
+ valueToken: route.vaultAsset,
3696
+ }
3697
+ }
3698
+
3699
+ export declare namespace getVaultPosition {
3700
+ /** Inputs selecting one direct or Zone-routed account position. */
3701
+ type Options = {
3702
+ account: z.output<typeof Schema.Address>
3703
+ asOf?: string | undefined
3704
+ chainId: z.output<typeof Schema.ChainId>
3705
+ vault: z.output<typeof Schema.Address>
3706
+ }
3707
+
3708
+ /** Normalized position state across a direct chain or private Zone. */
3709
+ type ReturnType = Actions.earn.getPosition.ReturnValue & {
3710
+ block?: { number: number; timestamp: string } | undefined
3711
+ valueToken: z.output<typeof Schema.Address>
3712
+ }
3713
+ }
3714
+
3715
+ /** Reads a private Zone token balance with the holder as the authorized RPC caller. */
3716
+ async function readZoneBalance(
3717
+ client: ReturnType<App.Environment['Variables']['getClient']>,
3718
+ options: readZoneBalance.Options,
3719
+ ) {
3720
+ return client.readContract({
3721
+ abi: tokenPositionAbi,
3722
+ account: options.account,
3723
+ address: options.token,
3724
+ args: [options.account],
3725
+ ...(options.blockNumber === undefined ? {} : { blockNumber: options.blockNumber }),
3726
+ functionName: 'balanceOf',
3727
+ })
3728
+ }
3729
+
3730
+ declare namespace readZoneBalance {
3731
+ /** Private balance selector and optional historical Zone block. */
3732
+ type Options = {
3733
+ account: z.output<typeof Schema.Address>
3734
+ blockNumber?: bigint | undefined
3735
+ token: z.output<typeof Schema.Address>
3736
+ }
3737
+ }
3738
+
3739
+ /** Reads the parent Tempo checkpoint imported by one Zone observation. */
3740
+ async function readZoneSourceBlock(
3741
+ c: Context<App.Environment>,
3742
+ options: readZoneSourceBlock.Options,
3743
+ ): Promise<bigint> {
3744
+ const verifiedPromise = c.get('getClient')(options.sourceChainId).readContract({
3745
+ abi: zonePortalAbi,
3746
+ address: options.portal,
3747
+ functionName: 'lastSyncedTempoBlockNumber',
3748
+ })
3749
+ // A historical Zone without TempoState rejects before awaiting the portal.
3750
+ void verifiedPromise.catch(() => undefined)
3751
+ const reported = await (async () => {
3752
+ try {
3753
+ return await options.zoneClient.readContract({
3754
+ abi: tempoStateAbi,
3755
+ address: tempoStateAddress,
3756
+ blockNumber: options.blockNumber,
3757
+ functionName: 'tempoBlockNumber',
3758
+ })
3759
+ } catch (cause) {
3760
+ if (
3761
+ !(cause instanceof ContractFunctionExecutionError) ||
3762
+ !(cause.cause instanceof ContractFunctionZeroDataError)
3763
+ )
3764
+ throw cause
3765
+ if (options.historical) throw new ZoneHistoricalAlignmentError()
3766
+ return undefined
3767
+ }
3768
+ })()
3769
+ const verified = await verifiedPromise
3770
+ return alignZoneSourceBlock({ reported, verified })
3771
+ }
3772
+
3773
+ declare namespace readZoneSourceBlock {
3774
+ /** Zone observation and parent Portal used to resolve one synchronization boundary. */
3775
+ type Options = {
3776
+ blockNumber: bigint
3777
+ historical: boolean
3778
+ portal: z.output<typeof Schema.Address>
3779
+ sourceChainId: number
3780
+ zoneClient: ReturnType<App.Environment['Variables']['getClient']>
3781
+ }
3782
+ }
3783
+
3784
+ /** Caps a Zone-reported parent checkpoint at the L1 portal's verified watermark. */
3785
+ export function alignZoneSourceBlock(options: alignZoneSourceBlock.Options): bigint {
3786
+ if (options.reported === undefined || options.verified < options.reported) return options.verified
3787
+ return options.reported
3788
+ }
3789
+
3790
+ export declare namespace alignZoneSourceBlock {
3791
+ /** Zone-reported and L1-verified parent checkpoints. */
3792
+ type Options = {
3793
+ /** Parent checkpoint reported by the Zone, when available. */
3794
+ reported?: bigint | undefined
3795
+ /** Latest parent checkpoint verified by the L1 portal. */
3796
+ verified: bigint
3797
+ }
3798
+ }
3799
+
3800
+ /** Resolves the verified parent vault and token pair for one Zone route. */
3801
+ async function resolveZonePositionRoute(
3802
+ c: Context<App.Environment>,
3803
+ options: resolveZonePositionRoute.Options,
3804
+ ): Promise<resolveZonePositionRoute.ReturnType> {
3805
+ const zone = c.get('zones').get(options.chainId)
3806
+ if (!zone?.sourceId) throw new EarnVaults.NotFoundError()
3807
+ const db = Db.get(c.get('dbCached'))
3808
+ const record = await core_EarnVaults.get(db, {
3809
+ chainId: zone.sourceId,
3810
+ vaultAddress: options.vault,
3811
+ })
3812
+ const configured = record?.zones.find((route) => route.chainId === options.chainId)
3813
+ if (!record || !configured) throw new EarnVaults.NotFoundError()
3814
+ const discovery = await resolveVaultDiscovery(c, {
3815
+ chainId: zone.sourceId,
3816
+ include: [],
3817
+ vaultAddress: options.vault,
3818
+ })
3819
+ const [route] = await EarnVaults.resolveZoneRoutes({
3820
+ discovery,
3821
+ getClient: c.get('getClient'),
3822
+ routes: [configured],
3823
+ zones: [...c.get('zones').values()],
3824
+ })
3825
+ const privateAsset = route?.inputTokens[0]
3826
+ const portalContract = zone.contracts?.['portal']
3827
+ const portal = Schema.Address.safeParse(
3828
+ portalContract && 'address' in portalContract
3829
+ ? portalContract.address
3830
+ : portalContract?.[zone.sourceId]?.address,
2313
3831
  )
2314
- const last = matches[options.limit - 1]
3832
+ if (!route || !privateAsset || !portal.success) throw new EarnVaults.NotFoundError()
2315
3833
  return {
2316
- data,
2317
- ...(rates ? { meta: { valuation: Valuation.pricing(rates, options.oracle) } } : {}),
2318
- nextCursor:
2319
- matches.length > options.limit && last ? Cursor.encode([last.record.vaultAddress]) : null,
3834
+ deploymentBlock: route.deploymentBlock,
3835
+ portal: portal.data,
3836
+ privateAsset,
3837
+ shareToken: discovery.shareToken.address,
3838
+ sourceChainId: zone.sourceId,
3839
+ vaultAsset: discovery.assetToken.address,
2320
3840
  }
2321
3841
  }
2322
3842
 
2323
- export declare namespace listVerifiedVaults {
2324
- /** One verified vault and its current onchain state. */
2325
- type Match = {
2326
- discovery: EarnVaults.Discovery
2327
- record: RegistryRecord
2328
- zoneRoutes: readonly EarnVaults.ResolvedZoneRoute[]
3843
+ declare namespace resolveZonePositionRoute {
3844
+ /** Zone and parent vault selecting one curated route. */
3845
+ type Options = {
3846
+ chainId: z.output<typeof Schema.ChainId>
3847
+ vault: z.output<typeof Schema.Address>
2329
3848
  }
2330
3849
 
2331
- /** Options for listing verified Earn vaults. */
2332
- type Options = z.output<typeof schema.getVerifiedEarnVaults.Query> & {
2333
- chainId: z.output<typeof Schema.ChainId>
2334
- /** FX rate oracle backing TVL valuation. */
2335
- oracle: FxOracle.Oracle
3850
+ /** Verified addresses needed to read and value a Zone position. */
3851
+ type ReturnType = {
3852
+ deploymentBlock: number
3853
+ privateAsset: z.output<typeof Schema.Address>
3854
+ portal: z.output<typeof Schema.Address>
3855
+ shareToken: z.output<typeof Schema.Address>
3856
+ sourceChainId: number
3857
+ vaultAsset: z.output<typeof Schema.Address>
2336
3858
  }
2337
3859
  }
2338
3860
 
2339
3861
  /** Lists every vault where an account holds earn shares, ordered by vault address. */
2340
3862
  export async function listPositions(c: Context<App.Environment>, options: listPositions.Options) {
2341
3863
  const denomination = options['valuation.currency']
3864
+ const zone = c.get('zones').get(options.chainId)
3865
+ if (zone && options.include.includes('earnings')) throw new ZoneEarningsUnsupportedError()
3866
+ const sourceChainId = zone ? zone.sourceId : options.chainId
3867
+ if (sourceChainId === undefined) throw new EarnVaults.NotFoundError()
2342
3868
  const db = Db.get(c.get('dbCached'))
2343
- const recordsPromise = core_EarnVaults.list(db, { chainId: options.chainId })
3869
+ const recordsPromise = core_EarnVaults
3870
+ .list(db, { chainId: sourceChainId })
3871
+ .then((records) =>
3872
+ zone
3873
+ ? records.filter((record) =>
3874
+ record.zones.some((route) => route.chainId === options.chainId),
3875
+ )
3876
+ : records,
3877
+ )
2344
3878
  // The verified page needs no index: the registry is the candidate set.
2345
- const heldPromise = options.verified
2346
- ? undefined
2347
- : queryHeldVaults(c, { address: options.address, chainId: options.chainId })
3879
+ const heldPromise =
3880
+ options.verified || zone
3881
+ ? undefined
3882
+ : queryHeldVaults(c, { address: options.address, chainId: options.chainId })
2348
3883
  // Observing these branches prevents unhandled rejections while the
2349
3884
  // denomination validates; the later await still propagates them.
2350
3885
  void recordsPromise.catch(() => undefined)
@@ -2383,13 +3918,14 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2383
3918
  batch.map(async (vault) => {
2384
3919
  const record = registry.get(vault)
2385
3920
  try {
2386
- const position = await readPosition(c, {
2387
- address: options.address,
3921
+ const position = await getVaultPosition(c, {
3922
+ account: options.address,
2388
3923
  chainId: options.chainId,
2389
- vaultAddress: Schema.Address.parse(vault),
3924
+ vault: Schema.Address.parse(vault),
2390
3925
  })
2391
3926
  return { position, record, vaultAddress: vault }
2392
3927
  } catch (cause) {
3928
+ if (cause instanceof EarnVaults.NotFoundError) return undefined
2393
3929
  if (!record && isIncompatible(cause)) return undefined
2394
3930
  throw cause
2395
3931
  }
@@ -2403,7 +3939,18 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2403
3939
  }
2404
3940
 
2405
3941
  const page = matches.slice(0, options.limit)
2406
- const [tokens, snapshot] = await Promise.all([
3942
+ const indexedBlock =
3943
+ !zone && page.length > 0 && options.include.includes('earnings')
3944
+ ? await getLatestIndexedBlock(c.get('getTidx')(options.chainId))
3945
+ : undefined
3946
+ const [cashFlows, tokens, valueTokens, snapshot] = await Promise.all([
3947
+ indexedBlock
3948
+ ? getVaultLifetimeCashFlows(c.get('getTidx')(options.chainId), {
3949
+ account: options.address,
3950
+ blockNumber: indexedBlock.number,
3951
+ vaults: page.map(({ vaultAddress }) => Schema.Address.parse(vaultAddress)),
3952
+ })
3953
+ : undefined,
2407
3954
  (async () => {
2408
3955
  const addresses = [
2409
3956
  ...new Set(page.flatMap(({ position }) => [position.assetToken, position.shareToken])),
@@ -2419,13 +3966,20 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2419
3966
  )
2420
3967
  return new Map(groups.flatMap((group) => [...group]))
2421
3968
  })(),
2422
- denomination ? VerifiedTokens.snapshot(c, options.chainId) : undefined,
3969
+ zone
3970
+ ? Tokens.resolveTokens(c, {
3971
+ addresses: [...new Set(page.map(({ position }) => position.valueToken))],
3972
+ chainId: sourceChainId,
3973
+ include: ['token.verified'],
3974
+ })
3975
+ : undefined,
3976
+ denomination ? VerifiedTokens.snapshot(c, sourceChainId) : undefined,
2423
3977
  ])
2424
3978
  const rates =
2425
3979
  denomination && snapshot
2426
3980
  ? await Valuation.ratesFor(c, {
2427
3981
  currencies: page.flatMap(({ position }) => {
2428
- const currency = snapshot.byAddress.get(position.assetToken.toLowerCase())?.currency
3982
+ const currency = snapshot.byAddress.get(position.valueToken.toLowerCase())?.currency
2429
3983
  return currency === undefined ? [] : [currency]
2430
3984
  }),
2431
3985
  denomination,
@@ -2444,11 +3998,14 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2444
3998
  return {
2445
3999
  data: page.map((match) =>
2446
4000
  serializePosition({
4001
+ cashFlows: cashFlows?.get(match.vaultAddress),
4002
+ chainId: options.chainId,
2447
4003
  denomination,
2448
4004
  position: match.position,
2449
4005
  rates,
2450
4006
  snapshot,
2451
4007
  tokens,
4008
+ valueTokens,
2452
4009
  vaultAddress: match.vaultAddress,
2453
4010
  verified: match.record !== undefined,
2454
4011
  }),
@@ -2461,7 +4018,7 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2461
4018
  export declare namespace listPositions {
2462
4019
  /** One candidate vault holding a non-zero position. */
2463
4020
  type Match = {
2464
- position: Actions.earn.getPosition.ReturnValue
4021
+ position: getVaultPosition.ReturnType
2465
4022
  record?: RegistryRecord | undefined
2466
4023
  vaultAddress: string
2467
4024
  }
@@ -2481,12 +4038,11 @@ async function queryHeldVaults(
2481
4038
  c: Context<App.Environment>,
2482
4039
  options: queryHeldVaults.Options,
2483
4040
  ): Promise<readonly string[]> {
4041
+ const factories = deploymentFactories[options.chainId] ?? []
4042
+ if (factories.length === 0) return []
2484
4043
  const tidx = c.get('getTidx')(options.chainId)
2485
- // Each vault's newest deployment binds its current share token; joining it
2486
- // against the account's holder deltas server-side turns holdings into vault
2487
- // candidates in one round trip, with no literal list to overflow the request.
2488
- // Earn shares are factory-minted TIP-20s, so the `0x20c0` prefix bounds the
2489
- // subquery. Stale delta rows are fine: the live read drops them.
4044
+ // Filter emitters before ranking so forged events cannot replace share bindings.
4045
+ // TIP-20 prefixes bound holder scans; live reads discard exited positions.
2490
4046
  const result = await Timing.time(c, 'earn_positions_discovery', () =>
2491
4047
  tidx.fetch({
2492
4048
  chainId: options.chainId,
@@ -2502,6 +4058,7 @@ async function queryHeldVaults(
2502
4058
  ORDER BY block_num DESC, log_idx DESC
2503
4059
  ) AS deployment_rank
2504
4060
  FROM EarnStackDeployed
4061
+ WHERE address IN (${factories.map((address) => `'${address}'`).join(', ')})
2505
4062
  )
2506
4063
  WHERE deployment_rank = 1 AND lower("earnShare") IN (
2507
4064
  SELECT DISTINCT lower(token)
@@ -2528,35 +4085,19 @@ declare namespace queryHeldVaults {
2528
4085
  }
2529
4086
  }
2530
4087
 
2531
- /** Reads one account's live position in one vault. */
2532
- function readPosition(
2533
- c: Context<App.Environment>,
2534
- options: readPosition.Options,
2535
- ): Promise<Actions.earn.getPosition.ReturnValue> {
2536
- return c
2537
- .get('getClient')(options.chainId)
2538
- .earn.getPosition({ account: options.address, vault: options.vaultAddress })
2539
- }
2540
-
2541
- declare namespace readPosition {
2542
- /** Inputs selecting one vault position. */
2543
- type Options = {
2544
- address: z.output<typeof Schema.Address>
2545
- chainId: z.output<typeof Schema.ChainId>
2546
- vaultAddress: z.output<typeof Schema.Address>
2547
- }
2548
- }
2549
-
2550
4088
  /** Serializes one live position into the address-scoped row contract. */
2551
4089
  function serializePosition(options: serializePosition.Options) {
2552
4090
  const { position, tokens } = options
2553
4091
  const assetToken = getToken(tokens, position.assetToken)
2554
4092
  const shareToken = getToken(tokens, position.shareToken)
4093
+ const valueToken = getToken(options.valueTokens ?? tokens, position.valueToken)
2555
4094
  const vaultAddress = Schema.Address.parse(options.vaultAddress.toLowerCase())
2556
4095
  return {
2557
- assetAmount: valueFor(position.value, assetToken),
4096
+ assetAmount: valueFor(position.value, valueToken),
2558
4097
  assetToken,
4098
+ chainId: options.chainId,
2559
4099
  id: vaultAddress,
4100
+ ...(options.cashFlows ? { lifetimeCashFlows: options.cashFlows } : {}),
2560
4101
  shareAmount: valueFor(position.shareBalance, shareToken),
2561
4102
  shareToken,
2562
4103
  ...(options.denomination
@@ -2565,10 +4106,11 @@ function serializePosition(options: serializePosition.Options) {
2565
4106
  amount: position.value,
2566
4107
  denomination: options.denomination,
2567
4108
  rates: options.rates,
2568
- token: options.snapshot?.byAddress.get(assetToken.address),
4109
+ token: options.snapshot?.byAddress.get(valueToken.address),
2569
4110
  }),
2570
4111
  }
2571
4112
  : {}),
4113
+ valueToken,
2572
4114
  vaultAddress,
2573
4115
  verified: options.verified,
2574
4116
  }
@@ -2577,19 +4119,174 @@ function serializePosition(options: serializePosition.Options) {
2577
4119
  declare namespace serializePosition {
2578
4120
  /** Values used to serialize one account position row. */
2579
4121
  type Options = {
4122
+ /** Indexed lifetime cash flows, when requested. */
4123
+ cashFlows?: z.output<
4124
+ typeof schema.getEarnAddressPositions.Response
4125
+ >['data'][number]['lifetimeCashFlows']
4126
+ chainId: z.output<typeof Schema.ChainId>
2580
4127
  /** Currency the row valuation is denominated in (uppercase). */
2581
4128
  denomination?: string | undefined
2582
- position: Actions.earn.getPosition.ReturnValue
4129
+ position: getVaultPosition.ReturnType
2583
4130
  /** Consulted FX rate set, or undefined when unavailable or unnecessary. */
2584
4131
  rates: Awaited<ReturnType<typeof Valuation.ratesFor>>
2585
4132
  /** Curated token snapshot backing valuation, present when requested. */
2586
4133
  snapshot?: VerifiedTokens.Snapshot | undefined
2587
4134
  tokens: Tokens.resolveTokens.ReturnType
4135
+ valueTokens?: Tokens.resolveTokens.ReturnType | undefined
2588
4136
  vaultAddress: string
2589
4137
  verified: boolean
2590
4138
  }
2591
4139
  }
2592
4140
 
4141
+ /** Reads complete protocol cash flows for one account and a set of vaults. */
4142
+ export async function getVaultLifetimeCashFlows(
4143
+ tidx: Tidx.Client,
4144
+ options: getVaultLifetimeCashFlows.Options,
4145
+ ) {
4146
+ const vaults = [...new Set(options.vaults.map((vault) => vault.toLowerCase()))]
4147
+ const cashFlows = new Map<string, getVaultLifetimeCashFlows.CashFlows>()
4148
+ if (vaults.length === 0) return cashFlows
4149
+ const addresses = vaults.map((vault) => `'${vault}'`).join(', ')
4150
+ const seeds = vaults
4151
+ .map(
4152
+ (vault) => `
4153
+ SELECT '${vault}' AS vault,
4154
+ toUInt256(0) AS deposited_assets,
4155
+ toUInt256(0) AS finalized_assets,
4156
+ toUInt256(0) AS redeemed_assets,
4157
+ toUInt64(0) AS venue_deposits,
4158
+ toUInt256(0) AS withdrew_exact`,
4159
+ )
4160
+ .join('\n UNION ALL')
4161
+ const result = await tidx.fetch({
4162
+ engine: 'clickhouse',
4163
+ query: `
4164
+ SELECT vault,
4165
+ toString(sum(deposited_assets)) AS deposited_assets,
4166
+ toString(sum(finalized_assets)) AS finalized_assets,
4167
+ toString(sum(redeemed_assets)) AS redeemed_assets,
4168
+ sum(venue_deposits) AS venue_deposits,
4169
+ toString(sum(withdrew_exact)) AS withdrew_exact
4170
+ FROM (
4171
+ ${seeds}
4172
+ UNION ALL
4173
+ SELECT address AS vault, sum(toUInt256(assets)) AS deposited_assets,
4174
+ toUInt256(0) AS finalized_assets, toUInt256(0) AS redeemed_assets,
4175
+ toUInt64(0) AS venue_deposits, toUInt256(0) AS withdrew_exact
4176
+ FROM Deposited
4177
+ WHERE address IN (${addresses})
4178
+ AND receiver = '${options.account}'
4179
+ AND block_num > ${beforeFirstBlockNumber}
4180
+ AND block_num <= ${options.blockNumber}
4181
+ GROUP BY address
4182
+ UNION ALL
4183
+ SELECT finalized.address AS vault, toUInt256(0) AS deposited_assets,
4184
+ sum(toUInt256(finalized.assets)) AS finalized_assets,
4185
+ toUInt256(0) AS redeemed_assets, toUInt64(0) AS venue_deposits,
4186
+ toUInt256(0) AS withdrew_exact
4187
+ FROM RedeemFinalized AS finalized
4188
+ INNER JOIN RedeemRequested AS requested
4189
+ ON requested.address = finalized.address
4190
+ AND requested."requestId" = finalized."requestId"
4191
+ WHERE finalized.address IN (${addresses})
4192
+ AND requested.requester = '${options.account}'
4193
+ AND finalized.block_num > ${beforeFirstBlockNumber}
4194
+ AND finalized.block_num <= ${options.blockNumber}
4195
+ AND requested.block_num <= ${options.blockNumber}
4196
+ GROUP BY finalized.address
4197
+ UNION ALL
4198
+ SELECT address AS vault, toUInt256(0) AS deposited_assets,
4199
+ toUInt256(0) AS finalized_assets, sum(toUInt256(assets)) AS redeemed_assets,
4200
+ toUInt64(0) AS venue_deposits,
4201
+ toUInt256(0) AS withdrew_exact
4202
+ FROM Redeemed
4203
+ WHERE address IN (${addresses})
4204
+ AND caller = '${options.account}'
4205
+ AND block_num > ${beforeFirstBlockNumber}
4206
+ AND block_num <= ${options.blockNumber}
4207
+ GROUP BY address
4208
+ UNION ALL
4209
+ SELECT address AS vault, toUInt256(0) AS deposited_assets,
4210
+ toUInt256(0) AS finalized_assets, toUInt256(0) AS redeemed_assets,
4211
+ count() AS venue_deposits, toUInt256(0) AS withdrew_exact
4212
+ FROM VenueSharesDeposited
4213
+ WHERE address IN (${addresses})
4214
+ AND receiver = '${options.account}'
4215
+ AND block_num > ${beforeFirstBlockNumber}
4216
+ AND block_num <= ${options.blockNumber}
4217
+ GROUP BY address
4218
+ UNION ALL
4219
+ SELECT address AS vault, toUInt256(0) AS deposited_assets,
4220
+ toUInt256(0) AS finalized_assets, toUInt256(0) AS redeemed_assets,
4221
+ toUInt64(0) AS venue_deposits, sum(toUInt256(assets)) AS withdrew_exact
4222
+ FROM WithdrewExact
4223
+ WHERE address IN (${addresses})
4224
+ AND caller = '${options.account}'
4225
+ AND block_num > ${beforeFirstBlockNumber}
4226
+ AND block_num <= ${options.blockNumber}
4227
+ GROUP BY address
4228
+ ) AS histories
4229
+ GROUP BY vault
4230
+ ` as string,
4231
+ signatures: [
4232
+ depositedSignature,
4233
+ redeemFinalizedSignature,
4234
+ redeemRequestedSignature,
4235
+ redeemedSignature,
4236
+ venueSharesDepositedSignature,
4237
+ withdrewExactSignature,
4238
+ ],
4239
+ })
4240
+ for (const row of result.rows) {
4241
+ const vault = Schema.Address.safeParse(Value.toText(row['vault']))
4242
+ const deposited = Value.toIntegerString(row['deposited_assets'])
4243
+ const finalized = Value.toIntegerString(row['finalized_assets'])
4244
+ const redeemed = Value.toIntegerString(row['redeemed_assets'])
4245
+ const venueDeposits = Value.toNumber(row['venue_deposits'])
4246
+ const withdrewExact = Value.toIntegerString(row['withdrew_exact'])
4247
+ if (
4248
+ !vault.success ||
4249
+ deposited === undefined ||
4250
+ finalized === undefined ||
4251
+ redeemed === undefined ||
4252
+ venueDeposits === undefined ||
4253
+ withdrewExact === undefined
4254
+ )
4255
+ throw new Error('TIDX returned invalid Earn lifetime cash flows.')
4256
+ cashFlows.set(
4257
+ vault.data.toLowerCase(),
4258
+ venueDeposits > 0
4259
+ ? { status: 'incomplete_history' }
4260
+ : {
4261
+ status: 'complete',
4262
+ totalDeposited: deposited,
4263
+ totalWithdrawn: (
4264
+ BigInt(finalized) +
4265
+ BigInt(redeemed) +
4266
+ BigInt(withdrewExact)
4267
+ ).toString(),
4268
+ },
4269
+ )
4270
+ }
4271
+ if (cashFlows.size !== vaults.length)
4272
+ throw new Error('TIDX returned incomplete Earn lifetime cash flows.')
4273
+ return cashFlows
4274
+ }
4275
+
4276
+ export declare namespace getVaultLifetimeCashFlows {
4277
+ /** Lifetime cash-flow state for one vault. */
4278
+ type CashFlows = NonNullable<
4279
+ z.output<typeof schema.getEarnAddressPositions.Response>['data'][number]['lifetimeCashFlows']
4280
+ >
4281
+
4282
+ /** Indexed boundary and account whose protocol cash flows are read. */
4283
+ type Options = {
4284
+ account: z.output<typeof Schema.Address>
4285
+ blockNumber: number
4286
+ vaults: readonly z.output<typeof Schema.Address>[]
4287
+ }
4288
+ }
4289
+
2593
4290
  /** Renders a base-unit amount in the token's display currency and precision. */
2594
4291
  function valueFor(amount: bigint, token: z.output<typeof Tokens.schema.Token>) {
2595
4292
  return {
@@ -2624,7 +4321,7 @@ export async function getVault(c: Context<App.Environment>, options: getVault.Op
2624
4321
  record,
2625
4322
  })
2626
4323
  const discoveries = [discovery]
2627
- const [apy, rates, tokens] = await Promise.all([
4324
+ const [apy, rates, rewards, tokens] = await Promise.all([
2628
4325
  resolvePageApy(c, {
2629
4326
  chainId: options.chainId,
2630
4327
  include,
@@ -2632,6 +4329,11 @@ export async function getVault(c: Context<App.Environment>, options: getVault.Op
2632
4329
  window: options['apy.window'],
2633
4330
  }),
2634
4331
  resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
4332
+ resolveVaultRewards(c, {
4333
+ chainId: options.chainId,
4334
+ include,
4335
+ vault: discovery.vaultAddress,
4336
+ }),
2635
4337
  resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
2636
4338
  ])
2637
4339
  return {
@@ -2641,6 +4343,7 @@ export async function getVault(c: Context<App.Environment>, options: getVault.Op
2641
4343
  include,
2642
4344
  rates,
2643
4345
  record,
4346
+ rewards,
2644
4347
  snapshot,
2645
4348
  tokens,
2646
4349
  zoneRoutes,
@@ -2718,29 +4421,36 @@ async function resolveVaultDiscovery(
2718
4421
  c: Context<App.Environment>,
2719
4422
  options: resolveVaultDiscovery.Options,
2720
4423
  ) {
2721
- return Store.memoize(
2722
- () =>
2723
- Timing.time(c, 'earn_vault_discovery', () =>
2724
- EarnVaults.resolve({
2725
- getClient: c.get('getClient'),
2726
- include: options.include,
2727
- input: {
2728
- chainId: options.chainId,
2729
- vaultAddress: options.vaultAddress,
2730
- },
2731
- zones: [...c.get('zones').values()],
2732
- }),
2733
- ),
2734
- {
2735
- key: `earn-vault:v3:${options.chainId}:${options.vaultAddress.toLowerCase()}:include:${options.include.join(',') || 'base'}`,
2736
- store: c.get('store'),
2737
- // Matches the routes' `state` policy `max-age` so the two cache layers do
2738
- // not compound staleness on live vault state.
2739
- ttl: Ttl.seconds(30),
2740
- },
4424
+ return Store.memoize(() => readVaultDiscovery(c, options), {
4425
+ key: `earn-vault:v3:${options.chainId}:${options.vaultAddress.toLowerCase()}:include:${options.include.join(',') || 'base'}`,
4426
+ store: c.get('store'),
4427
+ // Matches the routes' `state` policy `max-age` so the two cache layers do
4428
+ // not compound staleness on live vault state.
4429
+ ttl: Ttl.seconds(30),
4430
+ })
4431
+ }
4432
+
4433
+ async function readVaultDiscovery(
4434
+ c: Context<App.Environment>,
4435
+ options: readVaultDiscovery.Options,
4436
+ ) {
4437
+ return Timing.time(c, 'earn_vault_discovery', () =>
4438
+ EarnVaults.resolve({
4439
+ getClient: c.get('getClient'),
4440
+ include: options.include,
4441
+ input: {
4442
+ chainId: options.chainId,
4443
+ vaultAddress: options.vaultAddress,
4444
+ },
4445
+ zones: [...c.get('zones').values()],
4446
+ }),
2741
4447
  )
2742
4448
  }
2743
4449
 
4450
+ declare namespace readVaultDiscovery {
4451
+ type Options = resolveVaultDiscovery.Options
4452
+ }
4453
+
2744
4454
  declare namespace resolveVaultDiscovery {
2745
4455
  type Options = {
2746
4456
  chainId: z.output<typeof Schema.ChainId>
@@ -2803,6 +4513,129 @@ declare namespace resolveVaultRoutes {
2803
4513
  }
2804
4514
  }
2805
4515
 
4516
+ async function resolvePositionApy(
4517
+ c: Context<App.Environment>,
4518
+ options: resolvePositionApy.Options,
4519
+ ): Promise<RewardRead.PositionApy | null> {
4520
+ const sourceChainId = c.get('zones').get(options.chainId)?.sourceId ?? options.chainId
4521
+ const recordFailure = (failure: Log.ProviderFailure) => {
4522
+ Cache.setPolicy(c, Cache.policies.noStore)
4523
+ c.set('providerFailures', [...(c.get('providerFailures') ?? []), failure])
4524
+ }
4525
+ const rateFailures: Log.ProviderFailure[] = []
4526
+ const [rates, discovery] = await Promise.all([
4527
+ Timing.time(c, 'earn_apy', () =>
4528
+ EarnRates.resolve({
4529
+ chainId: sourceChainId,
4530
+ getClient: c.get('getClient'),
4531
+ getTidx: c.get('getTidx'),
4532
+ onFailure: (failure) => {
4533
+ rateFailures.push({ ...failure, chainId: sourceChainId })
4534
+ },
4535
+ store: c.get('store'),
4536
+ vaults: [options.vault],
4537
+ }),
4538
+ ).catch(() => {
4539
+ rateFailures.push({
4540
+ chainId: sourceChainId,
4541
+ failure: 'unknown',
4542
+ id: 'earn-apy',
4543
+ operation: 'resolve',
4544
+ })
4545
+ return undefined
4546
+ }),
4547
+ resolveVaultDiscovery(c, {
4548
+ chainId: sourceChainId,
4549
+ include: [],
4550
+ vaultAddress: options.vault,
4551
+ }).catch((cause) => {
4552
+ const failure = Viem.requestFailure(cause)
4553
+ recordFailure(
4554
+ failure
4555
+ ? { ...failure, chainId: sourceChainId, id: 'rpc', operation: 'eth_call' }
4556
+ : {
4557
+ chainId: sourceChainId,
4558
+ failure: 'unknown',
4559
+ id: 'earn-vaults',
4560
+ operation: 'getVault',
4561
+ },
4562
+ )
4563
+ return undefined
4564
+ }),
4565
+ ])
4566
+ const base = rates?.get(options.vault.toLowerCase())
4567
+ const rateFailure = rateFailures[0]
4568
+ if (rateFailure) recordFailure(rateFailure)
4569
+ if (!base || !discovery) return null
4570
+ return await Timing.time(c, 'earn_rewards_apy', async () => {
4571
+ const [earnFees, record] = await Promise.all([
4572
+ c.get('getClient')(sourceChainId).readContract({
4573
+ abi: Abis.earnVault,
4574
+ address: options.vault,
4575
+ functionName: 'earnFees',
4576
+ }),
4577
+ core_EarnVaults.get(Db.get(c.get('db')), {
4578
+ chainId: options.chainId,
4579
+ vaultAddress: options.vault,
4580
+ }),
4581
+ ])
4582
+ // Accounting retains qualified shares for excluded infrastructure, so apply its current exclusions here too.
4583
+ const zoneContracts = [...c.get('zones').values()]
4584
+ .filter((zone) => zone.sourceId === options.chainId)
4585
+ .flatMap((zone) => positionApyContractAddresses(zone.contracts))
4586
+ return RewardRead.positionApy({
4587
+ assetDecimals: discovery.assetToken.decimals,
4588
+ baseNet: base.net,
4589
+ chainId: options.chainId,
4590
+ db: c.get('db'),
4591
+ excludedAddresses: [
4592
+ discovery.engine.address,
4593
+ earnFees,
4594
+ ...(record?.zones.map((zone) => zone.earnRouter) ?? []),
4595
+ ...zoneContracts,
4596
+ ],
4597
+ recipient: options.recipient,
4598
+ shareBalance: options.shareBalance.toString(),
4599
+ value: options.value.toString(),
4600
+ vaultAddress: options.vault,
4601
+ })
4602
+ }).catch((cause) => {
4603
+ recordFailure(
4604
+ sharePriceProviderFailure({ cause, chainId: sourceChainId }) ?? {
4605
+ chainId: options.chainId,
4606
+ failure: 'unknown',
4607
+ id: 'earn-rewards',
4608
+ operation: 'getPositionApy',
4609
+ },
4610
+ )
4611
+ return null
4612
+ })
4613
+ }
4614
+
4615
+ declare namespace resolvePositionApy {
4616
+ /** Current wallet position used to resolve its effective APY. */
4617
+ type Options = {
4618
+ /** Chain containing the vault position. */
4619
+ chainId: z.output<typeof Schema.ChainId>
4620
+ /** Wallet receiving the position yield. */
4621
+ recipient: z.output<typeof Schema.Address>
4622
+ /** Current EarnShare balance. */
4623
+ shareBalance: bigint
4624
+ /** Current asset value of the position. */
4625
+ value: bigint
4626
+ /** EarnVault containing the position. */
4627
+ vault: z.output<typeof Schema.Address>
4628
+ }
4629
+ }
4630
+
4631
+ function positionApyContractAddresses(contract: unknown): string[] {
4632
+ if (!contract || typeof contract !== 'object') return []
4633
+ const value = contract as Record<string, unknown>
4634
+ const address = value['address']
4635
+ if (typeof address === 'string' && isAddress(address)) return [address]
4636
+ return Object.values(value).flatMap(positionApyContractAddresses)
4637
+ }
4638
+
2806
4639
  // Measuring a rate costs indexer boundary queries plus historical quotes against
2807
4640
  // an archive node, so an unrequested rate reaches neither.
2808
4641
  function resolvePageApy(c: Context<App.Environment>, options: resolvePageApy.Options) {
@@ -2819,6 +4652,64 @@ function resolvePageApy(c: Context<App.Environment>, options: resolvePageApy.Opt
2819
4652
  )
2820
4653
  }
2821
4654
 
4655
+ function resolvePageRewards(c: Context<App.Environment>, options: resolvePageRewards.Options) {
4656
+ if (!options.include.includes('apy')) return undefined
4657
+ return Timing.time(c, 'earn_rewards_apy', () =>
4658
+ RewardRead.resolveCollection({
4659
+ chainId: options.chainId,
4660
+ db: c.get('dbCached'),
4661
+ vaults: options.vaults,
4662
+ }).catch(() => {
4663
+ Cache.setPolicy(c, Cache.policies.noStore)
4664
+ const failure = {
4665
+ chainId: options.chainId,
4666
+ failure: 'unknown',
4667
+ id: 'earn-rewards',
4668
+ operation: 'listVaultRewards',
4669
+ } satisfies Log.ProviderFailure
4670
+ c.set('providerFailures', [...(c.get('providerFailures') ?? []), failure])
4671
+ return new Map<string, readonly RewardRead.Reward[]>()
4672
+ }),
4673
+ )
4674
+ }
4675
+
4676
+ function resolveVaultRewards(c: Context<App.Environment>, options: resolveVaultRewards.Options) {
4677
+ if (!options.include.includes('apy')) return undefined
4678
+ return Timing.time(c, 'earn_rewards_apy', () =>
4679
+ RewardRead.get({
4680
+ chainId: options.chainId,
4681
+ db: c.get('dbCached'),
4682
+ vaultAddress: options.vault,
4683
+ }).catch(() => {
4684
+ Cache.setPolicy(c, Cache.policies.noStore)
4685
+ const failure = {
4686
+ chainId: options.chainId,
4687
+ failure: 'unknown',
4688
+ id: 'earn-rewards',
4689
+ operation: 'getVaultRewards',
4690
+ } satisfies Log.ProviderFailure
4691
+ c.set('providerFailures', [...(c.get('providerFailures') ?? []), failure])
4692
+ return undefined
4693
+ }),
4694
+ )
4695
+ }
4696
+
4697
+ declare namespace resolveVaultRewards {
4698
+ type Options = {
4699
+ chainId: z.output<typeof Schema.ChainId>
4700
+ include: z.output<typeof schema.getEarnVault.Query>['include']
4701
+ vault: z.output<typeof Schema.Address>
4702
+ }
4703
+ }
4704
+
4705
+ declare namespace resolvePageRewards {
4706
+ type Options = {
4707
+ chainId: z.output<typeof Schema.ChainId>
4708
+ include: z.output<typeof schema.getEarnVault.Query>['include']
4709
+ vaults: readonly z.output<typeof Schema.Address>[]
4710
+ }
4711
+ }
4712
+
2822
4713
  declare namespace resolvePageApy {
2823
4714
  type Options = {
2824
4715
  chainId: z.output<typeof Schema.ChainId>
@@ -2898,12 +4789,30 @@ async function resolveVault(c: Context<App.Environment>, options: resolveVault.O
2898
4789
  token: options.snapshot.byAddress.get(discovery.assetToken.address),
2899
4790
  })
2900
4791
  : undefined
4792
+ const liquidityValue =
4793
+ options.include.includes('tvl') && discovery.instantLiquidity !== null
4794
+ ? Valuation.valuationFor({
4795
+ amount: BigInt(discovery.instantLiquidity),
4796
+ denomination: tvlDenomination,
4797
+ rates: options.rates,
4798
+ token: options.snapshot.byAddress.get(discovery.assetToken.address),
4799
+ })
4800
+ : undefined
2901
4801
  return serializeVault({
2902
4802
  apy: options.apy?.get(discovery.vaultAddress) ?? null,
2903
4803
  assetToken: getToken(options.tokens, discovery.assetToken.address),
2904
4804
  discovery,
2905
4805
  include: options.include,
4806
+ instantLiquidityValue: liquidityValue
4807
+ ? {
4808
+ amount: core_Value.from(liquidityValue.amount, tvlDecimals).toString(),
4809
+ currency: liquidityValue.currency,
4810
+ decimals: tvlDecimals,
4811
+ formatted: liquidityValue.amount,
4812
+ }
4813
+ : null,
2906
4814
  record,
4815
+ rewards: options.rewards,
2907
4816
  shareToken: getToken(options.tokens, discovery.shareToken.address),
2908
4817
  tvl: valued
2909
4818
  ? {
@@ -2925,6 +4834,7 @@ declare namespace resolveVault {
2925
4834
  include: z.output<typeof schema.getEarnVault.Query>['include']
2926
4835
  rates: Awaited<ReturnType<typeof Valuation.ratesFor>>
2927
4836
  record?: RegistryRecord | undefined
4837
+ rewards?: readonly RewardRead.Reward[] | undefined
2928
4838
  snapshot: VerifiedTokens.Snapshot
2929
4839
  tokens: Tokens.resolveTokens.ReturnType
2930
4840
  zoneRoutes: readonly EarnVaults.ResolvedZoneRoute[]
@@ -2939,9 +4849,41 @@ export function serializeVault(options: serializeVault.Options): z.output<typeof
2939
4849
  if (options.include.includes('capabilities') && !discovery.capabilities)
2940
4850
  throw new Error('Earn vault capabilities were not resolved.')
2941
4851
  const vaultAddress = Schema.Address.parse(discovery.vaultAddress.toLowerCase())
4852
+ const rewards = options.rewards?.map((reward) => ({
4853
+ apr:
4854
+ reward.currentRewardsAprBps === null ? null : RewardRead.ratio(reward.currentRewardsAprBps),
4855
+ asset: reward.asset,
4856
+ ...(reward.calculatedThrough !== undefined
4857
+ ? { calculatedThrough: reward.calculatedThrough }
4858
+ : {}),
4859
+ campaignId: reward.campaignId,
4860
+ endsAt: reward.endsAt,
4861
+ startsAt: reward.startsAt,
4862
+ targetTotalApr: RewardRead.ratio(reward.targetTotalAprBps),
4863
+ }))
4864
+ const startedRewards = options.rewards?.filter(
4865
+ (reward) => reward.startsAt <= Math.floor(Date.now() / 1_000),
4866
+ )
4867
+ const targetTotalAprBps = startedRewards?.reduce(
4868
+ (target, reward) => Math.max(target, reward.targetTotalAprBps),
4869
+ 0,
4870
+ )
4871
+ const apy = options.apy
4872
+ ? {
4873
+ ...options.apy,
4874
+ net: (() => {
4875
+ if (!startedRewards || startedRewards.length === 0) return options.apy.net
4876
+ const target = targetTotalAprBps ?? 0
4877
+ if (parseUnits(options.apy.net, 6) >= BigInt(target) * 100n) return options.apy.net
4878
+ return RewardRead.ratio(target)
4879
+ })(),
4880
+ ...(rewards ? { rewards } : {}),
4881
+ vault: options.apy.net,
4882
+ }
4883
+ : null
2942
4884
  return Response.validated(schema.Vault, {
2943
4885
  ...(options.include.includes('access') ? { access: discovery.access } : {}),
2944
- ...(options.include.includes('apy') ? { apy: options.apy } : {}),
4886
+ ...(options.include.includes('apy') ? { apy } : {}),
2945
4887
  assetToken: options.assetToken,
2946
4888
  ...(options.include.includes('capabilities') ? { capabilities: discovery.capabilities } : {}),
2947
4889
  description: record?.description ?? null,
@@ -2952,6 +4894,9 @@ export function serializeVault(options: serializeVault.Options): z.output<typeof
2952
4894
  },
2953
4895
  id: vaultAddress,
2954
4896
  instantLiquidity: discovery.instantLiquidity,
4897
+ ...(options.include.includes('tvl')
4898
+ ? { instantLiquidityValue: options.instantLiquidityValue }
4899
+ : {}),
2955
4900
  label: record?.label ?? options.shareToken.name,
2956
4901
  sharePrice:
2957
4902
  discovery.sharePrice === null
@@ -2995,8 +4940,12 @@ export declare namespace serializeVault {
2995
4940
  discovery: EarnVaults.Discovery
2996
4941
  /** Optional vault fields selected by the request. */
2997
4942
  include: z.output<typeof schema.getEarnVault.Query>['include']
4943
+ /** USD value of instant liquidity, or null when unavailable; omitted unless `include` carries `tvl`. */
4944
+ instantLiquidityValue: z.output<typeof schema.Vault>['instantLiquidityValue']
2998
4945
  /** Curated registry row, when verified. */
2999
4946
  record?: core_EarnVaults.Record | undefined
4947
+ /** Enabled, registered Merkle rewards, or undefined when enrichment is unavailable. */
4948
+ rewards?: readonly RewardRead.Reward[] | undefined
3000
4949
  /** Resolved share token. */
3001
4950
  shareToken: z.output<typeof Tokens.schema.Token>
3002
4951
  /** USD value of the assets, or null when unpriced; omitted unless `include` carries `tvl`. */
@@ -3065,6 +5014,8 @@ async function queryDeployments(
3065
5014
  c: Context<App.Environment>,
3066
5015
  options: queryDeployments.Options,
3067
5016
  ): Promise<queryDeployments.Result> {
5017
+ const factories = deploymentFactories[options.chainId] ?? []
5018
+ if (factories.length === 0) return { hasMore: false, items: [] }
3068
5019
  const filters: string[] = []
3069
5020
  if (options.asset) filters.push(`lower(asset) = '${options.asset.toLowerCase()}'`)
3070
5021
  if (options.cursor)
@@ -3083,7 +5034,8 @@ async function queryDeployments(
3083
5034
  ]),
3084
5035
  )
3085
5036
 
3086
- // Deduplicate before outer filters so every vault is filtered by its latest deployment.
5037
+ // Authenticate emitters before deduplication so a forged newer event cannot
5038
+ // replace a factory deployment or suppress it through outer filters.
3087
5039
  const result = await c
3088
5040
  .get('getTidx')(options.chainId)
3089
5041
  .fetch({
@@ -3098,6 +5050,7 @@ async function queryDeployments(
3098
5050
  block_num,
3099
5051
  log_idx
3100
5052
  FROM EarnStackDeployed
5053
+ WHERE address IN (${factories.map((address) => `'${address}'`).join(', ')})
3101
5054
  ORDER BY block_num DESC, log_idx DESC
3102
5055
  LIMIT 1 BY lower("earnVault")
3103
5056
  )
@@ -3195,3 +5148,36 @@ type RegistryRecord = Awaited<ReturnType<typeof core_EarnVaults.list>>[number]
3195
5148
  export class NotFoundError extends Error {
3196
5149
  override name = 'Earn.NotFoundError'
3197
5150
  }
5151
+
5152
+ /** Thrown when a historical position boundary is not indexed. */
5153
+ class IndexedBlockNotFoundError extends Error {
5154
+ override name = 'Earn.IndexedBlockNotFoundError'
5155
+
5156
+ constructor(asOf: string) {
5157
+ super(`No indexed block exists at or before ${asOf}.`)
5158
+ }
5159
+ }
5160
+
5161
+ class IndexedCoverageError extends Error {
5162
+ override name = 'Earn.IndexedCoverageError'
5163
+
5164
+ constructor(asOf: string) {
5165
+ super(`Indexed history does not yet cover ${asOf}.`)
5166
+ }
5167
+ }
5168
+
5169
+ class ZoneHistoricalAlignmentError extends Error {
5170
+ override name = 'Earn.ZoneHistoricalAlignmentError'
5171
+
5172
+ constructor() {
5173
+ super('This Zone does not expose historical parent-chain synchronization checkpoints.')
5174
+ }
5175
+ }
5176
+
5177
+ class ZoneEarningsUnsupportedError extends Error {
5178
+ override name = 'Earn.ZoneEarningsUnsupportedError'
5179
+
5180
+ constructor() {
5181
+ super('Zone positions do not support the earnings include.')
5182
+ }
5183
+ }