tapimo 0.14.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 (1141) 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 +12457 -10365
  9. package/dist/App.d.ts.map +1 -1
  10. package/dist/App.js +300 -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 +1 -1
  24. package/dist/Webhooks.js.map +1 -1
  25. package/dist/admin/App.d.ts +3057 -549
  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 +1398 -0
  34. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  35. package/dist/admin/apps/earn-vaults.js +463 -7
  36. package/dist/admin/apps/earn-vaults.js.map +1 -1
  37. package/dist/admin/apps/organizations.d.ts +267 -15
  38. package/dist/admin/apps/organizations.d.ts.map +1 -1
  39. package/dist/admin/apps/organizations.js +241 -25
  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 +88 -88
  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 +37 -5
  76. package/dist/apps/Zones.js.map +1 -1
  77. package/dist/apps/data/App.d.ts +3971 -2595
  78. package/dist/apps/data/App.d.ts.map +1 -1
  79. package/dist/apps/data/App.js +76 -21
  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 +72 -0
  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 +1450 -337
  92. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  93. package/dist/apps/data/routes/earn.js +1226 -144
  94. package/dist/apps/data/routes/earn.js.map +1 -1
  95. package/dist/apps/data/routes/fee-amm.d.ts +5 -0
  96. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  97. package/dist/apps/data/routes/fee-amm.js +31 -19
  98. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  99. package/dist/apps/data/routes/indexer.d.ts +125 -0
  100. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  101. package/dist/apps/data/routes/indexer.js +83 -27
  102. package/dist/apps/data/routes/indexer.js.map +1 -1
  103. package/dist/apps/data/routes/rpc.d.ts +507 -33
  104. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  105. package/dist/apps/data/routes/rpc.js +266 -156
  106. package/dist/apps/data/routes/rpc.js.map +1 -1
  107. package/dist/apps/data/routes/tokenlist.d.ts +47 -15
  108. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  109. package/dist/apps/data/routes/tokenlist.js +68 -11
  110. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  111. package/dist/apps/data/routes/tokens.d.ts +2 -0
  112. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  113. package/dist/apps/data/routes/tokens.js +26 -14
  114. package/dist/apps/data/routes/tokens.js.map +1 -1
  115. package/dist/apps/data/routes/transfers.d.ts +28 -12
  116. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  117. package/dist/apps/data/routes/transfers.js +9 -7
  118. package/dist/apps/data/routes/transfers.js.map +1 -1
  119. package/dist/apps/data/routes/valuation.d.ts +30 -30
  120. package/dist/apps/data/routes/verified-tokens.d.ts +9 -9
  121. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  122. package/dist/apps/data/routes/verified-tokens.js +64 -20
  123. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  124. package/dist/apps/data/routes/webhooks.d.ts +613 -176
  125. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  126. package/dist/apps/data/routes/webhooks.js +291 -195
  127. package/dist/apps/data/routes/webhooks.js.map +1 -1
  128. package/dist/apps/data/routes/zones.d.ts.map +1 -1
  129. package/dist/apps/data/routes/zones.js +20 -12
  130. package/dist/apps/data/routes/zones.js.map +1 -1
  131. package/dist/apps/docs/reference.js +2 -2
  132. package/dist/apps/docs/reference.js.map +1 -1
  133. package/dist/apps/index.d.ts +1 -1
  134. package/dist/apps/index.d.ts.map +1 -1
  135. package/dist/apps/index.js +1 -1
  136. package/dist/apps/index.js.map +1 -1
  137. package/dist/apps/management/App.d.ts +3763 -3385
  138. package/dist/apps/management/App.d.ts.map +1 -1
  139. package/dist/apps/management/App.js +5 -14
  140. package/dist/apps/management/App.js.map +1 -1
  141. package/dist/apps/management/Billing.d.ts +48 -7
  142. package/dist/apps/management/Billing.d.ts.map +1 -1
  143. package/dist/apps/management/Billing.js +204 -25
  144. package/dist/apps/management/Billing.js.map +1 -1
  145. package/dist/apps/management/routes/api-keys.d.ts +106 -4
  146. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  147. package/dist/apps/management/routes/api-keys.js +43 -15
  148. package/dist/apps/management/routes/api-keys.js.map +1 -1
  149. package/dist/apps/management/routes/billing.d.ts +290 -4
  150. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  151. package/dist/apps/management/routes/billing.js +274 -49
  152. package/dist/apps/management/routes/billing.js.map +1 -1
  153. package/dist/apps/management/routes/faucet.d.ts +6 -6
  154. package/dist/apps/management/routes/faucet.js +1 -1
  155. package/dist/apps/management/routes/faucet.js.map +1 -1
  156. package/dist/apps/management/routes/invitations.d.ts +77 -2
  157. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  158. package/dist/apps/management/routes/invitations.js +117 -13
  159. package/dist/apps/management/routes/invitations.js.map +1 -1
  160. package/dist/apps/management/routes/invite-links.d.ts +4 -4
  161. package/dist/apps/management/routes/me.d.ts.map +1 -1
  162. package/dist/apps/management/routes/me.js +6 -2
  163. package/dist/apps/management/routes/me.js.map +1 -1
  164. package/dist/apps/management/routes/members.d.ts +30 -30
  165. package/dist/apps/management/routes/orgs.d.ts +18 -2
  166. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  167. package/dist/apps/management/routes/orgs.js +52 -12
  168. package/dist/apps/management/routes/orgs.js.map +1 -1
  169. package/dist/apps/management/routes/projects.d.ts +4 -4
  170. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  171. package/dist/apps/management/routes/projects.js +6 -1
  172. package/dist/apps/management/routes/projects.js.map +1 -1
  173. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  174. package/dist/apps/management/routes/scopes.js +4 -1
  175. package/dist/apps/management/routes/scopes.js.map +1 -1
  176. package/dist/apps/management/routes/usage.d.ts +24 -24
  177. package/dist/apps/management/routes/usage.js +21 -4
  178. package/dist/apps/management/routes/usage.js.map +1 -1
  179. package/dist/apps/management/routes/verified-token-requests.d.ts +2 -2
  180. package/dist/apps/management/routes/webhooks.d.ts +280 -214
  181. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  182. package/dist/apps/management/routes/webhooks.js +58 -12
  183. package/dist/apps/management/routes/webhooks.js.map +1 -1
  184. package/dist/apps/mcp.d.ts +1 -1
  185. package/dist/apps/mcp.js +7 -7
  186. package/dist/apps/mcp.js.map +1 -1
  187. package/dist/apps/mpp/App.d.ts.map +1 -1
  188. package/dist/apps/mpp/App.js +35 -7
  189. package/dist/apps/mpp/App.js.map +1 -1
  190. package/dist/apps/mpp/internal/Adapter.d.ts +2 -0
  191. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
  192. package/dist/apps/mpp/internal/Adapter.js +30 -1
  193. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  194. package/dist/apps/mpp/internal/Policy.d.ts +22 -4
  195. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
  196. package/dist/apps/mpp/internal/Policy.js +24 -6
  197. package/dist/apps/mpp/internal/Policy.js.map +1 -1
  198. package/dist/apps/mpp/internal/Sponsorship.d.ts +3 -0
  199. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
  200. package/dist/apps/mpp/internal/Sponsorship.js +6 -3
  201. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
  202. package/dist/apps/relay/App.d.ts +2 -0
  203. package/dist/apps/relay/App.d.ts.map +1 -1
  204. package/dist/apps/relay/App.js +134 -75
  205. package/dist/apps/relay/App.js.map +1 -1
  206. package/dist/apps/relay/Sponsorships.d.ts +5 -3
  207. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  208. package/dist/apps/relay/Sponsorships.js +209 -28
  209. package/dist/apps/relay/Sponsorships.js.map +1 -1
  210. package/dist/apps/{funding → routes}/Access.d.ts +4 -4
  211. package/dist/apps/routes/Access.d.ts.map +1 -0
  212. package/dist/apps/{funding → routes}/Access.js +4 -4
  213. package/dist/apps/routes/Access.js.map +1 -0
  214. package/dist/apps/{funding → routes}/App.d.ts +418 -124
  215. package/dist/apps/{funding → routes}/App.d.ts.map +1 -1
  216. package/dist/apps/{funding → routes}/App.js +16 -16
  217. package/dist/apps/routes/App.js.map +1 -0
  218. package/dist/apps/routes/Inventory.d.ts.map +1 -0
  219. package/dist/apps/{funding → routes}/Inventory.js +2 -2
  220. package/dist/apps/routes/Inventory.js.map +1 -0
  221. package/dist/apps/routes/index.d.ts +2 -0
  222. package/dist/apps/routes/index.d.ts.map +1 -0
  223. package/dist/apps/routes/index.js +2 -0
  224. package/dist/apps/routes/index.js.map +1 -0
  225. package/dist/apps/{funding → routes}/routes/chains.d.ts +7 -7
  226. package/dist/apps/routes/routes/chains.d.ts.map +1 -0
  227. package/dist/apps/{funding → routes}/routes/chains.js +25 -25
  228. package/dist/apps/routes/routes/chains.js.map +1 -0
  229. package/dist/apps/{funding → routes}/routes/deposit-addresses.d.ts +107 -43
  230. package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -0
  231. package/dist/apps/{funding → routes}/routes/deposit-addresses.js +497 -222
  232. package/dist/apps/routes/routes/deposit-addresses.js.map +1 -0
  233. package/dist/apps/{funding → routes}/routes/providers.d.ts +8 -8
  234. package/dist/apps/routes/routes/providers.d.ts.map +1 -0
  235. package/dist/apps/routes/routes/providers.js +57 -0
  236. package/dist/apps/routes/routes/providers.js.map +1 -0
  237. package/dist/apps/{funding → routes}/routes/quotes.d.ts +19 -16
  238. package/dist/apps/routes/routes/quotes.d.ts.map +1 -0
  239. package/dist/apps/{funding → routes}/routes/quotes.js +55 -38
  240. package/dist/apps/routes/routes/quotes.js.map +1 -0
  241. package/dist/apps/{funding → routes}/routes/transfers.d.ts +379 -74
  242. package/dist/apps/routes/routes/transfers.d.ts.map +1 -0
  243. package/dist/apps/routes/routes/transfers.js +1140 -0
  244. package/dist/apps/routes/routes/transfers.js.map +1 -0
  245. package/dist/cli/Cli.d.ts +30 -0
  246. package/dist/cli/Cli.d.ts.map +1 -1
  247. package/dist/cli/commands/admin.d.ts +36 -0
  248. package/dist/cli/commands/admin.d.ts.map +1 -1
  249. package/dist/cli/commands/admin.js +273 -0
  250. package/dist/cli/commands/admin.js.map +1 -1
  251. package/dist/cloudflare.d.ts +16 -14
  252. package/dist/cloudflare.d.ts.map +1 -1
  253. package/dist/cloudflare.js +26 -21
  254. package/dist/cloudflare.js.map +1 -1
  255. package/dist/db/Db.d.ts +63 -21
  256. package/dist/db/Db.d.ts.map +1 -1
  257. package/dist/db/Db.js +1030 -15
  258. package/dist/db/Db.js.map +1 -1
  259. package/dist/db/Schema.d.ts +780 -133
  260. package/dist/db/Schema.d.ts.map +1 -1
  261. package/dist/db/Schema.js +575 -162
  262. package/dist/db/Schema.js.map +1 -1
  263. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts +4 -0
  264. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts.map +1 -0
  265. package/dist/db/tables/apiKeyAdmissionBootstraps.js +2 -0
  266. package/dist/db/tables/apiKeyAdmissionBootstraps.js.map +1 -0
  267. package/dist/db/tables/apiKeyAdmissions.d.ts +73 -0
  268. package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -0
  269. package/dist/db/tables/apiKeyAdmissions.js +142 -0
  270. package/dist/db/tables/apiKeyAdmissions.js.map +1 -0
  271. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts +4 -0
  272. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts.map +1 -0
  273. package/dist/db/tables/apiKeyOwnerAdmissions.js +2 -0
  274. package/dist/db/tables/apiKeyOwnerAdmissions.js.map +1 -0
  275. package/dist/db/tables/apiKeyOwnerTombstones.d.ts +20 -0
  276. package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -0
  277. package/dist/db/tables/apiKeyOwnerTombstones.js +57 -0
  278. package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -0
  279. package/dist/db/tables/apiKeyRevocations.d.ts +21 -0
  280. package/dist/db/tables/apiKeyRevocations.d.ts.map +1 -0
  281. package/dist/db/tables/apiKeyRevocations.js +98 -0
  282. package/dist/db/tables/apiKeyRevocations.js.map +1 -0
  283. package/dist/db/tables/authAccounts.d.ts +47 -0
  284. package/dist/db/tables/authAccounts.d.ts.map +1 -0
  285. package/dist/db/tables/authAccounts.js +72 -0
  286. package/dist/db/tables/authAccounts.js.map +1 -0
  287. package/dist/db/tables/authSessions.d.ts +12 -0
  288. package/dist/db/tables/authSessions.d.ts.map +1 -0
  289. package/dist/db/tables/authSessions.js +2 -0
  290. package/dist/db/tables/authSessions.js.map +1 -0
  291. package/dist/db/tables/authVerifications.d.ts +12 -0
  292. package/dist/db/tables/authVerifications.d.ts.map +1 -0
  293. package/dist/db/tables/authVerifications.js +2 -0
  294. package/dist/db/tables/authVerifications.js.map +1 -0
  295. package/dist/db/tables/invitations.d.ts +17 -1
  296. package/dist/db/tables/invitations.d.ts.map +1 -1
  297. package/dist/db/tables/invitations.js +123 -43
  298. package/dist/db/tables/invitations.js.map +1 -1
  299. package/dist/db/tables/memberships.d.ts +8 -0
  300. package/dist/db/tables/memberships.d.ts.map +1 -1
  301. package/dist/db/tables/memberships.js +51 -2
  302. package/dist/db/tables/memberships.js.map +1 -1
  303. package/dist/db/tables/organizations.d.ts +32 -10
  304. package/dist/db/tables/organizations.d.ts.map +1 -1
  305. package/dist/db/tables/organizations.js +110 -49
  306. package/dist/db/tables/organizations.js.map +1 -1
  307. package/dist/db/tables/projects.d.ts +2 -0
  308. package/dist/db/tables/projects.d.ts.map +1 -1
  309. package/dist/db/tables/projects.js +12 -3
  310. package/dist/db/tables/projects.js.map +1 -1
  311. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  312. package/dist/db/tables/requestUsage.js.map +1 -1
  313. package/dist/db/tables/rewardAccounts.d.ts +86 -0
  314. package/dist/db/tables/rewardAccounts.d.ts.map +1 -0
  315. package/dist/db/tables/rewardAccounts.js +88 -0
  316. package/dist/db/tables/rewardAccounts.js.map +1 -0
  317. package/dist/db/tables/rewardCampaignDistributors.d.ts +36 -0
  318. package/dist/db/tables/rewardCampaignDistributors.d.ts.map +1 -0
  319. package/dist/db/tables/rewardCampaignDistributors.js +30 -0
  320. package/dist/db/tables/rewardCampaignDistributors.js.map +1 -0
  321. package/dist/db/tables/rewardCampaigns.d.ts +171 -0
  322. package/dist/db/tables/rewardCampaigns.d.ts.map +1 -0
  323. package/dist/db/tables/rewardCampaigns.js +392 -0
  324. package/dist/db/tables/rewardCampaigns.js.map +1 -0
  325. package/dist/db/tables/rewardCredits.d.ts +53 -0
  326. package/dist/db/tables/rewardCredits.d.ts.map +1 -0
  327. package/dist/db/tables/rewardCredits.js +63 -0
  328. package/dist/db/tables/rewardCredits.js.map +1 -0
  329. package/dist/db/tables/rewardEligibilityAssociations.d.ts +61 -0
  330. package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -0
  331. package/dist/db/tables/rewardEligibilityAssociations.js +103 -0
  332. package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -0
  333. package/dist/db/tables/rewardRuns.d.ts +188 -0
  334. package/dist/db/tables/rewardRuns.d.ts.map +1 -0
  335. package/dist/db/tables/rewardRuns.js +278 -0
  336. package/dist/db/tables/rewardRuns.js.map +1 -0
  337. package/dist/db/tables/rewardTransactionAttempts.d.ts +100 -0
  338. package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -0
  339. package/dist/db/tables/rewardTransactionAttempts.js +163 -0
  340. package/dist/db/tables/rewardTransactionAttempts.js.map +1 -0
  341. package/dist/db/tables/routesCatalog.d.ts +58 -0
  342. package/dist/db/tables/routesCatalog.d.ts.map +1 -0
  343. package/dist/db/tables/{fundingCatalog.js → routesCatalog.js} +19 -19
  344. package/dist/db/tables/routesCatalog.js.map +1 -0
  345. package/dist/db/tables/routesDepositAddresses.d.ts +476 -0
  346. package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -0
  347. package/dist/db/tables/routesDepositAddresses.js +587 -0
  348. package/dist/db/tables/routesDepositAddresses.js.map +1 -0
  349. package/dist/db/tables/{fundingDepositRequestObservations.d.ts → routesDepositRequestObservations.d.ts} +6 -6
  350. package/dist/db/tables/routesDepositRequestObservations.d.ts.map +1 -0
  351. package/dist/db/tables/{fundingDepositRequestObservations.js → routesDepositRequestObservations.js} +6 -6
  352. package/dist/db/tables/routesDepositRequestObservations.js.map +1 -0
  353. package/dist/db/tables/{fundingDeposits.d.ts → routesDeposits.d.ts} +210 -22
  354. package/dist/db/tables/routesDeposits.d.ts.map +1 -0
  355. package/dist/db/tables/routesDeposits.js +576 -0
  356. package/dist/db/tables/routesDeposits.js.map +1 -0
  357. package/dist/db/tables/routesIdempotency.d.ts +166 -0
  358. package/dist/db/tables/routesIdempotency.d.ts.map +1 -0
  359. package/dist/db/tables/routesIdempotency.js +330 -0
  360. package/dist/db/tables/routesIdempotency.js.map +1 -0
  361. package/dist/db/tables/routesSubsidies.d.ts +27 -0
  362. package/dist/db/tables/routesSubsidies.d.ts.map +1 -0
  363. package/dist/db/tables/routesSubsidies.js +45 -0
  364. package/dist/db/tables/routesSubsidies.js.map +1 -0
  365. package/dist/db/tables/{fundingTransferEvents.d.ts → routesTransferEvents.d.ts} +3 -3
  366. package/dist/db/tables/routesTransferEvents.d.ts.map +1 -0
  367. package/dist/db/tables/{fundingTransferEvents.js → routesTransferEvents.js} +3 -3
  368. package/dist/db/tables/routesTransferEvents.js.map +1 -0
  369. package/dist/db/tables/routesTransferSubsidies.d.ts +19 -0
  370. package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -0
  371. package/dist/db/tables/routesTransferSubsidies.js +17 -0
  372. package/dist/db/tables/routesTransferSubsidies.js.map +1 -0
  373. package/dist/db/tables/routesTransferSubsidyNonces.d.ts +51 -0
  374. package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -0
  375. package/dist/db/tables/routesTransferSubsidyNonces.js +82 -0
  376. package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -0
  377. package/dist/db/tables/routesTransferTransactions.d.ts +77 -0
  378. package/dist/db/tables/routesTransferTransactions.d.ts.map +1 -0
  379. package/dist/db/tables/{fundingTransferTransactions.js → routesTransferTransactions.js} +31 -13
  380. package/dist/db/tables/routesTransferTransactions.js.map +1 -0
  381. package/dist/db/tables/routesTransfers.d.ts +319 -0
  382. package/dist/db/tables/routesTransfers.d.ts.map +1 -0
  383. package/dist/db/tables/routesTransfers.js +372 -0
  384. package/dist/db/tables/routesTransfers.js.map +1 -0
  385. package/dist/db/tables/sponsoredTransactions.d.ts +75 -20
  386. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  387. package/dist/db/tables/sponsoredTransactions.js +124 -54
  388. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  389. package/dist/db/tables/sponsorshipAttributions.d.ts +69 -0
  390. package/dist/db/tables/sponsorshipAttributions.d.ts.map +1 -0
  391. package/dist/db/tables/sponsorshipAttributions.js +77 -0
  392. package/dist/db/tables/sponsorshipAttributions.js.map +1 -0
  393. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts +22 -0
  394. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts.map +1 -0
  395. package/dist/db/tables/sponsorshipReconciliationCursors.js +45 -0
  396. package/dist/db/tables/sponsorshipReconciliationCursors.js.map +1 -0
  397. package/dist/db/tables/stripeCustomers.d.ts +11 -0
  398. package/dist/db/tables/stripeCustomers.d.ts.map +1 -1
  399. package/dist/db/tables/stripeCustomers.js +13 -0
  400. package/dist/db/tables/stripeCustomers.js.map +1 -1
  401. package/dist/db/tables/users.d.ts +17 -1
  402. package/dist/db/tables/users.d.ts.map +1 -1
  403. package/dist/db/tables/users.js +47 -2
  404. package/dist/db/tables/users.js.map +1 -1
  405. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  406. package/dist/db/tables/webhookQueueEvents.js +5 -1
  407. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  408. package/dist/db/tables/webhookSubscriptions.d.ts +19 -11
  409. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  410. package/dist/db/tables/webhookSubscriptions.js +45 -13
  411. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  412. package/dist/handlers/internal/executionError.d.ts +2 -2
  413. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  414. package/dist/handlers/internal/executionError.js +1 -2
  415. package/dist/handlers/internal/executionError.js.map +1 -1
  416. package/dist/handlers/internal/multisig.d.ts +4 -232
  417. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  418. package/dist/handlers/internal/multisig.js +11 -537
  419. package/dist/handlers/internal/multisig.js.map +1 -1
  420. package/dist/handlers/internal/sponsorship.d.ts +19 -8
  421. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  422. package/dist/handlers/internal/sponsorship.js +13 -9
  423. package/dist/handlers/internal/sponsorship.js.map +1 -1
  424. package/dist/handlers/relay.d.ts +9 -12
  425. package/dist/handlers/relay.d.ts.map +1 -1
  426. package/dist/handlers/relay.js +81 -85
  427. package/dist/handlers/relay.js.map +1 -1
  428. package/dist/index.d.ts +0 -17
  429. package/dist/index.d.ts.map +1 -1
  430. package/dist/index.js +0 -17
  431. package/dist/index.js.map +1 -1
  432. package/dist/internal/AssetResponse.d.ts +12 -0
  433. package/dist/internal/AssetResponse.d.ts.map +1 -0
  434. package/dist/internal/AssetResponse.js +21 -0
  435. package/dist/internal/AssetResponse.js.map +1 -0
  436. package/dist/internal/Auth.d.ts +48 -12
  437. package/dist/internal/Auth.d.ts.map +1 -1
  438. package/dist/internal/Auth.js +603 -175
  439. package/dist/internal/Auth.js.map +1 -1
  440. package/dist/internal/BetterAuth.d.ts +1488 -0
  441. package/dist/internal/BetterAuth.d.ts.map +1 -0
  442. package/dist/internal/BetterAuth.js +253 -0
  443. package/dist/internal/BetterAuth.js.map +1 -0
  444. package/dist/internal/Cache.d.ts +2 -0
  445. package/dist/internal/Cache.d.ts.map +1 -1
  446. package/dist/internal/Cache.js +2 -0
  447. package/dist/internal/Cache.js.map +1 -1
  448. package/dist/internal/EarnRates.d.ts +26 -3
  449. package/dist/internal/EarnRates.d.ts.map +1 -1
  450. package/dist/internal/EarnRates.js +84 -19
  451. package/dist/internal/EarnRates.js.map +1 -1
  452. package/dist/internal/EarnSharePrices.d.ts +120 -0
  453. package/dist/internal/EarnSharePrices.d.ts.map +1 -0
  454. package/dist/internal/EarnSharePrices.js +179 -0
  455. package/dist/internal/EarnSharePrices.js.map +1 -0
  456. package/dist/internal/EarnVaults.d.ts +4 -0
  457. package/dist/internal/EarnVaults.d.ts.map +1 -1
  458. package/dist/internal/EarnVaults.js +27 -10
  459. package/dist/internal/EarnVaults.js.map +1 -1
  460. package/dist/internal/Email.d.ts +37 -0
  461. package/dist/internal/Email.d.ts.map +1 -0
  462. package/dist/internal/Email.js +16 -0
  463. package/dist/internal/Email.js.map +1 -0
  464. package/dist/internal/Log.d.ts +32 -28
  465. package/dist/internal/Log.d.ts.map +1 -1
  466. package/dist/internal/Log.js +18 -11
  467. package/dist/internal/Log.js.map +1 -1
  468. package/dist/internal/MetricSink.d.ts +3 -0
  469. package/dist/internal/MetricSink.d.ts.map +1 -1
  470. package/dist/internal/MetricSink.js +67 -8
  471. package/dist/internal/MetricSink.js.map +1 -1
  472. package/dist/internal/Mpp.d.ts.map +1 -1
  473. package/dist/internal/Mpp.js +20 -25
  474. package/dist/internal/Mpp.js.map +1 -1
  475. package/dist/internal/OpenApi.d.ts +26 -3
  476. package/dist/internal/OpenApi.d.ts.map +1 -1
  477. package/dist/internal/OpenApi.js +25 -10
  478. package/dist/internal/OpenApi.js.map +1 -1
  479. package/dist/internal/Provider.d.ts +1 -1
  480. package/dist/internal/Provider.d.ts.map +1 -1
  481. package/dist/internal/RateLimit.d.ts.map +1 -1
  482. package/dist/internal/RateLimit.js +13 -1
  483. package/dist/internal/RateLimit.js.map +1 -1
  484. package/dist/internal/Store.d.ts +16 -0
  485. package/dist/internal/Store.d.ts.map +1 -1
  486. package/dist/internal/Store.js +59 -8
  487. package/dist/internal/Store.js.map +1 -1
  488. package/dist/internal/Tidx.d.ts +9 -2
  489. package/dist/internal/Tidx.d.ts.map +1 -1
  490. package/dist/internal/Tidx.js +78 -11
  491. package/dist/internal/Tidx.js.map +1 -1
  492. package/dist/internal/Timing.d.ts +26 -0
  493. package/dist/internal/Timing.d.ts.map +1 -1
  494. package/dist/internal/Timing.js +63 -5
  495. package/dist/internal/Timing.js.map +1 -1
  496. package/dist/internal/VercelOidc.d.ts +94 -0
  497. package/dist/internal/VercelOidc.d.ts.map +1 -0
  498. package/dist/internal/VercelOidc.js +128 -0
  499. package/dist/internal/VercelOidc.js.map +1 -0
  500. package/dist/internal/VerifiedTokens.d.ts +2 -8
  501. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  502. package/dist/internal/VerifiedTokens.js +24 -24
  503. package/dist/internal/VerifiedTokens.js.map +1 -1
  504. package/dist/internal/Viem.d.ts +30 -2067
  505. package/dist/internal/Viem.d.ts.map +1 -1
  506. package/dist/internal/Viem.js +167 -17
  507. package/dist/internal/Viem.js.map +1 -1
  508. package/dist/internal/Webhooks.d.ts +12 -4
  509. package/dist/internal/Webhooks.d.ts.map +1 -1
  510. package/dist/internal/Webhooks.js +9 -3
  511. package/dist/internal/Webhooks.js.map +1 -1
  512. package/dist/internal/ZoneRpc.d.ts +7 -0
  513. package/dist/internal/ZoneRpc.d.ts.map +1 -0
  514. package/dist/internal/ZoneRpc.js +49 -0
  515. package/dist/internal/ZoneRpc.js.map +1 -0
  516. package/dist/internal/index.d.ts +3 -1
  517. package/dist/internal/index.d.ts.map +1 -1
  518. package/dist/internal/index.js +3 -1
  519. package/dist/internal/index.js.map +1 -1
  520. package/dist/internal/rewards/Audit.d.ts +88 -0
  521. package/dist/internal/rewards/Audit.d.ts.map +1 -0
  522. package/dist/internal/rewards/Audit.js +134 -0
  523. package/dist/internal/rewards/Audit.js.map +1 -0
  524. package/dist/internal/rewards/Campaigns.d.ts +572 -0
  525. package/dist/internal/rewards/Campaigns.d.ts.map +1 -0
  526. package/dist/internal/rewards/Campaigns.js +531 -0
  527. package/dist/internal/rewards/Campaigns.js.map +1 -0
  528. package/dist/internal/rewards/Credits.d.ts +76 -0
  529. package/dist/internal/rewards/Credits.d.ts.map +1 -0
  530. package/dist/internal/rewards/Credits.js +95 -0
  531. package/dist/internal/rewards/Credits.js.map +1 -0
  532. package/dist/internal/rewards/Events.d.ts +100 -0
  533. package/dist/internal/rewards/Events.d.ts.map +1 -0
  534. package/dist/internal/rewards/Events.js +340 -0
  535. package/dist/internal/rewards/Events.js.map +1 -0
  536. package/dist/internal/rewards/Projection.d.ts +237 -0
  537. package/dist/internal/rewards/Projection.d.ts.map +1 -0
  538. package/dist/internal/rewards/Projection.js +616 -0
  539. package/dist/internal/rewards/Projection.js.map +1 -0
  540. package/dist/internal/rewards/Rates.d.ts +44 -0
  541. package/dist/internal/rewards/Rates.d.ts.map +1 -0
  542. package/dist/internal/rewards/Rates.js +60 -0
  543. package/dist/internal/rewards/Rates.js.map +1 -0
  544. package/dist/internal/rewards/Read.d.ts +158 -0
  545. package/dist/internal/rewards/Read.d.ts.map +1 -0
  546. package/dist/internal/rewards/Read.js +292 -0
  547. package/dist/internal/rewards/Read.js.map +1 -0
  548. package/dist/internal/rewards/Runner.d.ts +164 -0
  549. package/dist/internal/rewards/Runner.d.ts.map +1 -0
  550. package/dist/internal/rewards/Runner.js +1778 -0
  551. package/dist/internal/rewards/Runner.js.map +1 -0
  552. package/dist/internal/rewards/Signer.d.ts +224 -0
  553. package/dist/internal/rewards/Signer.d.ts.map +1 -0
  554. package/dist/internal/rewards/Signer.js +135 -0
  555. package/dist/internal/rewards/Signer.js.map +1 -0
  556. package/dist/internal/rewards/index.d.ts +15 -0
  557. package/dist/internal/rewards/index.d.ts.map +1 -0
  558. package/dist/internal/rewards/index.js +15 -0
  559. package/dist/internal/rewards/index.js.map +1 -0
  560. package/dist/internal/{funding → routes}/Action.d.ts +12 -1
  561. package/dist/internal/routes/Action.d.ts.map +1 -0
  562. package/dist/internal/{funding → routes}/Action.js +17 -9
  563. package/dist/internal/routes/Action.js.map +1 -0
  564. package/dist/internal/{funding → routes}/Catalog.d.ts +20 -20
  565. package/dist/internal/routes/Catalog.d.ts.map +1 -0
  566. package/dist/internal/{funding → routes}/Catalog.js +29 -29
  567. package/dist/internal/routes/Catalog.js.map +1 -0
  568. package/dist/internal/{funding → routes}/Chain.d.ts +10 -10
  569. package/dist/internal/routes/Chain.d.ts.map +1 -0
  570. package/dist/internal/{funding → routes}/Chain.js +3 -3
  571. package/dist/internal/routes/Chain.js.map +1 -0
  572. package/dist/internal/{funding → routes}/Deposit.d.ts +45 -23
  573. package/dist/internal/routes/Deposit.d.ts.map +1 -0
  574. package/dist/internal/{funding → routes}/Deposit.js +60 -27
  575. package/dist/internal/routes/Deposit.js.map +1 -0
  576. package/dist/internal/{funding → routes}/DepositAddress.d.ts +56 -27
  577. package/dist/internal/routes/DepositAddress.d.ts.map +1 -0
  578. package/dist/internal/{funding → routes}/DepositAddress.js +95 -33
  579. package/dist/internal/routes/DepositAddress.js.map +1 -0
  580. package/dist/internal/{funding → routes}/DepositWebhook.d.ts +4 -4
  581. package/dist/internal/routes/DepositWebhook.d.ts.map +1 -0
  582. package/dist/internal/{funding → routes}/DepositWebhook.js +4 -4
  583. package/dist/internal/routes/DepositWebhook.js.map +1 -0
  584. package/dist/internal/routes/Economics.d.ts +25 -0
  585. package/dist/internal/routes/Economics.d.ts.map +1 -0
  586. package/dist/internal/routes/Economics.js +28 -0
  587. package/dist/internal/routes/Economics.js.map +1 -0
  588. package/dist/internal/{funding → routes}/Evidence.d.ts +3 -3
  589. package/dist/internal/routes/Evidence.d.ts.map +1 -0
  590. package/dist/internal/{funding → routes}/Evidence.js +7 -7
  591. package/dist/internal/routes/Evidence.js.map +1 -0
  592. package/dist/internal/routes/Observability.d.ts +154 -0
  593. package/dist/internal/routes/Observability.d.ts.map +1 -0
  594. package/dist/internal/routes/Observability.js +389 -0
  595. package/dist/internal/routes/Observability.js.map +1 -0
  596. package/dist/internal/{funding → routes}/Provider.d.ts +311 -105
  597. package/dist/internal/routes/Provider.d.ts.map +1 -0
  598. package/dist/internal/{funding → routes}/Provider.js +229 -98
  599. package/dist/internal/routes/Provider.js.map +1 -0
  600. package/dist/internal/{funding → routes}/Reconciliation.d.ts +11 -6
  601. package/dist/internal/routes/Reconciliation.d.ts.map +1 -0
  602. package/dist/internal/{funding → routes}/Reconciliation.js +408 -158
  603. package/dist/internal/routes/Reconciliation.js.map +1 -0
  604. package/dist/internal/routes/Route.d.ts +21 -0
  605. package/dist/internal/routes/Route.d.ts.map +1 -0
  606. package/dist/internal/{funding → routes}/Route.js +1 -1
  607. package/dist/internal/routes/Route.js.map +1 -0
  608. package/dist/internal/{funding → routes}/SourceObservation.d.ts +6 -6
  609. package/dist/internal/routes/SourceObservation.d.ts.map +1 -0
  610. package/dist/internal/{funding → routes}/SourceObservation.js +8 -8
  611. package/dist/internal/routes/SourceObservation.js.map +1 -0
  612. package/dist/internal/{funding → routes}/Subsidy.d.ts +48 -3
  613. package/dist/internal/routes/Subsidy.d.ts.map +1 -0
  614. package/dist/internal/{funding → routes}/Subsidy.js +43 -3
  615. package/dist/internal/routes/Subsidy.js.map +1 -0
  616. package/dist/internal/{funding → routes}/Token.d.ts +4 -4
  617. package/dist/internal/routes/Token.d.ts.map +1 -0
  618. package/dist/internal/{funding → routes}/Token.js +3 -3
  619. package/dist/internal/routes/Token.js.map +1 -0
  620. package/dist/internal/{funding → routes}/Transfer.d.ts +241 -46
  621. package/dist/internal/routes/Transfer.d.ts.map +1 -0
  622. package/dist/internal/routes/Transfer.js +728 -0
  623. package/dist/internal/routes/Transfer.js.map +1 -0
  624. package/dist/internal/{funding → routes}/TransferReconciliation.d.ts +13 -10
  625. package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -0
  626. package/dist/internal/routes/TransferReconciliation.js +422 -0
  627. package/dist/internal/routes/TransferReconciliation.js.map +1 -0
  628. package/dist/internal/routes/TransferSubsidy.d.ts +201 -0
  629. package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -0
  630. package/dist/internal/routes/TransferSubsidy.js +256 -0
  631. package/dist/internal/routes/TransferSubsidy.js.map +1 -0
  632. package/dist/internal/{funding → routes}/TransferWebhook.d.ts +5 -5
  633. package/dist/internal/routes/TransferWebhook.d.ts.map +1 -0
  634. package/dist/internal/{funding → routes}/TransferWebhook.js +3 -3
  635. package/dist/internal/routes/TransferWebhook.js.map +1 -0
  636. package/dist/internal/{funding → routes}/index.d.ts +2 -0
  637. package/dist/internal/routes/index.d.ts.map +1 -0
  638. package/dist/internal/{funding → routes}/index.js +2 -0
  639. package/dist/internal/routes/index.js.map +1 -0
  640. package/dist/internal/{funding → routes}/providers/across.d.ts +4 -4
  641. package/dist/internal/routes/providers/across.d.ts.map +1 -0
  642. package/dist/internal/{funding → routes}/providers/across.js +13 -13
  643. package/dist/internal/routes/providers/across.js.map +1 -0
  644. package/dist/internal/{funding → routes}/providers/relay.d.ts +21 -12
  645. package/dist/internal/routes/providers/relay.d.ts.map +1 -0
  646. package/dist/internal/{funding → routes}/providers/relay.js +194 -110
  647. package/dist/internal/routes/providers/relay.js.map +1 -0
  648. package/dist/internal/{funding → routes}/providers/rhino.d.ts +4 -4
  649. package/dist/internal/routes/providers/rhino.d.ts.map +1 -0
  650. package/dist/internal/{funding → routes}/providers/rhino.js +10 -10
  651. package/dist/internal/routes/providers/rhino.js.map +1 -0
  652. package/dist/internal/{funding → routes}/providers/squid.d.ts +4 -4
  653. package/dist/internal/routes/providers/squid.d.ts.map +1 -0
  654. package/dist/internal/{funding → routes}/providers/squid.js +14 -14
  655. package/dist/internal/routes/providers/squid.js.map +1 -0
  656. package/dist/internal/{funding → routes}/providers/stargate.d.ts +46 -18
  657. package/dist/internal/routes/providers/stargate.d.ts.map +1 -0
  658. package/dist/internal/routes/providers/stargate.js +898 -0
  659. package/dist/internal/routes/providers/stargate.js.map +1 -0
  660. package/dist/internal/{funding → routes}/providers/symbiosis.d.ts +4 -4
  661. package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -0
  662. package/dist/internal/{funding → routes}/providers/symbiosis.js +30 -20
  663. package/dist/internal/routes/providers/symbiosis.js.map +1 -0
  664. package/dist/internal/webhookDestinations/slack.js +12 -12
  665. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  666. package/dist/providers/index.d.ts +12 -12
  667. package/dist/providers/index.d.ts.map +1 -1
  668. package/dist/providers/index.js +12 -12
  669. package/dist/providers/index.js.map +1 -1
  670. package/dist/server.d.ts +19 -0
  671. package/dist/server.d.ts.map +1 -0
  672. package/dist/server.js +19 -0
  673. package/dist/server.js.map +1 -0
  674. package/package.json +15 -4
  675. package/src/ApiKey.ts +1 -1
  676. package/src/ApiKeys.test.ts +1033 -3
  677. package/src/ApiKeys.ts +489 -48
  678. package/src/App.test-d.ts +1 -1
  679. package/src/App.test.ts +216 -25
  680. package/src/App.ts +334 -31
  681. package/src/Assets.test.ts +2 -2
  682. package/src/Assets.ts +3 -1
  683. package/src/Auth.test-d.ts +1 -1
  684. package/src/Client.test-d.ts +539 -110
  685. package/src/Handler.ts +0 -1
  686. package/src/OpenApi.contract.test.ts +195 -0
  687. package/src/Scope.ts +17 -5
  688. package/src/Webhooks.ts +1 -1
  689. package/src/admin/App.test.ts +30 -2
  690. package/src/admin/App.ts +42 -6
  691. package/src/admin/apps/api-keys.test.ts +267 -5
  692. package/src/admin/apps/api-keys.ts +100 -50
  693. package/src/admin/apps/earn-vaults.test.ts +158 -0
  694. package/src/admin/apps/earn-vaults.ts +679 -6
  695. package/src/admin/apps/organizations.test.ts +306 -17
  696. package/src/admin/apps/organizations.ts +299 -24
  697. package/src/admin/apps/routes-support.test.ts +200 -0
  698. package/src/admin/apps/routes-support.ts +201 -0
  699. package/src/admin/apps/routes.test.ts +450 -0
  700. package/src/admin/apps/routes.ts +896 -0
  701. package/src/admin/apps/scopes.test.ts +15 -5
  702. package/src/admin/apps/subsidies.test.ts +306 -0
  703. package/src/admin/apps/subsidies.ts +457 -0
  704. package/src/admin/apps/verified-tokens.test.ts +8 -3
  705. package/src/admin/apps/verified-tokens.ts +3 -3
  706. package/src/admin/ui/plugin.ts +1 -2
  707. package/src/admin/ui/src/components/RewardCampaignForm.tsx +1009 -0
  708. package/src/admin/ui/src/lib/config.ts +11 -2
  709. package/src/admin/ui/src/lib/earn-vaults.ts +2 -2
  710. package/src/admin/ui/src/lib/organizations.test.ts +83 -50
  711. package/src/admin/ui/src/lib/organizations.ts +106 -25
  712. package/src/admin/ui/src/lib/query-client.ts +1 -1
  713. package/src/admin/ui/src/lib/reward-campaigns.ts +136 -0
  714. package/src/admin/ui/src/lib/routes.test.ts +88 -0
  715. package/src/admin/ui/src/lib/routes.ts +304 -0
  716. package/src/admin/ui/src/lib/subsidies.ts +75 -0
  717. package/src/admin/ui/src/routeTree.gen.ts +63 -0
  718. package/src/admin/ui/src/routes/__root.tsx +12 -1
  719. package/src/admin/ui/src/routes/api-keys.tsx +196 -84
  720. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +105 -70
  721. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +239 -23
  722. package/src/admin/ui/src/routes/routes.tsx +1615 -0
  723. package/src/admin/ui/src/routes/routes_.support.tsx +248 -0
  724. package/src/admin/ui/src/routes/subsidies.tsx +799 -0
  725. package/src/admin/ui.generated.ts +144 -69
  726. package/src/analytics/Analytics.test.ts +108 -18
  727. package/src/analytics/Analytics.ts +38 -6
  728. package/src/analytics/tables/requestEvents.test.ts +100 -0
  729. package/src/analytics/tables/requestEvents.ts +10 -1
  730. package/src/analytics/tables/routesLifecycleIntervals.ts +140 -0
  731. package/src/apps/Zones.test.ts +15 -0
  732. package/src/apps/Zones.ts +52 -5
  733. package/src/apps/composition.test.ts +68 -60
  734. package/src/apps/data/App.test.ts +154 -2
  735. package/src/apps/data/App.ts +88 -28
  736. package/src/apps/data/routes/activities.test.ts +419 -7
  737. package/src/apps/data/routes/activities.ts +683 -216
  738. package/src/apps/data/routes/balances.test.ts +2 -2
  739. package/src/apps/data/routes/blocks.test.ts +46 -1
  740. package/src/apps/data/routes/coingecko.test.ts +419 -1
  741. package/src/apps/data/routes/coingecko.ts +309 -253
  742. package/src/apps/data/routes/earn.discovery.test.ts +274 -0
  743. package/src/apps/data/routes/earn.test.ts +1294 -17
  744. package/src/apps/data/routes/earn.ts +1838 -321
  745. package/src/apps/data/routes/exchanges.test.ts +12 -15
  746. package/src/apps/data/routes/fee-amm.test.ts +224 -0
  747. package/src/apps/data/routes/fee-amm.ts +191 -160
  748. package/src/apps/data/routes/indexer.test.ts +389 -0
  749. package/src/apps/data/routes/indexer.ts +165 -69
  750. package/src/apps/data/routes/receipts.test.ts +1 -1
  751. package/src/apps/data/routes/rpc.test.ts +352 -8
  752. package/src/apps/data/routes/rpc.ts +407 -219
  753. package/src/apps/data/routes/rpc.zone.test.ts +340 -0
  754. package/src/apps/data/routes/tokenlist.test.ts +285 -0
  755. package/src/apps/data/routes/tokenlist.ts +173 -88
  756. package/src/apps/data/routes/tokens.test.ts +93 -3
  757. package/src/apps/data/routes/tokens.ts +39 -23
  758. package/src/apps/data/routes/transactions.test.ts +1 -1
  759. package/src/apps/data/routes/transfers.test.ts +56 -24
  760. package/src/apps/data/routes/transfers.ts +19 -8
  761. package/src/apps/data/routes/valuation.test.ts +1 -1
  762. package/src/apps/data/routes/verified-tokens.test.ts +345 -0
  763. package/src/apps/data/routes/verified-tokens.ts +99 -29
  764. package/src/apps/data/routes/webhooks.test.ts +645 -83
  765. package/src/apps/data/routes/webhooks.ts +1089 -857
  766. package/src/apps/data/routes/zones.test.ts +148 -71
  767. package/src/apps/data/routes/zones.ts +71 -55
  768. package/src/apps/docs/reference.ts +2 -2
  769. package/src/apps/index.ts +1 -1
  770. package/src/apps/management/App.ts +9 -51
  771. package/src/apps/management/Billing.test.ts +375 -14
  772. package/src/apps/management/Billing.ts +265 -29
  773. package/src/apps/management/routes/api-keys.test.ts +269 -7
  774. package/src/apps/management/routes/api-keys.ts +61 -20
  775. package/src/apps/management/routes/billing.test.ts +477 -15
  776. package/src/apps/management/routes/billing.ts +377 -50
  777. package/src/apps/management/routes/faucet.test.ts +1 -1
  778. package/src/apps/management/routes/faucet.ts +1 -1
  779. package/src/apps/management/routes/invitations.test.ts +270 -3
  780. package/src/apps/management/routes/invitations.ts +185 -15
  781. package/src/apps/management/routes/invoicePreview.test.ts +549 -0
  782. package/src/apps/management/routes/me.test.ts +246 -5
  783. package/src/apps/management/routes/me.ts +7 -2
  784. package/src/apps/management/routes/members.test.ts +108 -0
  785. package/src/apps/management/routes/orgs.test.ts +191 -3
  786. package/src/apps/management/routes/orgs.ts +58 -11
  787. package/src/apps/management/routes/projects.test.ts +33 -3
  788. package/src/apps/management/routes/projects.ts +6 -1
  789. package/src/apps/management/routes/scopes.test.ts +30 -2
  790. package/src/apps/management/routes/scopes.ts +4 -1
  791. package/src/apps/management/routes/usage.test.ts +32 -0
  792. package/src/apps/management/routes/usage.ts +22 -4
  793. package/src/apps/management/routes/webhooks.test.ts +49 -26
  794. package/src/apps/management/routes/webhooks.ts +78 -22
  795. package/src/apps/mcp.test.ts +6 -6
  796. package/src/apps/mcp.ts +7 -7
  797. package/src/apps/mpp/App.ts +37 -7
  798. package/src/apps/mpp/Mpp.test.ts +49 -8
  799. package/src/apps/mpp/Mpp.zone.test.ts +219 -0
  800. package/src/apps/mpp/internal/Adapter.ts +34 -0
  801. package/src/apps/mpp/internal/Policy.test.ts +86 -31
  802. package/src/apps/mpp/internal/Policy.ts +49 -7
  803. package/src/apps/mpp/internal/Sponsorship.test.ts +207 -0
  804. package/src/apps/mpp/internal/Sponsorship.ts +9 -3
  805. package/src/apps/relay/App.test.ts +839 -64
  806. package/src/apps/relay/App.ts +148 -81
  807. package/src/apps/relay/App.zone-billing.test.ts +257 -0
  808. package/src/apps/relay/Sponsorships.test.ts +67 -4
  809. package/src/apps/relay/Sponsorships.ts +233 -32
  810. package/src/apps/relay/Sponsorships.zone.test.ts +102 -0
  811. package/src/apps/{funding → routes}/Access.ts +6 -6
  812. package/src/apps/routes/App.test.ts +165 -0
  813. package/src/apps/routes/App.ts +96 -0
  814. package/src/apps/{funding → routes}/Inventory.ts +2 -2
  815. package/src/apps/routes/index.ts +1 -0
  816. package/src/apps/{funding → routes}/routes/chains.test.ts +66 -66
  817. package/src/apps/{funding → routes}/routes/chains.ts +23 -23
  818. package/src/apps/{funding → routes}/routes/deposit-addresses.test.ts +1358 -458
  819. package/src/apps/{funding → routes}/routes/deposit-addresses.ts +566 -246
  820. package/src/apps/{funding → routes}/routes/providers.test.ts +27 -33
  821. package/src/apps/{funding → routes}/routes/providers.ts +23 -23
  822. package/src/apps/{funding → routes}/routes/quotes.test.ts +298 -41
  823. package/src/apps/{funding → routes}/routes/quotes.ts +57 -40
  824. package/src/apps/{funding → routes}/routes/transfers.test.ts +1100 -353
  825. package/src/apps/routes/routes/transfers.ts +1332 -0
  826. package/src/cli/Cli.test.ts +1 -1
  827. package/src/cli/commands/admin.test.ts +748 -1
  828. package/src/cli/commands/admin.ts +278 -0
  829. package/src/cloudflare.ts +52 -47
  830. package/src/db/Db.test.ts +245 -2
  831. package/src/db/Db.ts +1476 -34
  832. package/src/db/Schema.ts +729 -228
  833. package/src/db/tables/apiKeyAdmissionBootstraps.ts +4 -0
  834. package/src/db/tables/apiKeyAdmissions.ts +218 -0
  835. package/src/db/tables/apiKeyOwnerAdmissions.ts +4 -0
  836. package/src/db/tables/apiKeyOwnerTombstones.ts +81 -0
  837. package/src/db/tables/apiKeyRevocations.test.ts +74 -0
  838. package/src/db/tables/apiKeyRevocations.ts +121 -0
  839. package/src/db/tables/authAccounts.ts +105 -0
  840. package/src/db/tables/authSessions.ts +13 -0
  841. package/src/db/tables/authVerifications.ts +13 -0
  842. package/src/db/tables/invitations.ts +142 -45
  843. package/src/db/tables/memberships.ts +79 -3
  844. package/src/db/tables/organizations.test.ts +690 -13
  845. package/src/db/tables/organizations.ts +139 -55
  846. package/src/db/tables/projects.test.ts +16 -0
  847. package/src/db/tables/projects.ts +13 -3
  848. package/src/db/tables/requestUsage.ts +4 -1
  849. package/src/db/tables/rewardAccounts.ts +178 -0
  850. package/src/db/tables/rewardCampaignDistributors.ts +68 -0
  851. package/src/db/tables/rewardCampaigns.test.ts +1388 -0
  852. package/src/db/tables/rewardCampaigns.ts +647 -0
  853. package/src/db/tables/rewardCredits.ts +110 -0
  854. package/src/db/tables/rewardEligibilityAssociations.test.ts +271 -0
  855. package/src/db/tables/rewardEligibilityAssociations.ts +184 -0
  856. package/src/db/tables/rewardRuns.ts +514 -0
  857. package/src/db/tables/rewardTransactionAttempts.ts +272 -0
  858. package/src/db/tables/routesCatalog.test.ts +185 -0
  859. package/src/db/tables/routesCatalog.ts +163 -0
  860. package/src/db/tables/routesDepositAddresses.test.ts +1048 -0
  861. package/src/db/tables/routesDepositAddresses.ts +1008 -0
  862. package/src/db/tables/{fundingDepositRequestObservations.test.ts → routesDepositRequestObservations.test.ts} +13 -14
  863. package/src/db/tables/{fundingDepositRequestObservations.ts → routesDepositRequestObservations.ts} +10 -10
  864. package/src/db/tables/{fundingDeposits.test.ts → routesDeposits.test.ts} +316 -46
  865. package/src/db/tables/routesDeposits.ts +1062 -0
  866. package/src/db/tables/routesIdempotency.test.ts +796 -0
  867. package/src/db/tables/routesIdempotency.ts +496 -0
  868. package/src/db/tables/routesSubsidies.test.ts +120 -0
  869. package/src/db/tables/routesSubsidies.ts +71 -0
  870. package/src/db/tables/{fundingTransferEvents.ts → routesTransferEvents.ts} +4 -4
  871. package/src/db/tables/routesTransferSubsidies.ts +38 -0
  872. package/src/db/tables/routesTransferSubsidyNonces.ts +124 -0
  873. package/src/db/tables/routesTransferTransactions.test.ts +195 -0
  874. package/src/db/tables/{fundingTransferTransactions.ts → routesTransferTransactions.ts} +63 -18
  875. package/src/db/tables/routesTransfers.test.ts +338 -0
  876. package/src/db/tables/routesTransfers.ts +662 -0
  877. package/src/db/tables/sponsoredTransactions.test.ts +269 -45
  878. package/src/db/tables/sponsoredTransactions.ts +225 -65
  879. package/src/db/tables/sponsorshipAttributions.test.ts +160 -0
  880. package/src/db/tables/sponsorshipAttributions.ts +168 -0
  881. package/src/db/tables/sponsorshipReconciliationCursors.test.ts +115 -0
  882. package/src/db/tables/sponsorshipReconciliationCursors.ts +67 -0
  883. package/src/db/tables/stripeCustomers.test.ts +85 -0
  884. package/src/db/tables/stripeCustomers.ts +24 -0
  885. package/src/db/tables/users.test.ts +129 -0
  886. package/src/db/tables/users.ts +49 -3
  887. package/src/db/tables/webhookQueueEvents.ts +8 -1
  888. package/src/db/tables/webhookSubscriptions.ts +95 -20
  889. package/src/handlers/internal/executionError.ts +3 -4
  890. package/src/handlers/internal/multisig.ts +13 -816
  891. package/src/handlers/internal/sponsorship.ts +26 -10
  892. package/src/handlers/relay.test-d.ts +9 -1
  893. package/src/handlers/relay.test.ts +823 -3
  894. package/src/handlers/relay.ts +127 -107
  895. package/src/index.test-d.ts +7 -0
  896. package/src/index.ts +0 -17
  897. package/src/internal/AssetResponse.ts +26 -0
  898. package/src/internal/Auth.test.ts +1564 -9
  899. package/src/internal/Auth.ts +833 -239
  900. package/src/internal/BetterAuth.test.ts +242 -0
  901. package/src/internal/BetterAuth.ts +349 -0
  902. package/src/internal/Cache.ts +2 -0
  903. package/src/internal/EarnRates.test.ts +17 -0
  904. package/src/internal/EarnRates.ts +124 -31
  905. package/src/internal/EarnSharePrices.test.ts +182 -0
  906. package/src/internal/EarnSharePrices.ts +313 -0
  907. package/src/internal/EarnVaults.test.ts +21 -0
  908. package/src/internal/EarnVaults.ts +33 -11
  909. package/src/internal/Email.ts +54 -0
  910. package/src/internal/Idempotency.test.ts +2 -2
  911. package/src/internal/Log.test-d.ts +8 -7
  912. package/src/internal/Log.test.ts +114 -16
  913. package/src/internal/Log.ts +53 -40
  914. package/src/internal/MetricSink.test.ts +313 -26
  915. package/src/internal/MetricSink.ts +73 -8
  916. package/src/internal/Mpp.test.ts +31 -0
  917. package/src/internal/Mpp.ts +21 -24
  918. package/src/internal/OpenApi.ts +45 -12
  919. package/src/internal/Provider.test.ts +1 -1
  920. package/src/internal/Provider.ts +1 -1
  921. package/src/internal/RateLimit.test.ts +21 -3
  922. package/src/internal/RateLimit.ts +12 -1
  923. package/src/internal/Store.test-d.ts +1 -1
  924. package/src/internal/Store.test.ts +151 -8
  925. package/src/internal/Store.ts +76 -9
  926. package/src/internal/Tidx.test.ts +59 -8
  927. package/src/internal/Tidx.ts +86 -14
  928. package/src/internal/Timing.test.ts +77 -0
  929. package/src/internal/Timing.ts +83 -6
  930. package/src/internal/VercelOidc.test.ts +188 -0
  931. package/src/internal/VercelOidc.ts +193 -0
  932. package/src/internal/VerifiedTokens.test.ts +60 -6
  933. package/src/internal/VerifiedTokens.ts +21 -24
  934. package/src/internal/Viem.batch.test.ts +80 -0
  935. package/src/internal/Viem.test.ts +201 -2
  936. package/src/internal/Viem.ts +221 -25
  937. package/src/internal/Webhooks.test.ts +85 -6
  938. package/src/internal/Webhooks.ts +22 -4
  939. package/src/internal/ZoneRpc.ts +50 -0
  940. package/src/internal/exchange/Provider.test.ts +2 -2
  941. package/src/internal/index.test.ts +3 -1
  942. package/src/internal/index.ts +3 -1
  943. package/src/internal/rewards/Audit.ts +231 -0
  944. package/src/internal/rewards/Campaigns.test.ts +1234 -0
  945. package/src/internal/rewards/Campaigns.ts +1205 -0
  946. package/src/internal/rewards/Credits.ts +165 -0
  947. package/src/internal/rewards/Events.local.test.ts +113 -0
  948. package/src/internal/rewards/Events.test.ts +152 -0
  949. package/src/internal/rewards/Events.ts +468 -0
  950. package/src/internal/rewards/Projection.test.ts +681 -0
  951. package/src/internal/rewards/Projection.ts +987 -0
  952. package/src/internal/rewards/Rates.test.ts +171 -0
  953. package/src/internal/rewards/Rates.ts +110 -0
  954. package/src/internal/rewards/Read.test.ts +501 -0
  955. package/src/internal/rewards/Read.ts +476 -0
  956. package/src/internal/rewards/Runner.checkpoint.test.ts +80 -0
  957. package/src/internal/rewards/Runner.states.test.ts +188 -0
  958. package/src/internal/rewards/Runner.test.ts +90 -0
  959. package/src/internal/rewards/Runner.ts +2229 -0
  960. package/src/internal/rewards/Signer.test.ts +79 -0
  961. package/src/internal/rewards/Signer.ts +180 -0
  962. package/src/internal/rewards/index.ts +14 -0
  963. package/src/internal/{funding → routes}/Action.test.ts +63 -14
  964. package/src/internal/{funding → routes}/Action.ts +35 -8
  965. package/src/internal/{funding → routes}/Catalog.test.ts +3 -3
  966. package/src/internal/{funding → routes}/Catalog.ts +39 -39
  967. package/src/internal/{funding → routes}/Chain.test.ts +1 -1
  968. package/src/internal/{funding → routes}/Chain.ts +11 -11
  969. package/src/internal/{funding → routes}/Deposit.test.ts +1 -1
  970. package/src/internal/{funding → routes}/Deposit.ts +93 -45
  971. package/src/internal/{funding → routes}/DepositAddress.test.ts +11 -3
  972. package/src/internal/{funding → routes}/DepositAddress.ts +132 -53
  973. package/src/internal/{funding → routes}/DepositWebhook.test.ts +61 -18
  974. package/src/internal/{funding → routes}/DepositWebhook.ts +9 -9
  975. package/src/internal/routes/Economics.test.ts +42 -0
  976. package/src/internal/routes/Economics.ts +47 -0
  977. package/src/internal/{funding → routes}/Evidence.test.ts +6 -6
  978. package/src/internal/{funding → routes}/Evidence.ts +9 -9
  979. package/src/internal/routes/Observability.test.ts +636 -0
  980. package/src/internal/routes/Observability.ts +668 -0
  981. package/src/internal/{funding → routes}/Provider.test.ts +489 -153
  982. package/src/internal/{funding → routes}/Provider.ts +489 -186
  983. package/src/internal/{funding → routes}/Reconciliation.local.test.ts +84 -12
  984. package/src/internal/{funding → routes}/Reconciliation.test.ts +1213 -279
  985. package/src/internal/{funding → routes}/Reconciliation.ts +501 -203
  986. package/src/internal/routes/Reconciliation.unattributed.test.ts +250 -0
  987. package/src/internal/{funding → routes}/Route.ts +7 -7
  988. package/src/internal/{funding → routes}/SourceObservation.test.ts +13 -14
  989. package/src/internal/{funding → routes}/SourceObservation.ts +13 -13
  990. package/src/internal/{funding → routes}/Subsidy.local.test.ts +7 -7
  991. package/src/internal/routes/Subsidy.test.ts +169 -0
  992. package/src/internal/{funding → routes}/Subsidy.ts +86 -6
  993. package/src/internal/{funding → routes}/Token.test.ts +1 -1
  994. package/src/internal/{funding → routes}/Token.ts +5 -5
  995. package/src/internal/routes/Transfer.test.ts +1199 -0
  996. package/src/internal/routes/Transfer.ts +1206 -0
  997. package/src/internal/routes/TransferReconciliation.test.ts +731 -0
  998. package/src/internal/routes/TransferReconciliation.ts +527 -0
  999. package/src/internal/routes/TransferSubsidy.test.ts +172 -0
  1000. package/src/internal/routes/TransferSubsidy.ts +447 -0
  1001. package/src/internal/{funding → routes}/TransferWebhook.test.ts +13 -13
  1002. package/src/internal/{funding → routes}/TransferWebhook.ts +7 -7
  1003. package/src/internal/{funding → routes}/index.test.ts +5 -3
  1004. package/src/internal/{funding → routes}/index.ts +2 -0
  1005. package/src/internal/{funding → routes}/providers/across.ts +16 -16
  1006. package/src/internal/{funding → routes}/providers/index.test.ts +157 -44
  1007. package/src/internal/{funding → routes}/providers/relay.test.ts +165 -32
  1008. package/src/internal/{funding → routes}/providers/relay.ts +228 -121
  1009. package/src/internal/{funding → routes}/providers/rhino.ts +16 -16
  1010. package/src/internal/{funding → routes}/providers/squid.ts +17 -17
  1011. package/src/internal/routes/providers/stargate.test.ts +728 -0
  1012. package/src/internal/routes/providers/stargate.ts +1276 -0
  1013. package/src/internal/{funding → routes}/providers/symbiosis.ts +64 -24
  1014. package/src/internal/webhookDestinations/slack.ts +10 -10
  1015. package/src/providers/index.ts +13 -13
  1016. package/src/server.ts +18 -0
  1017. package/dist/apps/funding/Access.d.ts.map +0 -1
  1018. package/dist/apps/funding/Access.js.map +0 -1
  1019. package/dist/apps/funding/App.js.map +0 -1
  1020. package/dist/apps/funding/Inventory.d.ts.map +0 -1
  1021. package/dist/apps/funding/Inventory.js.map +0 -1
  1022. package/dist/apps/funding/ProviderAttempts.d.ts +0 -5
  1023. package/dist/apps/funding/ProviderAttempts.d.ts.map +0 -1
  1024. package/dist/apps/funding/ProviderAttempts.js +0 -24
  1025. package/dist/apps/funding/ProviderAttempts.js.map +0 -1
  1026. package/dist/apps/funding/index.d.ts +0 -2
  1027. package/dist/apps/funding/index.d.ts.map +0 -1
  1028. package/dist/apps/funding/index.js +0 -2
  1029. package/dist/apps/funding/index.js.map +0 -1
  1030. package/dist/apps/funding/routes/chains.d.ts.map +0 -1
  1031. package/dist/apps/funding/routes/chains.js.map +0 -1
  1032. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +0 -1
  1033. package/dist/apps/funding/routes/deposit-addresses.js.map +0 -1
  1034. package/dist/apps/funding/routes/providers.d.ts.map +0 -1
  1035. package/dist/apps/funding/routes/providers.js +0 -57
  1036. package/dist/apps/funding/routes/providers.js.map +0 -1
  1037. package/dist/apps/funding/routes/quotes.d.ts.map +0 -1
  1038. package/dist/apps/funding/routes/quotes.js.map +0 -1
  1039. package/dist/apps/funding/routes/transfers.d.ts.map +0 -1
  1040. package/dist/apps/funding/routes/transfers.js +0 -828
  1041. package/dist/apps/funding/routes/transfers.js.map +0 -1
  1042. package/dist/db/tables/fundingCatalog.d.ts +0 -58
  1043. package/dist/db/tables/fundingCatalog.d.ts.map +0 -1
  1044. package/dist/db/tables/fundingCatalog.js.map +0 -1
  1045. package/dist/db/tables/fundingDepositAddresses.d.ts +0 -192
  1046. package/dist/db/tables/fundingDepositAddresses.d.ts.map +0 -1
  1047. package/dist/db/tables/fundingDepositAddresses.js +0 -235
  1048. package/dist/db/tables/fundingDepositAddresses.js.map +0 -1
  1049. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +0 -1
  1050. package/dist/db/tables/fundingDepositRequestObservations.js.map +0 -1
  1051. package/dist/db/tables/fundingDeposits.d.ts.map +0 -1
  1052. package/dist/db/tables/fundingDeposits.js +0 -295
  1053. package/dist/db/tables/fundingDeposits.js.map +0 -1
  1054. package/dist/db/tables/fundingIdempotency.d.ts +0 -91
  1055. package/dist/db/tables/fundingIdempotency.d.ts.map +0 -1
  1056. package/dist/db/tables/fundingIdempotency.js +0 -103
  1057. package/dist/db/tables/fundingIdempotency.js.map +0 -1
  1058. package/dist/db/tables/fundingTransferEvents.d.ts.map +0 -1
  1059. package/dist/db/tables/fundingTransferEvents.js.map +0 -1
  1060. package/dist/db/tables/fundingTransferTransactions.d.ts +0 -53
  1061. package/dist/db/tables/fundingTransferTransactions.d.ts.map +0 -1
  1062. package/dist/db/tables/fundingTransferTransactions.js.map +0 -1
  1063. package/dist/db/tables/fundingTransfers.d.ts +0 -99
  1064. package/dist/db/tables/fundingTransfers.d.ts.map +0 -1
  1065. package/dist/db/tables/fundingTransfers.js +0 -110
  1066. package/dist/db/tables/fundingTransfers.js.map +0 -1
  1067. package/dist/internal/funding/Action.d.ts.map +0 -1
  1068. package/dist/internal/funding/Action.js.map +0 -1
  1069. package/dist/internal/funding/Catalog.d.ts.map +0 -1
  1070. package/dist/internal/funding/Catalog.js.map +0 -1
  1071. package/dist/internal/funding/Chain.d.ts.map +0 -1
  1072. package/dist/internal/funding/Chain.js.map +0 -1
  1073. package/dist/internal/funding/Deposit.d.ts.map +0 -1
  1074. package/dist/internal/funding/Deposit.js.map +0 -1
  1075. package/dist/internal/funding/DepositAddress.d.ts.map +0 -1
  1076. package/dist/internal/funding/DepositAddress.js.map +0 -1
  1077. package/dist/internal/funding/DepositWebhook.d.ts.map +0 -1
  1078. package/dist/internal/funding/DepositWebhook.js.map +0 -1
  1079. package/dist/internal/funding/Evidence.d.ts.map +0 -1
  1080. package/dist/internal/funding/Evidence.js.map +0 -1
  1081. package/dist/internal/funding/Provider.d.ts.map +0 -1
  1082. package/dist/internal/funding/Provider.js.map +0 -1
  1083. package/dist/internal/funding/Reconciliation.d.ts.map +0 -1
  1084. package/dist/internal/funding/Reconciliation.js.map +0 -1
  1085. package/dist/internal/funding/Route.d.ts +0 -21
  1086. package/dist/internal/funding/Route.d.ts.map +0 -1
  1087. package/dist/internal/funding/Route.js.map +0 -1
  1088. package/dist/internal/funding/SourceObservation.d.ts.map +0 -1
  1089. package/dist/internal/funding/SourceObservation.js.map +0 -1
  1090. package/dist/internal/funding/Subsidy.d.ts.map +0 -1
  1091. package/dist/internal/funding/Subsidy.js.map +0 -1
  1092. package/dist/internal/funding/Token.d.ts.map +0 -1
  1093. package/dist/internal/funding/Token.js.map +0 -1
  1094. package/dist/internal/funding/Transfer.d.ts.map +0 -1
  1095. package/dist/internal/funding/Transfer.js +0 -462
  1096. package/dist/internal/funding/Transfer.js.map +0 -1
  1097. package/dist/internal/funding/TransferReconciliation.d.ts.map +0 -1
  1098. package/dist/internal/funding/TransferReconciliation.js +0 -192
  1099. package/dist/internal/funding/TransferReconciliation.js.map +0 -1
  1100. package/dist/internal/funding/TransferWebhook.d.ts.map +0 -1
  1101. package/dist/internal/funding/TransferWebhook.js.map +0 -1
  1102. package/dist/internal/funding/index.d.ts.map +0 -1
  1103. package/dist/internal/funding/index.js.map +0 -1
  1104. package/dist/internal/funding/providers/across.d.ts.map +0 -1
  1105. package/dist/internal/funding/providers/across.js.map +0 -1
  1106. package/dist/internal/funding/providers/relay.d.ts.map +0 -1
  1107. package/dist/internal/funding/providers/relay.js.map +0 -1
  1108. package/dist/internal/funding/providers/rhino.d.ts.map +0 -1
  1109. package/dist/internal/funding/providers/rhino.js.map +0 -1
  1110. package/dist/internal/funding/providers/squid.d.ts.map +0 -1
  1111. package/dist/internal/funding/providers/squid.js.map +0 -1
  1112. package/dist/internal/funding/providers/stargate.d.ts.map +0 -1
  1113. package/dist/internal/funding/providers/stargate.js +0 -579
  1114. package/dist/internal/funding/providers/stargate.js.map +0 -1
  1115. package/dist/internal/funding/providers/symbiosis.d.ts.map +0 -1
  1116. package/dist/internal/funding/providers/symbiosis.js.map +0 -1
  1117. package/src/apps/funding/App.test.ts +0 -78
  1118. package/src/apps/funding/App.ts +0 -88
  1119. package/src/apps/funding/ProviderAttempts.ts +0 -26
  1120. package/src/apps/funding/index.ts +0 -1
  1121. package/src/apps/funding/routes/transfers.ts +0 -987
  1122. package/src/db/tables/fundingCatalog.test.ts +0 -122
  1123. package/src/db/tables/fundingCatalog.ts +0 -163
  1124. package/src/db/tables/fundingDepositAddresses.test.ts +0 -608
  1125. package/src/db/tables/fundingDepositAddresses.ts +0 -443
  1126. package/src/db/tables/fundingDeposits.ts +0 -572
  1127. package/src/db/tables/fundingIdempotency.test.ts +0 -182
  1128. package/src/db/tables/fundingIdempotency.ts +0 -180
  1129. package/src/db/tables/fundingTransferTransactions.test.ts +0 -112
  1130. package/src/db/tables/fundingTransfers.test.ts +0 -115
  1131. package/src/db/tables/fundingTransfers.ts +0 -189
  1132. package/src/handlers/internal/multisig.test.ts +0 -818
  1133. package/src/internal/funding/Subsidy.test.ts +0 -75
  1134. package/src/internal/funding/Transfer.test.ts +0 -524
  1135. package/src/internal/funding/Transfer.ts +0 -762
  1136. package/src/internal/funding/TransferReconciliation.test.ts +0 -147
  1137. package/src/internal/funding/TransferReconciliation.ts +0 -264
  1138. package/src/internal/funding/providers/stargate.test.ts +0 -328
  1139. package/src/internal/funding/providers/stargate.ts +0 -856
  1140. /package/dist/apps/{funding → routes}/Inventory.d.ts +0 -0
  1141. /package/src/internal/{funding → routes}/Route.test.ts +0 -0
@@ -6,6 +6,9 @@ import {
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: ['0x59d27987bc7cc1521bba5f688a170c65840cbf43'] }),
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: ['0x9a044ae05e5e6290dcf56afd69548565e957a626'] }),
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: [421700001] }),
175
- ),
176
- inputTokens: z
177
- .array(Schema.Address)
178
- .check(
179
- z.describe('Tokens accepted by private deposits.'),
180
- z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
181
- ),
182
- name: z
183
- .string()
184
- .check(z.describe('Private Zone name.'), z.meta({ examples: ['zone-mainnet-internal'] })),
185
- outputTokens: z
186
- .array(Schema.Address)
187
- .check(
188
- z.describe('Tokens supported by private redemptions.'),
189
- z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
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: [421700001] }),
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: [35816964] }),
207
- ),
208
- earnRouter: Schema.Address.check(
209
- z.describe('Parent-chain router for this Zone and Earn vault.'),
210
- z.meta({ examples: ['0x8117e0ba6239b9695f780deb010f72a2fa4bdfb6'] }),
211
- ),
212
- inputTokens: z
213
- .array(Schema.Address)
214
- .check(
215
- z.describe('Tokens accepted by private deposits.'),
216
- z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
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-mainnet-internal'] })),
221
- outputTokens: z
222
- .array(Schema.Address)
223
- .check(
224
- z.describe('Tokens returned by private redemptions.'),
225
- z.meta({ examples: [['0x20c0000000000000000000006fd9a167923ba194']] }),
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,10 +502,10 @@ 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
511
  assetToken: Tokens.schema.Token.check(
@@ -365,7 +610,7 @@ export namespace schema {
365
610
  [
366
611
  {
367
612
  chainId: 421700001,
368
- deploymentBlock: 35816964,
613
+ deploymentBlock: 35822949,
369
614
  earnRouter: '0x8117e0ba6239b9695f780deb010f72a2fa4bdfb6',
370
615
  inputTokens: ['0x20c0000000000000000000006fd9a167923ba194'],
371
616
  name: 'zone-mainnet-internal',
@@ -378,26 +623,32 @@ export namespace schema {
378
623
  })
379
624
 
380
625
  /** One compatible Earn vault resolved from current chain state. */
381
- 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
+ )
382
630
 
383
631
  /** One registry-curated Earn vault. */
384
- export const VerifiedVault = Schema.describe(
385
- z.extend(vault, {
386
- engine: z.extend(engine, { type: engineType }),
387
- slug: z
388
- .string()
389
- .check(
390
- z.describe('Stable curated vault slug.'),
391
- z.meta({ examples: ['btpathusd-earn-h8k2m4n6'] }),
392
- ),
393
- verified: z
394
- .literal(true)
395
- .check(
396
- z.describe('This vault is in the curated earn registry.'),
397
- z.meta({ examples: [true] }),
398
- ),
399
- }),
400
- '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',
401
652
  )
402
653
 
403
654
  const position = z.strictObject({
@@ -406,7 +657,9 @@ export namespace schema {
406
657
  z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] }),
407
658
  ),
408
659
  assetAllowance: Schema.DecimalString.check(
409
- 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
+ ),
410
663
  z.meta({ examples: ['0'] }),
411
664
  ),
412
665
  assetBalance: Schema.DecimalString.check(
@@ -414,14 +667,20 @@ export namespace schema {
414
667
  z.meta({ examples: ['250000000'] }),
415
668
  ),
416
669
  assetToken: Schema.tokenAddress('0x20c000000000000000000000b9537d11c60e8b50').check(
417
- 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] }),
418
675
  ),
419
676
  id: Schema.Address.check(
420
677
  z.describe('Stable resource ID for this vault position, equal to the account address.'),
421
678
  z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] }),
422
679
  ),
423
680
  shareAllowance: Schema.DecimalString.check(
424
- 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
+ ),
425
684
  z.meta({ examples: ['0'] }),
426
685
  ),
427
686
  shareBalance: Schema.DecimalString.check(
@@ -433,12 +692,91 @@ export namespace schema {
433
692
  ),
434
693
  value: Schema.DecimalString.check(
435
694
  z.describe(
436
- 'Asset value of this account’s earn shares at the observation block, 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.',
437
696
  ),
438
697
  z.meta({ examples: ['100000000'] }),
439
698
  ),
699
+ valueToken: Schema.tokenAddress('0x20c000000000000000000000b9537d11c60e8b50').check(
700
+ z.describe('TIP-20 asset denominating `value` on the vault’s parent chain.'),
701
+ ),
440
702
  })
441
703
 
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
+ }),
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'] }),
772
+ ),
773
+ })
774
+ .check(
775
+ z.describe(
776
+ 'Wallet-specific effective APY combining live position amounts with qualification from the latest delivered reward snapshot.',
777
+ ),
778
+ )
779
+
442
780
  const lifetimeCashFlows = z.union([
443
781
  z.strictObject({
444
782
  status: z
@@ -464,41 +802,55 @@ export namespace schema {
464
802
  }),
465
803
  ])
466
804
 
467
- const addressPosition = z.strictObject({
468
- assetAmount: value.check(
469
- z.describe('Current asset value of the held shares, including fees, in `assetToken` units.'),
470
- ),
471
- assetToken: Tokens.schema.Token.check(z.describe('Asset accepted by the vault.')),
472
- id: Schema.Address.check(
473
- z.describe('Stable resource ID for this position, equal to the vault address.'),
474
- z.meta({ examples: ['0x4f94590b636f5878bce585e82379de81e1ec174f'] }),
475
- ),
476
- lifetimeCashFlows: z
477
- .optional(lifetimeCashFlows)
478
- .check(z.describe('Lifetime deposits and completed withdrawals, when requested.')),
479
- shareAmount: value.check(
480
- z.describe('Earn share balance this account holds, in `shareToken` units.'),
481
- ),
482
- shareToken: Tokens.schema.Token.check(z.describe('Token representing vault shares.')),
483
- valuation: z
484
- .optional(z.nullable(Valuation.schema.Value))
485
- .check(
486
- z.describe(
487
- 'The position’s nominal value when `valuation.currency` is requested, or `null` when ' +
488
- 'the asset token is unverified, its display currency has no rate, or rates were unavailable.',
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
+ ),
489
812
  ),
490
- ),
491
- vaultAddress: Schema.Address.check(
492
- z.describe('Earn vault contract address holding this position.'),
493
- z.meta({ examples: ['0x4f94590b636f5878bce585e82379de81e1ec174f'] }),
494
- ),
495
- verified: z
496
- .boolean()
497
- .check(
498
- z.describe('Whether the vault is in the curated earn registry.'),
499
- z.meta({ examples: [true] }),
500
- ),
501
- })
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
+ )
502
854
 
503
855
  const earningsBase = z.strictObject({
504
856
  account: Schema.Address.check(
@@ -703,15 +1055,18 @@ export namespace schema {
703
1055
  export const Query = collectionQuery
704
1056
 
705
1057
  /** Paginated list of compatible deployed Earn vaults. */
706
- export const Response = z
707
- .strictObject({
708
- data: z
709
- .array(Vault)
710
- .check(z.describe('Compatible deployed earn vaults in deployment order.')),
711
- meta: z.optional(meta),
712
- nextCursor: Schema.NextCursor,
713
- })
714
- .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
+ )
715
1070
  }
716
1071
 
717
1072
  /** Schemas for the getVerifiedEarnVaults operation. */
@@ -726,15 +1081,18 @@ export namespace schema {
726
1081
  export const Query = collectionQuery
727
1082
 
728
1083
  /** Paginated list of registry-curated Earn vaults. */
729
- export const Response = z
730
- .strictObject({
731
- data: z
732
- .array(VerifiedVault)
733
- .check(z.describe('Registry-curated earn vaults ordered by vault address.')),
734
- meta: z.optional(meta),
735
- nextCursor: Schema.NextCursor,
736
- })
737
- .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
+ )
738
1096
  }
739
1097
 
740
1098
  /** Schemas for the getEarnAddressPositions operation. */
@@ -753,7 +1111,7 @@ export namespace schema {
753
1111
  cursor: Schema.Cursor,
754
1112
  include: Schema.includeQuery(
755
1113
  z.enum(['earnings']),
756
- 'Comma-separated position fields to include, such as `earnings`.',
1114
+ 'Comma-separated position fields to include, such as `earnings`. Earnings are unavailable when selecting a private Zone.',
757
1115
  ),
758
1116
  limit: Schema.Limit,
759
1117
  'valuation.currency': Schema.Denomination.check(
@@ -788,15 +1146,18 @@ export namespace schema {
788
1146
  .check(z.describe('Page-level valuation rate provenance.'))
789
1147
 
790
1148
  /** Paginated list of the account's earn vault positions. */
791
- export const Response = z
792
- .strictObject({
793
- data: z
794
- .array(addressPosition)
795
- .check(z.describe('Vaults where the account holds earn shares.')),
796
- meta: z.optional(Meta),
797
- nextCursor: Schema.NextCursor,
798
- })
799
- .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
+ )
800
1161
  }
801
1162
 
802
1163
  /** Schemas for the getEarnVault operation. */
@@ -820,9 +1181,103 @@ export namespace schema {
820
1181
  .check(z.describe('Query parameters selecting one earn vault.'))
821
1182
 
822
1183
  /** One compatible Earn vault resolved directly from current chain state. */
823
- export const Response = Schema.describe(
824
- z.extend(vault, { meta: z.optional(meta) }),
825
- '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',
826
1281
  )
827
1282
  }
828
1283
 
@@ -852,6 +1307,10 @@ export namespace schema {
852
1307
  z.meta({ examples: ['2026-07-21T09:00:00.000Z'] }),
853
1308
  ),
854
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
+ ),
855
1314
  })
856
1315
  .check(z.describe('Query parameters selecting an earn vault position.'))
857
1316
 
@@ -875,23 +1334,79 @@ export namespace schema {
875
1334
  .check(z.describe('Indexed block used for a historical Earn position observation.'))
876
1335
 
877
1336
  /** Current or historical asset and Earn-share state for one account in one vault. */
878
- export const Response = Schema.describe(
879
- z.extend(position, {
880
- asOf: z
881
- .optional(asOf)
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())
882
1345
  .check(
883
- z.describe('Requested historical observation time, when supplied.'),
884
- z.meta({ examples: ['2026-07-21T09:00:00.000Z'] }),
1346
+ z.describe('Current payout delivery exception.'),
1347
+ z.meta({ examples: ['transfer_unavailable'] }),
885
1348
  ),
886
- block: z
887
- .optional(Block)
888
- .check(
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(
889
1368
  z.describe(
890
- 'Indexed block used for a historical Earn position observation, when requested.',
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.',
891
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
+ }),
892
1389
  ),
893
- }),
894
- 'Current or historical asset and earn-share balances, allowances, and asset value for an account in one earn vault.',
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',
895
1410
  )
896
1411
  }
897
1412
 
@@ -916,9 +1431,12 @@ export namespace schema {
916
1431
  .check(z.describe('Query parameters selecting an earn vault account.'))
917
1432
 
918
1433
  /** Earnings for one account and period in one Earn vault. */
919
- export const Response = Schema.describe(
920
- earnings,
921
- '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',
922
1440
  )
923
1441
  }
924
1442
  }
@@ -927,6 +1445,113 @@ export namespace schema {
927
1445
  export function earn(options: earn.Options = {}) {
928
1446
  const oracle = options.fx?.oracle ?? FxOracle.ecb()
929
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
+ )
930
1555
  .get(
931
1556
  '/v1/earn/vaults',
932
1557
  Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
@@ -939,7 +1564,12 @@ export function earn(options: earn.Options = {}) {
939
1564
  'Lists compatible earn vaults discovered from deployment events and resolved from current chain state.',
940
1565
  operationId: 'getEarnVaults',
941
1566
  responses: OpenApi.responses({
942
- 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
+ },
943
1573
  success: {
944
1574
  description: 'A page of compatible deployed earn vaults.',
945
1575
  schema: schema.getEarnVaults.Response,
@@ -953,10 +1583,22 @@ export function earn(options: earn.Options = {}) {
953
1583
  Cache.response<App.Environment>({
954
1584
  cacheControl: Cache.policies.state,
955
1585
  key: (c) => vaultCacheKey(c, schema.getEarnVaults.Query),
956
- name: 'tempo-api:earn-vaults:v4',
1586
+ name: 'tempo-api:earn-vaults:v5',
957
1587
  }),
958
1588
  async (c) => {
959
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
+ })
960
1602
  if (OpenApi.narrowValidation)
961
1603
  return OpenApi.validationError(c, {
962
1604
  code: 'query_invalid',
@@ -985,7 +1627,12 @@ export function earn(options: earn.Options = {}) {
985
1627
  'Lists registry-curated earn vaults after resolving their current onchain configuration.',
986
1628
  operationId: 'getVerifiedEarnVaults',
987
1629
  responses: OpenApi.responses({
988
- 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
+ },
989
1636
  success: {
990
1637
  description: 'A page of registry-curated earn vaults.',
991
1638
  schema: schema.getVerifiedEarnVaults.Response,
@@ -1001,6 +1648,18 @@ export function earn(options: earn.Options = {}) {
1001
1648
  }),
1002
1649
  async (c) => {
1003
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
+ })
1004
1663
  if (OpenApi.narrowValidation)
1005
1664
  return OpenApi.validationError(c, {
1006
1665
  code: 'query_invalid',
@@ -1010,7 +1669,11 @@ export function earn(options: earn.Options = {}) {
1010
1669
  const query = c.req.valid('query')
1011
1670
  try {
1012
1671
  const chainId = query.chainId ?? c.get('chainId')
1013
- const data = await listVerifiedVaults(c, { ...query, chainId, oracle })
1672
+ const data = await listVerifiedVaults(c, {
1673
+ ...query,
1674
+ chainId,
1675
+ oracle,
1676
+ })
1014
1677
  return c.json(Response.validated(schema.getVerifiedEarnVaults.Response, data), 200)
1015
1678
  } catch (cause) {
1016
1679
  return Response.upstream(c, cause)
@@ -1030,7 +1693,7 @@ export function earn(options: earn.Options = {}) {
1030
1693
  }),
1031
1694
  OpenApi.describeRoute({
1032
1695
  description:
1033
- 'Lists every earn vault where an account currently holds shares, with current balances and optional lifetime cash flows.',
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.',
1034
1697
  operationId: 'getEarnAddressPositions',
1035
1698
  responses: OpenApi.responses({
1036
1699
  errors: {
@@ -1093,6 +1756,109 @@ export function earn(options: earn.Options = {}) {
1093
1756
  } catch (cause) {
1094
1757
  if (cause instanceof Valuation.UnsupportedDenominationError)
1095
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 })
1761
+ return Response.upstream(c, cause)
1762
+ }
1763
+ },
1764
+ )
1765
+ .get(
1766
+ '/v1/earn/vaults/:vaultId/share-prices',
1767
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
1768
+ OpenApi.validate('param', schema.getEarnVaultSharePrices.Params, {
1769
+ code: 'vault_id_invalid',
1770
+ message: 'Invalid vault id',
1771
+ }),
1772
+ OpenApi.validate('query', schema.getEarnVaultSharePrices.Query, {
1773
+ code: 'query_invalid',
1774
+ message: 'Invalid query parameters',
1775
+ }),
1776
+ OpenApi.describeRoute({
1777
+ description: 'Lists daily historical share prices for one earn vault.',
1778
+ operationId: 'getEarnVaultSharePrices',
1779
+ responses: OpenApi.responses({
1780
+ errors: {
1781
+ 400: {
1782
+ codes: [
1783
+ 'chain_id_invalid',
1784
+ 'chain_id_unsupported',
1785
+ 'query_invalid',
1786
+ 'vault_id_invalid',
1787
+ ],
1788
+ },
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.',
1794
+ },
1795
+ success: {
1796
+ description: 'Daily historical share prices for one earn vault.',
1797
+ schema: schema.getEarnVaultSharePrices.Response,
1798
+ },
1799
+ }),
1800
+ summary: 'List share prices',
1801
+ tags: ['Earn'],
1802
+ }),
1803
+ Cache.response<App.Environment>({
1804
+ cacheControl: Cache.policies.state,
1805
+ key: (c) => Cache.urlKey(c, schema.getEarnVaultSharePrices.Query),
1806
+ name: 'tempo-api:earn-vault-share-prices:v1',
1807
+ }),
1808
+ async (c) => {
1809
+ if (Auth.narrowAccess) return Auth.paidAccessError(c)
1810
+ if (OpenApi.narrowValidation)
1811
+ return Response.error(c, {
1812
+ code: 'chain_id_invalid',
1813
+ message: 'Invalid chain id',
1814
+ status: 400,
1815
+ })
1816
+ if (OpenApi.narrowValidation)
1817
+ return Response.error(c, {
1818
+ code: 'chain_id_unsupported',
1819
+ message: 'Unsupported chain id',
1820
+ status: 400,
1821
+ })
1822
+ if (OpenApi.narrowValidation)
1823
+ return OpenApi.validationError(c, {
1824
+ code: 'vault_id_invalid',
1825
+ message: 'Invalid vault id',
1826
+ })
1827
+ if (OpenApi.narrowValidation)
1828
+ return OpenApi.validationError(c, {
1829
+ code: 'query_invalid',
1830
+ message: 'Invalid query parameters',
1831
+ })
1832
+
1833
+ const { vaultId } = c.req.valid('param')
1834
+ const query = c.req.valid('query')
1835
+ const chainId = query.chainId ?? c.get('chainId')
1836
+ try {
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)
1854
+ } catch (cause) {
1855
+ if (
1856
+ cause instanceof EarnSharePrices.IndexedBlockNotFoundError ||
1857
+ cause instanceof EarnSharePrices.IndexedCoverageError
1858
+ )
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)
1096
1862
  return Response.upstream(c, cause)
1097
1863
  }
1098
1864
  },
@@ -1110,7 +1876,7 @@ export function earn(options: earn.Options = {}) {
1110
1876
  }),
1111
1877
  OpenApi.describeRoute({
1112
1878
  description:
1113
- 'Gets an account’s asset and earn-share balances, allowances, and asset value for one earn vault, optionally at an ISO 8601 timestamp.',
1879
+ 'Gets an account’s direct or Zone-routed balances and asset value for one earn vault, optionally at an ISO 8601 timestamp.',
1114
1880
  operationId: 'getEarnVaultPosition',
1115
1881
  responses: OpenApi.responses({
1116
1882
  errors: {
@@ -1140,7 +1906,7 @@ export function earn(options: earn.Options = {}) {
1140
1906
  Cache.response<App.Environment>({
1141
1907
  cacheControl: Cache.policies.noStore,
1142
1908
  key: (c) => Cache.urlKey(c, schema.getEarnVaultPosition.Query),
1143
- name: 'tempo-api:earn-vault-positions:v2',
1909
+ name: 'tempo-api:earn-vault-positions:v3',
1144
1910
  }),
1145
1911
  async (c) => {
1146
1912
  if (Auth.narrowAccess) return Auth.paidAccessError(c)
@@ -1170,85 +1936,133 @@ export function earn(options: earn.Options = {}) {
1170
1936
  const { address, vaultId } = c.req.valid('param')
1171
1937
  const query = c.req.valid('query')
1172
1938
  const chainId = query.chainId ?? c.get('chainId')
1173
- const client = c.get('getClient')(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)
1174
1946
  try {
1175
- const block =
1176
- query.asOf === undefined
1177
- ? undefined
1178
- : await getIndexedBlockAtOrBefore(c.get('getTidx')(chainId), query.asOf)
1179
- try {
1180
- const position = await client.earn.getPosition({
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
1971
+ return c.json(
1972
+ Response.validated(schema.getEarnVaultPosition.Response, {
1181
1973
  account: address,
1182
- ...(block ? { blockNumber: BigInt(block.number) } : {}),
1183
- vault: vaultId,
1974
+ ...(query.include.includes('apy') ? { apy } : {}),
1975
+ assetAllowance: position.assetAllowance.toString(),
1976
+ assetBalance: position.assetBalance.toString(),
1977
+ assetToken: position.assetToken,
1978
+ chainId,
1979
+ asOf: query.asOf,
1980
+ block: position.block,
1981
+ id: address,
1982
+ ...(rewards
1983
+ ? {
1984
+ rewards: query.include.includes('rewards.proof')
1985
+ ? rewards
1986
+ : { ...rewards, proof: undefined },
1987
+ }
1988
+ : {}),
1989
+ shareAllowance: position.shareAllowance.toString(),
1990
+ shareBalance: position.shareBalance.toString(),
1991
+ shareToken: position.shareToken,
1992
+ value: position.value.toString(),
1993
+ valueToken: position.valueToken,
1994
+ }),
1995
+ 200,
1996
+ )
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,
1184
2007
  })
1185
- return c.json(
1186
- Response.validated(schema.getEarnVaultPosition.Response, {
1187
- account: address,
1188
- assetAllowance: position.assetAllowance.toString(),
1189
- assetBalance: position.assetBalance.toString(),
1190
- assetToken: position.assetToken,
1191
- asOf: query.asOf,
1192
- block,
1193
- id: address,
1194
- shareAllowance: position.shareAllowance.toString(),
1195
- shareBalance: position.shareBalance.toString(),
1196
- shareToken: position.shareToken,
1197
- value: position.value.toString(),
1198
- }),
1199
- 200,
1200
- )
1201
- } catch (cause) {
1202
- if (block) {
1203
- try {
1204
- // These immutable bindings establish historical vault identity
1205
- // without repeating fallible token or redemption subreads.
1206
- const [asset, shareToken] = await Promise.all([
1207
- client.readContract({
1208
- abi: Abis.earnVault,
1209
- address: vaultId,
1210
- blockNumber: BigInt(block.number),
1211
- functionName: 'asset',
1212
- }),
1213
- client.readContract({
1214
- abi: Abis.earnVault,
1215
- address: vaultId,
1216
- blockNumber: BigInt(block.number),
1217
- functionName: 'earnShare',
1218
- }),
1219
- ])
1220
- if (asset === zeroAddress || shareToken === zeroAddress)
1221
- return Response.error(c, {
1222
- code: 'earn_vault_not_found',
1223
- message: 'Earn vault not found',
1224
- status: 404,
1225
- })
1226
- } catch (identityCause) {
1227
- if (isIncompatible(identityCause))
1228
- return Response.error(c, {
1229
- code: 'earn_vault_not_found',
1230
- message: 'Earn vault not found',
1231
- status: 404,
1232
- })
1233
- return Response.upstream(c, identityCause)
1234
- }
1235
- return Response.upstream(c, cause)
1236
- }
2008
+ const zone = c.get('zones').get(chainId)
2009
+ if (query.asOf && !zone) {
1237
2010
  try {
1238
- await resolveVaultDiscovery(c, { chainId, include: [], vaultAddress: vaultId })
1239
- } catch (vaultCause) {
1240
- if (isIncompatible(vaultCause))
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))
1241
2046
  return Response.error(c, {
1242
2047
  code: 'earn_vault_not_found',
1243
2048
  message: 'Earn vault not found',
1244
2049
  status: 404,
1245
2050
  })
2051
+ return Response.upstream(c, identityCause)
1246
2052
  }
1247
2053
  return Response.upstream(c, cause)
1248
2054
  }
1249
- } catch (cause) {
1250
- if (cause instanceof IndexedBlockNotFoundError || cause instanceof IndexedCoverageError)
1251
- return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
2055
+ if (zone) return Response.upstream(c, cause)
2056
+ try {
2057
+ await resolveVaultDiscovery(c, { chainId, include: [], vaultAddress: vaultId })
2058
+ } catch (vaultCause) {
2059
+ if (isIncompatible(vaultCause))
2060
+ return Response.error(c, {
2061
+ code: 'earn_vault_not_found',
2062
+ message: 'Earn vault not found',
2063
+ status: 404,
2064
+ })
2065
+ }
1252
2066
  return Response.upstream(c, cause)
1253
2067
  }
1254
2068
  },
@@ -1365,6 +2179,14 @@ export function earn(options: earn.Options = {}) {
1365
2179
  operationId: 'getEarnVault',
1366
2180
  responses: OpenApi.responses({
1367
2181
  errors: {
2182
+ 400: {
2183
+ codes: [
2184
+ 'chain_id_invalid',
2185
+ 'chain_id_unsupported',
2186
+ 'query_invalid',
2187
+ 'vault_id_invalid',
2188
+ ],
2189
+ },
1368
2190
  404: {
1369
2191
  codes: ['earn_vault_not_found'],
1370
2192
  description: 'No compatible earn vault was found at this address.',
@@ -1386,6 +2208,18 @@ export function earn(options: earn.Options = {}) {
1386
2208
  }),
1387
2209
  async (c) => {
1388
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
+ })
1389
2223
  if (OpenApi.narrowValidation)
1390
2224
  return OpenApi.validationError(c, {
1391
2225
  code: 'vault_id_invalid',
@@ -1437,7 +2271,10 @@ export async function getVaultEarnings(
1437
2271
  ) {
1438
2272
  const client = c.get('getClient')(options.chainId)
1439
2273
  const tidx = c.get('getTidx')(options.chainId)
1440
- const block = await getLatestIndexedBlock(tidx)
2274
+ const [block, rewardTransfer] = await Promise.all([
2275
+ getLatestIndexedBlock(tidx),
2276
+ getRewardTransferPredicate(c, options),
2277
+ ])
1441
2278
  const blockNumber = block.number
1442
2279
  if (options.period === '30d')
1443
2280
  return getWindowVaultEarnings(c, {
@@ -1445,6 +2282,7 @@ export async function getVaultEarnings(
1445
2282
  blockNumber,
1446
2283
  chainId: options.chainId,
1447
2284
  period: options.period,
2285
+ rewardTransfer,
1448
2286
  timestamp: block.timestamp,
1449
2287
  vault: options.vault,
1450
2288
  })
@@ -1465,6 +2303,7 @@ export async function getVaultEarnings(
1465
2303
  const activeCostBasis = await getActiveCostBasis(tidx, {
1466
2304
  account: options.account,
1467
2305
  blockNumber,
2306
+ rewardTransfer,
1468
2307
  shareBalance: current.shareBalance,
1469
2308
  shareToken: current.shareToken,
1470
2309
  vault: options.vault,
@@ -1488,6 +2327,7 @@ export async function getVaultEarnings(
1488
2327
  const externalShareTransfer = await hasExternalShareTransfer(tidx, {
1489
2328
  account: options.account,
1490
2329
  blockNumber,
2330
+ rewardTransfer,
1491
2331
  shareToken: current.shareToken,
1492
2332
  startBlockNumber: beforeFirstBlockNumber,
1493
2333
  vault: options.vault,
@@ -1536,6 +2376,7 @@ async function getWindowVaultEarnings(
1536
2376
  hasExternalShareTransfer(tidx, {
1537
2377
  account: options.account,
1538
2378
  blockNumber: options.blockNumber,
2379
+ rewardTransfer: options.rewardTransfer,
1539
2380
  shareToken: current.shareToken,
1540
2381
  startBlockNumber,
1541
2382
  vault: options.vault,
@@ -1600,6 +2441,8 @@ declare namespace getWindowVaultEarnings {
1600
2441
  chainId: z.output<typeof Schema.ChainId>
1601
2442
  /** Trailing earnings period. */
1602
2443
  period: '30d'
2444
+ /** SQL predicate recognizing paid rewards in the share-transfer history. */
2445
+ rewardTransfer: string
1603
2446
  /** Latest indexed block timestamp. */
1604
2447
  timestamp: string
1605
2448
  /** Earn vault contract address. */
@@ -1981,6 +2824,39 @@ declare namespace getWindowShareState {
1981
2824
  }
1982
2825
  }
1983
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
+
1984
2860
  /** Returns whether an account sent or received Earn shares outside known vault flows. */
1985
2861
  async function hasExternalShareTransfer(
1986
2862
  tidx: Tidx.Client,
@@ -1995,6 +2871,7 @@ async function hasExternalShareTransfer(
1995
2871
  SELECT count()
1996
2872
  FROM token_transfers
1997
2873
  WHERE token = '${options.shareToken.toLowerCase()}'
2874
+ AND toUInt256(amount) > 0
1998
2875
  AND block_num > ${options.startBlockNumber}
1999
2876
  AND block_num <= ${options.blockNumber}
2000
2877
  AND (
@@ -2003,6 +2880,7 @@ async function hasExternalShareTransfer(
2003
2880
  "to" = '${options.account}'
2004
2881
  AND "from" != '${zeroAddress}'
2005
2882
  AND "from" != '${options.vault}'
2883
+ AND NOT (${options.rewardTransfer})
2006
2884
  )
2007
2885
  )
2008
2886
  ) + (
@@ -2011,6 +2889,7 @@ async function hasExternalShareTransfer(
2011
2889
  SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
2012
2890
  FROM token_transfers
2013
2891
  WHERE token = '${options.shareToken.toLowerCase()}'
2892
+ AND toUInt256(amount) > 0
2014
2893
  AND "from" = '${options.account}'
2015
2894
  AND "to" = '${options.vault}'
2016
2895
  AND block_num > ${options.startBlockNumber}
@@ -2051,6 +2930,7 @@ async function hasExternalShareTransfer(
2051
2930
  SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
2052
2931
  FROM token_transfers
2053
2932
  WHERE token = '${options.shareToken.toLowerCase()}'
2933
+ AND toUInt256(amount) > 0
2054
2934
  AND "from" = '${options.vault}'
2055
2935
  AND "to" = '${options.account}'
2056
2936
  AND block_num > ${options.startBlockNumber}
@@ -2074,6 +2954,7 @@ async function hasExternalShareTransfer(
2074
2954
  redeemCancelledSignature,
2075
2955
  redeemedSignature,
2076
2956
  redeemRequestedSignature,
2957
+ unspentReturnedSignature,
2077
2958
  withdrewExactSignature,
2078
2959
  ],
2079
2960
  })
@@ -2086,6 +2967,7 @@ declare namespace hasExternalShareTransfer {
2086
2967
  type Options = {
2087
2968
  account: z.output<typeof Schema.Address>
2088
2969
  blockNumber: number
2970
+ rewardTransfer: string
2089
2971
  shareToken: z.output<typeof Schema.Address>
2090
2972
  startBlockNumber: number
2091
2973
  vault: z.output<typeof Schema.Address>
@@ -2111,6 +2993,7 @@ export function calculateActiveCostBasis(options: calculateActiveCostBasis.Optio
2111
2993
  }
2112
2994
 
2113
2995
  for (const action of options.actions) {
2996
+ if (action.shares === 0n) continue
2114
2997
  if (action.kind === 'deposit') {
2115
2998
  if (costBasis !== undefined) costBasis += action.assets
2116
2999
  shares += action.shares
@@ -2273,7 +3156,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2273
3156
  AND block_num <= ${options.blockNumber}
2274
3157
  UNION ALL
2275
3158
  SELECT
2276
- 'unknown' AS kind,
3159
+ if(${options.rewardTransfer}, 'deposit', 'unknown') AS kind,
2277
3160
  block_num,
2278
3161
  log_idx,
2279
3162
  '' AS request_id,
@@ -2281,6 +3164,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2281
3164
  '0' AS assets
2282
3165
  FROM token_transfers
2283
3166
  WHERE token = '${options.shareToken.toLowerCase()}'
3167
+ AND toUInt256(amount) > 0
2284
3168
  AND "to" = '${options.account}'
2285
3169
  AND "from" != '${zeroAddress}'
2286
3170
  AND "from" != '${options.vault}'
@@ -2295,6 +3179,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2295
3179
  '0' AS assets
2296
3180
  FROM token_transfers
2297
3181
  WHERE token = '${options.shareToken.toLowerCase()}'
3182
+ AND toUInt256(amount) > 0
2298
3183
  AND "from" = '${options.account}'
2299
3184
  AND "to" != '${options.vault}'
2300
3185
  AND block_num <= ${options.blockNumber}
@@ -2306,6 +3191,7 @@ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis
2306
3191
  redeemCancelledSignature,
2307
3192
  redeemRequestedSignature,
2308
3193
  redeemedSignature,
3194
+ unspentReturnedSignature,
2309
3195
  venueSharesDepositedSignature,
2310
3196
  withdrewExactSignature,
2311
3197
  ],
@@ -2355,6 +3241,7 @@ declare namespace getActiveCostBasis {
2355
3241
  type Options = {
2356
3242
  account: z.output<typeof Schema.Address>
2357
3243
  blockNumber: number
3244
+ rewardTransfer: string
2358
3245
  shareBalance: bigint
2359
3246
  shareToken: z.output<typeof Schema.Address>
2360
3247
  vault: z.output<typeof Schema.Address>
@@ -2458,7 +3345,7 @@ export async function listVaults(c: Context<App.Environment>, options: listVault
2458
3345
  apyByVault.has(vaultAddress.toLowerCase()) ? [] : [vaultAddress],
2459
3346
  )
2460
3347
  : []
2461
- const [missingApy, rates, tokens] = await Promise.all([
3348
+ const [missingApy, rates, rewards, tokens] = await Promise.all([
2462
3349
  missingApyVaults.length > 0
2463
3350
  ? resolvePageApy(c, {
2464
3351
  chainId: options.chainId,
@@ -2468,12 +3355,27 @@ export async function listVaults(c: Context<App.Environment>, options: listVault
2468
3355
  })
2469
3356
  : undefined,
2470
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
+ }),
2471
3363
  resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
2472
3364
  ])
2473
3365
  for (const [vault, apy] of missingApy ?? []) apyByVault.set(vault, apy)
2474
3366
  const apy = include.includes('apy') ? apyByVault : undefined
2475
3367
  const data = await Promise.all(
2476
- 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
+ ),
2477
3379
  )
2478
3380
  const last = matches[options.limit - 1]
2479
3381
  const nextPosition =
@@ -2557,7 +3459,7 @@ export async function listVerifiedVaults(
2557
3459
 
2558
3460
  const page = matches.slice(0, options.limit)
2559
3461
  const discoveries = page.map(({ discovery }) => discovery)
2560
- const [apy, rates, tokens] = await Promise.all([
3462
+ const [apy, rates, rewards, tokens] = await Promise.all([
2561
3463
  resolvePageApy(c, {
2562
3464
  chainId: options.chainId,
2563
3465
  include,
@@ -2565,13 +3467,26 @@ export async function listVerifiedVaults(
2565
3467
  window: options['apy.window'],
2566
3468
  }),
2567
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
+ }),
2568
3475
  resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
2569
3476
  ])
2570
3477
  const data = await Promise.all(
2571
3478
  page.map(async (match) =>
2572
3479
  Response.validated(
2573
3480
  schema.VerifiedVault,
2574
- await resolveVault(c, { ...match, apy, include, rates, snapshot, tokens }),
3481
+ await resolveVault(c, {
3482
+ ...match,
3483
+ apy,
3484
+ include,
3485
+ rates,
3486
+ rewards: rewards?.get(match.discovery.vaultAddress),
3487
+ snapshot,
3488
+ tokens,
3489
+ }),
2575
3490
  ),
2576
3491
  ),
2577
3492
  )
@@ -2600,15 +3515,371 @@ export declare namespace listVerifiedVaults {
2600
3515
  }
2601
3516
  }
2602
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,
3831
+ )
3832
+ if (!route || !privateAsset || !portal.success) throw new EarnVaults.NotFoundError()
3833
+ return {
3834
+ deploymentBlock: route.deploymentBlock,
3835
+ portal: portal.data,
3836
+ privateAsset,
3837
+ shareToken: discovery.shareToken.address,
3838
+ sourceChainId: zone.sourceId,
3839
+ vaultAsset: discovery.assetToken.address,
3840
+ }
3841
+ }
3842
+
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>
3848
+ }
3849
+
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>
3858
+ }
3859
+ }
3860
+
2603
3861
  /** Lists every vault where an account holds earn shares, ordered by vault address. */
2604
3862
  export async function listPositions(c: Context<App.Environment>, options: listPositions.Options) {
2605
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()
2606
3868
  const db = Db.get(c.get('dbCached'))
2607
- 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
+ )
2608
3878
  // The verified page needs no index: the registry is the candidate set.
2609
- const heldPromise = options.verified
2610
- ? undefined
2611
- : 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 })
2612
3883
  // Observing these branches prevents unhandled rejections while the
2613
3884
  // denomination validates; the later await still propagates them.
2614
3885
  void recordsPromise.catch(() => undefined)
@@ -2647,13 +3918,14 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2647
3918
  batch.map(async (vault) => {
2648
3919
  const record = registry.get(vault)
2649
3920
  try {
2650
- const position = await readPosition(c, {
2651
- address: options.address,
3921
+ const position = await getVaultPosition(c, {
3922
+ account: options.address,
2652
3923
  chainId: options.chainId,
2653
- vaultAddress: Schema.Address.parse(vault),
3924
+ vault: Schema.Address.parse(vault),
2654
3925
  })
2655
3926
  return { position, record, vaultAddress: vault }
2656
3927
  } catch (cause) {
3928
+ if (cause instanceof EarnVaults.NotFoundError) return undefined
2657
3929
  if (!record && isIncompatible(cause)) return undefined
2658
3930
  throw cause
2659
3931
  }
@@ -2668,10 +3940,10 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2668
3940
 
2669
3941
  const page = matches.slice(0, options.limit)
2670
3942
  const indexedBlock =
2671
- page.length > 0 && options.include.includes('earnings')
3943
+ !zone && page.length > 0 && options.include.includes('earnings')
2672
3944
  ? await getLatestIndexedBlock(c.get('getTidx')(options.chainId))
2673
3945
  : undefined
2674
- const [cashFlows, tokens, snapshot] = await Promise.all([
3946
+ const [cashFlows, tokens, valueTokens, snapshot] = await Promise.all([
2675
3947
  indexedBlock
2676
3948
  ? getVaultLifetimeCashFlows(c.get('getTidx')(options.chainId), {
2677
3949
  account: options.address,
@@ -2694,13 +3966,20 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2694
3966
  )
2695
3967
  return new Map(groups.flatMap((group) => [...group]))
2696
3968
  })(),
2697
- 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,
2698
3977
  ])
2699
3978
  const rates =
2700
3979
  denomination && snapshot
2701
3980
  ? await Valuation.ratesFor(c, {
2702
3981
  currencies: page.flatMap(({ position }) => {
2703
- const currency = snapshot.byAddress.get(position.assetToken.toLowerCase())?.currency
3982
+ const currency = snapshot.byAddress.get(position.valueToken.toLowerCase())?.currency
2704
3983
  return currency === undefined ? [] : [currency]
2705
3984
  }),
2706
3985
  denomination,
@@ -2720,11 +3999,13 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2720
3999
  data: page.map((match) =>
2721
4000
  serializePosition({
2722
4001
  cashFlows: cashFlows?.get(match.vaultAddress),
4002
+ chainId: options.chainId,
2723
4003
  denomination,
2724
4004
  position: match.position,
2725
4005
  rates,
2726
4006
  snapshot,
2727
4007
  tokens,
4008
+ valueTokens,
2728
4009
  vaultAddress: match.vaultAddress,
2729
4010
  verified: match.record !== undefined,
2730
4011
  }),
@@ -2737,7 +4018,7 @@ export async function listPositions(c: Context<App.Environment>, options: listPo
2737
4018
  export declare namespace listPositions {
2738
4019
  /** One candidate vault holding a non-zero position. */
2739
4020
  type Match = {
2740
- position: Actions.earn.getPosition.ReturnValue
4021
+ position: getVaultPosition.ReturnType
2741
4022
  record?: RegistryRecord | undefined
2742
4023
  vaultAddress: string
2743
4024
  }
@@ -2757,12 +4038,11 @@ async function queryHeldVaults(
2757
4038
  c: Context<App.Environment>,
2758
4039
  options: queryHeldVaults.Options,
2759
4040
  ): Promise<readonly string[]> {
4041
+ const factories = deploymentFactories[options.chainId] ?? []
4042
+ if (factories.length === 0) return []
2760
4043
  const tidx = c.get('getTidx')(options.chainId)
2761
- // Each vault's newest deployment binds its current share token; joining it
2762
- // against the account's holder deltas server-side turns holdings into vault
2763
- // candidates in one round trip, with no literal list to overflow the request.
2764
- // Earn shares are factory-minted TIP-20s, so the `0x20c0` prefix bounds the
2765
- // 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.
2766
4046
  const result = await Timing.time(c, 'earn_positions_discovery', () =>
2767
4047
  tidx.fetch({
2768
4048
  chainId: options.chainId,
@@ -2778,6 +4058,7 @@ async function queryHeldVaults(
2778
4058
  ORDER BY block_num DESC, log_idx DESC
2779
4059
  ) AS deployment_rank
2780
4060
  FROM EarnStackDeployed
4061
+ WHERE address IN (${factories.map((address) => `'${address}'`).join(', ')})
2781
4062
  )
2782
4063
  WHERE deployment_rank = 1 AND lower("earnShare") IN (
2783
4064
  SELECT DISTINCT lower(token)
@@ -2804,34 +4085,17 @@ declare namespace queryHeldVaults {
2804
4085
  }
2805
4086
  }
2806
4087
 
2807
- /** Reads one account's live position in one vault. */
2808
- function readPosition(
2809
- c: Context<App.Environment>,
2810
- options: readPosition.Options,
2811
- ): Promise<Actions.earn.getPosition.ReturnValue> {
2812
- return c
2813
- .get('getClient')(options.chainId)
2814
- .earn.getPosition({ account: options.address, vault: options.vaultAddress })
2815
- }
2816
-
2817
- declare namespace readPosition {
2818
- /** Inputs selecting one vault position. */
2819
- type Options = {
2820
- address: z.output<typeof Schema.Address>
2821
- chainId: z.output<typeof Schema.ChainId>
2822
- vaultAddress: z.output<typeof Schema.Address>
2823
- }
2824
- }
2825
-
2826
4088
  /** Serializes one live position into the address-scoped row contract. */
2827
4089
  function serializePosition(options: serializePosition.Options) {
2828
4090
  const { position, tokens } = options
2829
4091
  const assetToken = getToken(tokens, position.assetToken)
2830
4092
  const shareToken = getToken(tokens, position.shareToken)
4093
+ const valueToken = getToken(options.valueTokens ?? tokens, position.valueToken)
2831
4094
  const vaultAddress = Schema.Address.parse(options.vaultAddress.toLowerCase())
2832
4095
  return {
2833
- assetAmount: valueFor(position.value, assetToken),
4096
+ assetAmount: valueFor(position.value, valueToken),
2834
4097
  assetToken,
4098
+ chainId: options.chainId,
2835
4099
  id: vaultAddress,
2836
4100
  ...(options.cashFlows ? { lifetimeCashFlows: options.cashFlows } : {}),
2837
4101
  shareAmount: valueFor(position.shareBalance, shareToken),
@@ -2842,10 +4106,11 @@ function serializePosition(options: serializePosition.Options) {
2842
4106
  amount: position.value,
2843
4107
  denomination: options.denomination,
2844
4108
  rates: options.rates,
2845
- token: options.snapshot?.byAddress.get(assetToken.address),
4109
+ token: options.snapshot?.byAddress.get(valueToken.address),
2846
4110
  }),
2847
4111
  }
2848
4112
  : {}),
4113
+ valueToken,
2849
4114
  vaultAddress,
2850
4115
  verified: options.verified,
2851
4116
  }
@@ -2858,14 +4123,16 @@ declare namespace serializePosition {
2858
4123
  cashFlows?: z.output<
2859
4124
  typeof schema.getEarnAddressPositions.Response
2860
4125
  >['data'][number]['lifetimeCashFlows']
4126
+ chainId: z.output<typeof Schema.ChainId>
2861
4127
  /** Currency the row valuation is denominated in (uppercase). */
2862
4128
  denomination?: string | undefined
2863
- position: Actions.earn.getPosition.ReturnValue
4129
+ position: getVaultPosition.ReturnType
2864
4130
  /** Consulted FX rate set, or undefined when unavailable or unnecessary. */
2865
4131
  rates: Awaited<ReturnType<typeof Valuation.ratesFor>>
2866
4132
  /** Curated token snapshot backing valuation, present when requested. */
2867
4133
  snapshot?: VerifiedTokens.Snapshot | undefined
2868
4134
  tokens: Tokens.resolveTokens.ReturnType
4135
+ valueTokens?: Tokens.resolveTokens.ReturnType | undefined
2869
4136
  vaultAddress: string
2870
4137
  verified: boolean
2871
4138
  }
@@ -3054,7 +4321,7 @@ export async function getVault(c: Context<App.Environment>, options: getVault.Op
3054
4321
  record,
3055
4322
  })
3056
4323
  const discoveries = [discovery]
3057
- const [apy, rates, tokens] = await Promise.all([
4324
+ const [apy, rates, rewards, tokens] = await Promise.all([
3058
4325
  resolvePageApy(c, {
3059
4326
  chainId: options.chainId,
3060
4327
  include,
@@ -3062,6 +4329,11 @@ export async function getVault(c: Context<App.Environment>, options: getVault.Op
3062
4329
  window: options['apy.window'],
3063
4330
  }),
3064
4331
  resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
4332
+ resolveVaultRewards(c, {
4333
+ chainId: options.chainId,
4334
+ include,
4335
+ vault: discovery.vaultAddress,
4336
+ }),
3065
4337
  resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
3066
4338
  ])
3067
4339
  return {
@@ -3071,6 +4343,7 @@ export async function getVault(c: Context<App.Environment>, options: getVault.Op
3071
4343
  include,
3072
4344
  rates,
3073
4345
  record,
4346
+ rewards,
3074
4347
  snapshot,
3075
4348
  tokens,
3076
4349
  zoneRoutes,
@@ -3148,29 +4421,36 @@ async function resolveVaultDiscovery(
3148
4421
  c: Context<App.Environment>,
3149
4422
  options: resolveVaultDiscovery.Options,
3150
4423
  ) {
3151
- return Store.memoize(
3152
- () =>
3153
- Timing.time(c, 'earn_vault_discovery', () =>
3154
- EarnVaults.resolve({
3155
- getClient: c.get('getClient'),
3156
- include: options.include,
3157
- input: {
3158
- chainId: options.chainId,
3159
- vaultAddress: options.vaultAddress,
3160
- },
3161
- zones: [...c.get('zones').values()],
3162
- }),
3163
- ),
3164
- {
3165
- key: `earn-vault:v3:${options.chainId}:${options.vaultAddress.toLowerCase()}:include:${options.include.join(',') || 'base'}`,
3166
- store: c.get('store'),
3167
- // Matches the routes' `state` policy `max-age` so the two cache layers do
3168
- // not compound staleness on live vault state.
3169
- ttl: Ttl.seconds(30),
3170
- },
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
+ }),
3171
4447
  )
3172
4448
  }
3173
4449
 
4450
+ declare namespace readVaultDiscovery {
4451
+ type Options = resolveVaultDiscovery.Options
4452
+ }
4453
+
3174
4454
  declare namespace resolveVaultDiscovery {
3175
4455
  type Options = {
3176
4456
  chainId: z.output<typeof Schema.ChainId>
@@ -3233,6 +4513,129 @@ declare namespace resolveVaultRoutes {
3233
4513
  }
3234
4514
  }
3235
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
+
3236
4639
  // Measuring a rate costs indexer boundary queries plus historical quotes against
3237
4640
  // an archive node, so an unrequested rate reaches neither.
3238
4641
  function resolvePageApy(c: Context<App.Environment>, options: resolvePageApy.Options) {
@@ -3249,6 +4652,64 @@ function resolvePageApy(c: Context<App.Environment>, options: resolvePageApy.Opt
3249
4652
  )
3250
4653
  }
3251
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
+
3252
4713
  declare namespace resolvePageApy {
3253
4714
  type Options = {
3254
4715
  chainId: z.output<typeof Schema.ChainId>
@@ -3351,6 +4812,7 @@ async function resolveVault(c: Context<App.Environment>, options: resolveVault.O
3351
4812
  }
3352
4813
  : null,
3353
4814
  record,
4815
+ rewards: options.rewards,
3354
4816
  shareToken: getToken(options.tokens, discovery.shareToken.address),
3355
4817
  tvl: valued
3356
4818
  ? {
@@ -3372,6 +4834,7 @@ declare namespace resolveVault {
3372
4834
  include: z.output<typeof schema.getEarnVault.Query>['include']
3373
4835
  rates: Awaited<ReturnType<typeof Valuation.ratesFor>>
3374
4836
  record?: RegistryRecord | undefined
4837
+ rewards?: readonly RewardRead.Reward[] | undefined
3375
4838
  snapshot: VerifiedTokens.Snapshot
3376
4839
  tokens: Tokens.resolveTokens.ReturnType
3377
4840
  zoneRoutes: readonly EarnVaults.ResolvedZoneRoute[]
@@ -3386,9 +4849,41 @@ export function serializeVault(options: serializeVault.Options): z.output<typeof
3386
4849
  if (options.include.includes('capabilities') && !discovery.capabilities)
3387
4850
  throw new Error('Earn vault capabilities were not resolved.')
3388
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
3389
4884
  return Response.validated(schema.Vault, {
3390
4885
  ...(options.include.includes('access') ? { access: discovery.access } : {}),
3391
- ...(options.include.includes('apy') ? { apy: options.apy } : {}),
4886
+ ...(options.include.includes('apy') ? { apy } : {}),
3392
4887
  assetToken: options.assetToken,
3393
4888
  ...(options.include.includes('capabilities') ? { capabilities: discovery.capabilities } : {}),
3394
4889
  description: record?.description ?? null,
@@ -3449,6 +4944,8 @@ export declare namespace serializeVault {
3449
4944
  instantLiquidityValue: z.output<typeof schema.Vault>['instantLiquidityValue']
3450
4945
  /** Curated registry row, when verified. */
3451
4946
  record?: core_EarnVaults.Record | undefined
4947
+ /** Enabled, registered Merkle rewards, or undefined when enrichment is unavailable. */
4948
+ rewards?: readonly RewardRead.Reward[] | undefined
3452
4949
  /** Resolved share token. */
3453
4950
  shareToken: z.output<typeof Tokens.schema.Token>
3454
4951
  /** USD value of the assets, or null when unpriced; omitted unless `include` carries `tvl`. */
@@ -3517,6 +5014,8 @@ async function queryDeployments(
3517
5014
  c: Context<App.Environment>,
3518
5015
  options: queryDeployments.Options,
3519
5016
  ): Promise<queryDeployments.Result> {
5017
+ const factories = deploymentFactories[options.chainId] ?? []
5018
+ if (factories.length === 0) return { hasMore: false, items: [] }
3520
5019
  const filters: string[] = []
3521
5020
  if (options.asset) filters.push(`lower(asset) = '${options.asset.toLowerCase()}'`)
3522
5021
  if (options.cursor)
@@ -3535,7 +5034,8 @@ async function queryDeployments(
3535
5034
  ]),
3536
5035
  )
3537
5036
 
3538
- // 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.
3539
5039
  const result = await c
3540
5040
  .get('getTidx')(options.chainId)
3541
5041
  .fetch({
@@ -3550,6 +5050,7 @@ async function queryDeployments(
3550
5050
  block_num,
3551
5051
  log_idx
3552
5052
  FROM EarnStackDeployed
5053
+ WHERE address IN (${factories.map((address) => `'${address}'`).join(', ')})
3553
5054
  ORDER BY block_num DESC, log_idx DESC
3554
5055
  LIMIT 1 BY lower("earnVault")
3555
5056
  )
@@ -3664,3 +5165,19 @@ class IndexedCoverageError extends Error {
3664
5165
  super(`Indexed history does not yet cover ${asOf}.`)
3665
5166
  }
3666
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
+ }