tapimo 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1143) hide show
  1. package/README.md +7 -4
  2. package/dist/ApiKey.js +1 -1
  3. package/dist/ApiKey.js.map +1 -1
  4. package/dist/ApiKeys.d.ts +95 -14
  5. package/dist/ApiKeys.d.ts.map +1 -1
  6. package/dist/ApiKeys.js +360 -49
  7. package/dist/ApiKeys.js.map +1 -1
  8. package/dist/App.d.ts +21383 -18056
  9. package/dist/App.d.ts.map +1 -1
  10. package/dist/App.js +301 -22
  11. package/dist/App.js.map +1 -1
  12. package/dist/Assets.d.ts.map +1 -1
  13. package/dist/Assets.js +2 -1
  14. package/dist/Assets.js.map +1 -1
  15. package/dist/Handler.d.ts +0 -1
  16. package/dist/Handler.d.ts.map +1 -1
  17. package/dist/Handler.js +0 -1
  18. package/dist/Handler.js.map +1 -1
  19. package/dist/Scope.d.ts +13 -5
  20. package/dist/Scope.d.ts.map +1 -1
  21. package/dist/Scope.js +16 -5
  22. package/dist/Scope.js.map +1 -1
  23. package/dist/Webhooks.js +2 -2
  24. package/dist/Webhooks.js.map +1 -1
  25. package/dist/admin/App.d.ts +3936 -1337
  26. package/dist/admin/App.d.ts.map +1 -1
  27. package/dist/admin/App.js +26 -5
  28. package/dist/admin/App.js.map +1 -1
  29. package/dist/admin/apps/api-keys.d.ts +159 -7
  30. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  31. package/dist/admin/apps/api-keys.js +95 -43
  32. package/dist/admin/apps/api-keys.js.map +1 -1
  33. package/dist/admin/apps/earn-vaults.d.ts +1446 -0
  34. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  35. package/dist/admin/apps/earn-vaults.js +465 -8
  36. package/dist/admin/apps/earn-vaults.js.map +1 -1
  37. package/dist/admin/apps/organizations.d.ts +344 -3
  38. package/dist/admin/apps/organizations.d.ts.map +1 -1
  39. package/dist/admin/apps/organizations.js +288 -10
  40. package/dist/admin/apps/organizations.js.map +1 -1
  41. package/dist/admin/apps/routes-support.d.ts +95 -0
  42. package/dist/admin/apps/routes-support.d.ts.map +1 -0
  43. package/dist/admin/apps/routes-support.js +151 -0
  44. package/dist/admin/apps/routes-support.js.map +1 -0
  45. package/dist/admin/apps/routes.d.ts +2764 -0
  46. package/dist/admin/apps/routes.d.ts.map +1 -0
  47. package/dist/admin/apps/routes.js +728 -0
  48. package/dist/admin/apps/routes.js.map +1 -0
  49. package/dist/admin/apps/subsidies.d.ts +501 -0
  50. package/dist/admin/apps/subsidies.d.ts.map +1 -0
  51. package/dist/admin/apps/subsidies.js +384 -0
  52. package/dist/admin/apps/subsidies.js.map +1 -0
  53. package/dist/admin/apps/verified-tokens.d.ts +16 -16
  54. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  55. package/dist/admin/apps/verified-tokens.js +3 -3
  56. package/dist/admin/apps/verified-tokens.js.map +1 -1
  57. package/dist/admin/ui.generated.d.ts +1 -1
  58. package/dist/admin/ui.generated.d.ts.map +1 -1
  59. package/dist/admin/ui.generated.js +129 -69
  60. package/dist/admin/ui.generated.js.map +1 -1
  61. package/dist/analytics/Analytics.d.ts +5 -2
  62. package/dist/analytics/Analytics.d.ts.map +1 -1
  63. package/dist/analytics/Analytics.js +31 -1
  64. package/dist/analytics/Analytics.js.map +1 -1
  65. package/dist/analytics/tables/requestEvents.d.ts +2 -1
  66. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  67. package/dist/analytics/tables/requestEvents.js +10 -1
  68. package/dist/analytics/tables/requestEvents.js.map +1 -1
  69. package/dist/analytics/tables/routesLifecycleIntervals.d.ts +72 -0
  70. package/dist/analytics/tables/routesLifecycleIntervals.d.ts.map +1 -0
  71. package/dist/analytics/tables/routesLifecycleIntervals.js +56 -0
  72. package/dist/analytics/tables/routesLifecycleIntervals.js.map +1 -0
  73. package/dist/apps/Zones.d.ts +21 -12526
  74. package/dist/apps/Zones.d.ts.map +1 -1
  75. package/dist/apps/Zones.js +40 -8
  76. package/dist/apps/Zones.js.map +1 -1
  77. package/dist/apps/data/App.d.ts +4310 -2548
  78. package/dist/apps/data/App.d.ts.map +1 -1
  79. package/dist/apps/data/App.js +86 -26
  80. package/dist/apps/data/App.js.map +1 -1
  81. package/dist/apps/data/routes/activities.d.ts +364 -34
  82. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  83. package/dist/apps/data/routes/activities.js +495 -190
  84. package/dist/apps/data/routes/activities.js.map +1 -1
  85. package/dist/apps/data/routes/addresses.d.ts +94 -22
  86. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  87. package/dist/apps/data/routes/blocks.d.ts +37 -37
  88. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  89. package/dist/apps/data/routes/coingecko.js +47 -21
  90. package/dist/apps/data/routes/coingecko.js.map +1 -1
  91. package/dist/apps/data/routes/earn.d.ts +1641 -352
  92. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  93. package/dist/apps/data/routes/earn.js +1561 -120
  94. package/dist/apps/data/routes/earn.js.map +1 -1
  95. package/dist/apps/data/routes/exchanges.d.ts +15 -0
  96. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  97. package/dist/apps/data/routes/exchanges.js +180 -114
  98. package/dist/apps/data/routes/exchanges.js.map +1 -1
  99. package/dist/apps/data/routes/fee-amm.d.ts +5 -0
  100. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  101. package/dist/apps/data/routes/fee-amm.js +49 -38
  102. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  103. package/dist/apps/data/routes/indexer.d.ts +125 -0
  104. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  105. package/dist/apps/data/routes/indexer.js +83 -27
  106. package/dist/apps/data/routes/indexer.js.map +1 -1
  107. package/dist/apps/data/routes/rpc.d.ts +507 -33
  108. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  109. package/dist/apps/data/routes/rpc.js +266 -156
  110. package/dist/apps/data/routes/rpc.js.map +1 -1
  111. package/dist/apps/data/routes/tokenlist.d.ts +47 -15
  112. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  113. package/dist/apps/data/routes/tokenlist.js +68 -11
  114. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  115. package/dist/apps/data/routes/tokens.d.ts +95 -93
  116. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  117. package/dist/apps/data/routes/tokens.js +26 -14
  118. package/dist/apps/data/routes/tokens.js.map +1 -1
  119. package/dist/apps/data/routes/transfers.d.ts +16 -0
  120. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  121. package/dist/apps/data/routes/transfers.js +9 -7
  122. package/dist/apps/data/routes/transfers.js.map +1 -1
  123. package/dist/apps/data/routes/verified-tokens.d.ts +4 -4
  124. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  125. package/dist/apps/data/routes/verified-tokens.js +64 -20
  126. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  127. package/dist/apps/data/routes/webhooks.d.ts +1210 -70
  128. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  129. package/dist/apps/data/routes/webhooks.js +301 -163
  130. package/dist/apps/data/routes/webhooks.js.map +1 -1
  131. package/dist/apps/data/routes/zones.d.ts.map +1 -1
  132. package/dist/apps/data/routes/zones.js +20 -12
  133. package/dist/apps/data/routes/zones.js.map +1 -1
  134. package/dist/apps/docs/reference.js +2 -2
  135. package/dist/apps/docs/reference.js.map +1 -1
  136. package/dist/apps/index.d.ts +1 -1
  137. package/dist/apps/index.d.ts.map +1 -1
  138. package/dist/apps/index.js +1 -1
  139. package/dist/apps/index.js.map +1 -1
  140. package/dist/apps/management/App.d.ts +4119 -3297
  141. package/dist/apps/management/App.d.ts.map +1 -1
  142. package/dist/apps/management/App.js +5 -14
  143. package/dist/apps/management/App.js.map +1 -1
  144. package/dist/apps/management/Billing.d.ts +49 -8
  145. package/dist/apps/management/Billing.d.ts.map +1 -1
  146. package/dist/apps/management/Billing.js +221 -26
  147. package/dist/apps/management/Billing.js.map +1 -1
  148. package/dist/apps/management/routes/api-keys.d.ts +174 -72
  149. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  150. package/dist/apps/management/routes/api-keys.js +43 -15
  151. package/dist/apps/management/routes/api-keys.js.map +1 -1
  152. package/dist/apps/management/routes/billing.d.ts +364 -78
  153. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  154. package/dist/apps/management/routes/billing.js +274 -49
  155. package/dist/apps/management/routes/billing.js.map +1 -1
  156. package/dist/apps/management/routes/faucet.d.ts +6 -6
  157. package/dist/apps/management/routes/faucet.js +1 -1
  158. package/dist/apps/management/routes/faucet.js.map +1 -1
  159. package/dist/apps/management/routes/invitations.d.ts +99 -24
  160. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  161. package/dist/apps/management/routes/invitations.js +117 -13
  162. package/dist/apps/management/routes/invitations.js.map +1 -1
  163. package/dist/apps/management/routes/invite-links.d.ts +42 -42
  164. package/dist/apps/management/routes/me.d.ts.map +1 -1
  165. package/dist/apps/management/routes/me.js +6 -2
  166. package/dist/apps/management/routes/me.js.map +1 -1
  167. package/dist/apps/management/routes/members.d.ts +16 -16
  168. package/dist/apps/management/routes/orgs.d.ts +36 -20
  169. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  170. package/dist/apps/management/routes/orgs.js +52 -12
  171. package/dist/apps/management/routes/orgs.js.map +1 -1
  172. package/dist/apps/management/routes/projects.d.ts +40 -40
  173. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  174. package/dist/apps/management/routes/projects.js +6 -1
  175. package/dist/apps/management/routes/projects.js.map +1 -1
  176. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  177. package/dist/apps/management/routes/scopes.js +4 -1
  178. package/dist/apps/management/routes/scopes.js.map +1 -1
  179. package/dist/apps/management/routes/usage.d.ts +8 -8
  180. package/dist/apps/management/routes/usage.js +21 -4
  181. package/dist/apps/management/routes/usage.js.map +1 -1
  182. package/dist/apps/management/routes/verified-token-requests.d.ts +16 -16
  183. package/dist/apps/management/routes/webhooks.d.ts +931 -215
  184. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  185. package/dist/apps/management/routes/webhooks.js +71 -25
  186. package/dist/apps/management/routes/webhooks.js.map +1 -1
  187. package/dist/apps/mcp.d.ts +1 -1
  188. package/dist/apps/mcp.js +7 -7
  189. package/dist/apps/mcp.js.map +1 -1
  190. package/dist/apps/mpp/App.d.ts.map +1 -1
  191. package/dist/apps/mpp/App.js +35 -7
  192. package/dist/apps/mpp/App.js.map +1 -1
  193. package/dist/apps/mpp/internal/Adapter.d.ts +2 -0
  194. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
  195. package/dist/apps/mpp/internal/Adapter.js +30 -1
  196. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  197. package/dist/apps/mpp/internal/Policy.d.ts +22 -4
  198. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
  199. package/dist/apps/mpp/internal/Policy.js +24 -6
  200. package/dist/apps/mpp/internal/Policy.js.map +1 -1
  201. package/dist/apps/mpp/internal/Sponsorship.d.ts +3 -0
  202. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
  203. package/dist/apps/mpp/internal/Sponsorship.js +6 -3
  204. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
  205. package/dist/apps/relay/App.d.ts +2 -0
  206. package/dist/apps/relay/App.d.ts.map +1 -1
  207. package/dist/apps/relay/App.js +177 -26
  208. package/dist/apps/relay/App.js.map +1 -1
  209. package/dist/apps/relay/Sponsorships.d.ts +5 -3
  210. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  211. package/dist/apps/relay/Sponsorships.js +232 -25
  212. package/dist/apps/relay/Sponsorships.js.map +1 -1
  213. package/dist/apps/{funding → routes}/Access.d.ts +4 -4
  214. package/dist/apps/routes/Access.d.ts.map +1 -0
  215. package/dist/apps/{funding → routes}/Access.js +4 -4
  216. package/dist/apps/routes/Access.js.map +1 -0
  217. package/dist/apps/{funding → routes}/App.d.ts +841 -136
  218. package/dist/apps/{funding → routes}/App.d.ts.map +1 -1
  219. package/dist/apps/{funding → routes}/App.js +17 -17
  220. package/dist/apps/routes/App.js.map +1 -0
  221. package/dist/apps/routes/Inventory.d.ts.map +1 -0
  222. package/dist/apps/{funding → routes}/Inventory.js +2 -2
  223. package/dist/apps/routes/Inventory.js.map +1 -0
  224. package/dist/apps/routes/index.d.ts +2 -0
  225. package/dist/apps/routes/index.d.ts.map +1 -0
  226. package/dist/apps/routes/index.js +2 -0
  227. package/dist/apps/routes/index.js.map +1 -0
  228. package/dist/apps/{funding → routes}/routes/chains.d.ts +7 -7
  229. package/dist/apps/routes/routes/chains.d.ts.map +1 -0
  230. package/dist/apps/{funding → routes}/routes/chains.js +25 -25
  231. package/dist/apps/routes/routes/chains.js.map +1 -0
  232. package/dist/apps/{funding → routes}/routes/deposit-addresses.d.ts +123 -59
  233. package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -0
  234. package/dist/apps/{funding → routes}/routes/deposit-addresses.js +498 -223
  235. package/dist/apps/routes/routes/deposit-addresses.js.map +1 -0
  236. package/dist/apps/{funding → routes}/routes/providers.d.ts +8 -8
  237. package/dist/apps/routes/routes/providers.d.ts.map +1 -0
  238. package/dist/apps/routes/routes/providers.js +57 -0
  239. package/dist/apps/routes/routes/providers.js.map +1 -0
  240. package/dist/apps/{funding → routes}/routes/quotes.d.ts +19 -16
  241. package/dist/apps/routes/routes/quotes.d.ts.map +1 -0
  242. package/dist/apps/{funding → routes}/routes/quotes.js +55 -38
  243. package/dist/apps/routes/routes/quotes.js.map +1 -0
  244. package/dist/apps/{funding → routes}/routes/transfers.d.ts +957 -71
  245. package/dist/apps/routes/routes/transfers.d.ts.map +1 -0
  246. package/dist/apps/routes/routes/transfers.js +1140 -0
  247. package/dist/apps/routes/routes/transfers.js.map +1 -0
  248. package/dist/cli/Cli.d.ts +30 -0
  249. package/dist/cli/Cli.d.ts.map +1 -1
  250. package/dist/cli/commands/admin.d.ts +36 -0
  251. package/dist/cli/commands/admin.d.ts.map +1 -1
  252. package/dist/cli/commands/admin.js +273 -0
  253. package/dist/cli/commands/admin.js.map +1 -1
  254. package/dist/cloudflare.d.ts +16 -14
  255. package/dist/cloudflare.d.ts.map +1 -1
  256. package/dist/cloudflare.js +26 -21
  257. package/dist/cloudflare.js.map +1 -1
  258. package/dist/db/Db.d.ts +63 -21
  259. package/dist/db/Db.d.ts.map +1 -1
  260. package/dist/db/Db.js +1052 -15
  261. package/dist/db/Db.js.map +1 -1
  262. package/dist/db/Schema.d.ts +785 -133
  263. package/dist/db/Schema.d.ts.map +1 -1
  264. package/dist/db/Schema.js +590 -162
  265. package/dist/db/Schema.js.map +1 -1
  266. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts +4 -0
  267. package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts.map +1 -0
  268. package/dist/db/tables/apiKeyAdmissionBootstraps.js +2 -0
  269. package/dist/db/tables/apiKeyAdmissionBootstraps.js.map +1 -0
  270. package/dist/db/tables/apiKeyAdmissions.d.ts +73 -0
  271. package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -0
  272. package/dist/db/tables/apiKeyAdmissions.js +142 -0
  273. package/dist/db/tables/apiKeyAdmissions.js.map +1 -0
  274. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts +4 -0
  275. package/dist/db/tables/apiKeyOwnerAdmissions.d.ts.map +1 -0
  276. package/dist/db/tables/apiKeyOwnerAdmissions.js +2 -0
  277. package/dist/db/tables/apiKeyOwnerAdmissions.js.map +1 -0
  278. package/dist/db/tables/apiKeyOwnerTombstones.d.ts +20 -0
  279. package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -0
  280. package/dist/db/tables/apiKeyOwnerTombstones.js +57 -0
  281. package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -0
  282. package/dist/db/tables/apiKeyRevocations.d.ts +21 -0
  283. package/dist/db/tables/apiKeyRevocations.d.ts.map +1 -0
  284. package/dist/db/tables/apiKeyRevocations.js +98 -0
  285. package/dist/db/tables/apiKeyRevocations.js.map +1 -0
  286. package/dist/db/tables/authAccounts.d.ts +47 -0
  287. package/dist/db/tables/authAccounts.d.ts.map +1 -0
  288. package/dist/db/tables/authAccounts.js +72 -0
  289. package/dist/db/tables/authAccounts.js.map +1 -0
  290. package/dist/db/tables/authSessions.d.ts +12 -0
  291. package/dist/db/tables/authSessions.d.ts.map +1 -0
  292. package/dist/db/tables/authSessions.js +2 -0
  293. package/dist/db/tables/authSessions.js.map +1 -0
  294. package/dist/db/tables/authVerifications.d.ts +12 -0
  295. package/dist/db/tables/authVerifications.d.ts.map +1 -0
  296. package/dist/db/tables/authVerifications.js +2 -0
  297. package/dist/db/tables/authVerifications.js.map +1 -0
  298. package/dist/db/tables/invitations.d.ts +17 -1
  299. package/dist/db/tables/invitations.d.ts.map +1 -1
  300. package/dist/db/tables/invitations.js +123 -43
  301. package/dist/db/tables/invitations.js.map +1 -1
  302. package/dist/db/tables/memberships.d.ts +8 -0
  303. package/dist/db/tables/memberships.d.ts.map +1 -1
  304. package/dist/db/tables/memberships.js +51 -2
  305. package/dist/db/tables/memberships.js.map +1 -1
  306. package/dist/db/tables/organizations.d.ts +51 -5
  307. package/dist/db/tables/organizations.d.ts.map +1 -1
  308. package/dist/db/tables/organizations.js +135 -47
  309. package/dist/db/tables/organizations.js.map +1 -1
  310. package/dist/db/tables/projects.d.ts +27 -1
  311. package/dist/db/tables/projects.d.ts.map +1 -1
  312. package/dist/db/tables/projects.js +38 -3
  313. package/dist/db/tables/projects.js.map +1 -1
  314. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  315. package/dist/db/tables/requestUsage.js.map +1 -1
  316. package/dist/db/tables/rewardAccounts.d.ts +86 -0
  317. package/dist/db/tables/rewardAccounts.d.ts.map +1 -0
  318. package/dist/db/tables/rewardAccounts.js +88 -0
  319. package/dist/db/tables/rewardAccounts.js.map +1 -0
  320. package/dist/db/tables/rewardCampaignDistributors.d.ts +36 -0
  321. package/dist/db/tables/rewardCampaignDistributors.d.ts.map +1 -0
  322. package/dist/db/tables/rewardCampaignDistributors.js +30 -0
  323. package/dist/db/tables/rewardCampaignDistributors.js.map +1 -0
  324. package/dist/db/tables/rewardCampaigns.d.ts +171 -0
  325. package/dist/db/tables/rewardCampaigns.d.ts.map +1 -0
  326. package/dist/db/tables/rewardCampaigns.js +392 -0
  327. package/dist/db/tables/rewardCampaigns.js.map +1 -0
  328. package/dist/db/tables/rewardCredits.d.ts +53 -0
  329. package/dist/db/tables/rewardCredits.d.ts.map +1 -0
  330. package/dist/db/tables/rewardCredits.js +63 -0
  331. package/dist/db/tables/rewardCredits.js.map +1 -0
  332. package/dist/db/tables/rewardEligibilityAssociations.d.ts +61 -0
  333. package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -0
  334. package/dist/db/tables/rewardEligibilityAssociations.js +103 -0
  335. package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -0
  336. package/dist/db/tables/rewardRuns.d.ts +188 -0
  337. package/dist/db/tables/rewardRuns.d.ts.map +1 -0
  338. package/dist/db/tables/rewardRuns.js +278 -0
  339. package/dist/db/tables/rewardRuns.js.map +1 -0
  340. package/dist/db/tables/rewardTransactionAttempts.d.ts +100 -0
  341. package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -0
  342. package/dist/db/tables/rewardTransactionAttempts.js +163 -0
  343. package/dist/db/tables/rewardTransactionAttempts.js.map +1 -0
  344. package/dist/db/tables/routesCatalog.d.ts +58 -0
  345. package/dist/db/tables/routesCatalog.d.ts.map +1 -0
  346. package/dist/db/tables/{fundingCatalog.js → routesCatalog.js} +19 -19
  347. package/dist/db/tables/routesCatalog.js.map +1 -0
  348. package/dist/db/tables/routesDepositAddresses.d.ts +476 -0
  349. package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -0
  350. package/dist/db/tables/routesDepositAddresses.js +587 -0
  351. package/dist/db/tables/routesDepositAddresses.js.map +1 -0
  352. package/dist/db/tables/{fundingDepositRequestObservations.d.ts → routesDepositRequestObservations.d.ts} +6 -6
  353. package/dist/db/tables/routesDepositRequestObservations.d.ts.map +1 -0
  354. package/dist/db/tables/{fundingDepositRequestObservations.js → routesDepositRequestObservations.js} +6 -6
  355. package/dist/db/tables/routesDepositRequestObservations.js.map +1 -0
  356. package/dist/db/tables/{fundingDeposits.d.ts → routesDeposits.d.ts} +210 -22
  357. package/dist/db/tables/routesDeposits.d.ts.map +1 -0
  358. package/dist/db/tables/routesDeposits.js +576 -0
  359. package/dist/db/tables/routesDeposits.js.map +1 -0
  360. package/dist/db/tables/routesIdempotency.d.ts +166 -0
  361. package/dist/db/tables/routesIdempotency.d.ts.map +1 -0
  362. package/dist/db/tables/routesIdempotency.js +330 -0
  363. package/dist/db/tables/routesIdempotency.js.map +1 -0
  364. package/dist/db/tables/routesSubsidies.d.ts +27 -0
  365. package/dist/db/tables/routesSubsidies.d.ts.map +1 -0
  366. package/dist/db/tables/routesSubsidies.js +45 -0
  367. package/dist/db/tables/routesSubsidies.js.map +1 -0
  368. package/dist/db/tables/{fundingTransferEvents.d.ts → routesTransferEvents.d.ts} +3 -3
  369. package/dist/db/tables/routesTransferEvents.d.ts.map +1 -0
  370. package/dist/db/tables/{fundingTransferEvents.js → routesTransferEvents.js} +3 -3
  371. package/dist/db/tables/routesTransferEvents.js.map +1 -0
  372. package/dist/db/tables/routesTransferSubsidies.d.ts +19 -0
  373. package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -0
  374. package/dist/db/tables/routesTransferSubsidies.js +17 -0
  375. package/dist/db/tables/routesTransferSubsidies.js.map +1 -0
  376. package/dist/db/tables/routesTransferSubsidyNonces.d.ts +51 -0
  377. package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -0
  378. package/dist/db/tables/routesTransferSubsidyNonces.js +82 -0
  379. package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -0
  380. package/dist/db/tables/routesTransferTransactions.d.ts +77 -0
  381. package/dist/db/tables/routesTransferTransactions.d.ts.map +1 -0
  382. package/dist/db/tables/routesTransferTransactions.js +86 -0
  383. package/dist/db/tables/routesTransferTransactions.js.map +1 -0
  384. package/dist/db/tables/routesTransfers.d.ts +319 -0
  385. package/dist/db/tables/routesTransfers.d.ts.map +1 -0
  386. package/dist/db/tables/routesTransfers.js +372 -0
  387. package/dist/db/tables/routesTransfers.js.map +1 -0
  388. package/dist/db/tables/sponsoredTransactions.d.ts +112 -13
  389. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  390. package/dist/db/tables/sponsoredTransactions.js +241 -38
  391. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  392. package/dist/db/tables/sponsorshipAttributions.d.ts +69 -0
  393. package/dist/db/tables/sponsorshipAttributions.d.ts.map +1 -0
  394. package/dist/db/tables/sponsorshipAttributions.js +77 -0
  395. package/dist/db/tables/sponsorshipAttributions.js.map +1 -0
  396. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts +22 -0
  397. package/dist/db/tables/sponsorshipReconciliationCursors.d.ts.map +1 -0
  398. package/dist/db/tables/sponsorshipReconciliationCursors.js +45 -0
  399. package/dist/db/tables/sponsorshipReconciliationCursors.js.map +1 -0
  400. package/dist/db/tables/stripeCustomers.d.ts +11 -0
  401. package/dist/db/tables/stripeCustomers.d.ts.map +1 -1
  402. package/dist/db/tables/stripeCustomers.js +13 -0
  403. package/dist/db/tables/stripeCustomers.js.map +1 -1
  404. package/dist/db/tables/users.d.ts +17 -1
  405. package/dist/db/tables/users.d.ts.map +1 -1
  406. package/dist/db/tables/users.js +47 -2
  407. package/dist/db/tables/users.js.map +1 -1
  408. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  409. package/dist/db/tables/webhookQueueEvents.js +5 -1
  410. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  411. package/dist/db/tables/webhookSubscriptions.d.ts +22 -10
  412. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  413. package/dist/db/tables/webhookSubscriptions.js +50 -15
  414. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  415. package/dist/handlers/internal/executionError.d.ts +2 -2
  416. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  417. package/dist/handlers/internal/executionError.js +1 -2
  418. package/dist/handlers/internal/executionError.js.map +1 -1
  419. package/dist/handlers/internal/multisig.d.ts +4 -232
  420. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  421. package/dist/handlers/internal/multisig.js +11 -537
  422. package/dist/handlers/internal/multisig.js.map +1 -1
  423. package/dist/handlers/internal/sponsorship.d.ts +19 -8
  424. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  425. package/dist/handlers/internal/sponsorship.js +13 -9
  426. package/dist/handlers/internal/sponsorship.js.map +1 -1
  427. package/dist/handlers/relay.d.ts +9 -12
  428. package/dist/handlers/relay.d.ts.map +1 -1
  429. package/dist/handlers/relay.js +81 -85
  430. package/dist/handlers/relay.js.map +1 -1
  431. package/dist/index.d.ts +0 -17
  432. package/dist/index.d.ts.map +1 -1
  433. package/dist/index.js +0 -17
  434. package/dist/index.js.map +1 -1
  435. package/dist/internal/AssetResponse.d.ts +12 -0
  436. package/dist/internal/AssetResponse.d.ts.map +1 -0
  437. package/dist/internal/AssetResponse.js +21 -0
  438. package/dist/internal/AssetResponse.js.map +1 -0
  439. package/dist/internal/Auth.d.ts +48 -12
  440. package/dist/internal/Auth.d.ts.map +1 -1
  441. package/dist/internal/Auth.js +603 -175
  442. package/dist/internal/Auth.js.map +1 -1
  443. package/dist/internal/BetterAuth.d.ts +1488 -0
  444. package/dist/internal/BetterAuth.d.ts.map +1 -0
  445. package/dist/internal/BetterAuth.js +253 -0
  446. package/dist/internal/BetterAuth.js.map +1 -0
  447. package/dist/internal/Cache.d.ts +2 -0
  448. package/dist/internal/Cache.d.ts.map +1 -1
  449. package/dist/internal/Cache.js +2 -0
  450. package/dist/internal/Cache.js.map +1 -1
  451. package/dist/internal/EarnRates.d.ts +26 -3
  452. package/dist/internal/EarnRates.d.ts.map +1 -1
  453. package/dist/internal/EarnRates.js +84 -19
  454. package/dist/internal/EarnRates.js.map +1 -1
  455. package/dist/internal/EarnSharePrices.d.ts +120 -0
  456. package/dist/internal/EarnSharePrices.d.ts.map +1 -0
  457. package/dist/internal/EarnSharePrices.js +179 -0
  458. package/dist/internal/EarnSharePrices.js.map +1 -0
  459. package/dist/internal/EarnVaults.d.ts +4 -0
  460. package/dist/internal/EarnVaults.d.ts.map +1 -1
  461. package/dist/internal/EarnVaults.js +31 -14
  462. package/dist/internal/EarnVaults.js.map +1 -1
  463. package/dist/internal/Email.d.ts +37 -0
  464. package/dist/internal/Email.d.ts.map +1 -0
  465. package/dist/internal/Email.js +16 -0
  466. package/dist/internal/Email.js.map +1 -0
  467. package/dist/internal/Log.d.ts +32 -28
  468. package/dist/internal/Log.d.ts.map +1 -1
  469. package/dist/internal/Log.js +18 -11
  470. package/dist/internal/Log.js.map +1 -1
  471. package/dist/internal/MetricSink.d.ts +3 -0
  472. package/dist/internal/MetricSink.d.ts.map +1 -1
  473. package/dist/internal/MetricSink.js +67 -8
  474. package/dist/internal/MetricSink.js.map +1 -1
  475. package/dist/internal/Mpp.d.ts.map +1 -1
  476. package/dist/internal/Mpp.js +20 -25
  477. package/dist/internal/Mpp.js.map +1 -1
  478. package/dist/internal/OpenApi.d.ts +26 -3
  479. package/dist/internal/OpenApi.d.ts.map +1 -1
  480. package/dist/internal/OpenApi.js +25 -10
  481. package/dist/internal/OpenApi.js.map +1 -1
  482. package/dist/internal/Provider.d.ts +1 -1
  483. package/dist/internal/Provider.d.ts.map +1 -1
  484. package/dist/internal/RateLimit.d.ts.map +1 -1
  485. package/dist/internal/RateLimit.js +13 -1
  486. package/dist/internal/RateLimit.js.map +1 -1
  487. package/dist/internal/Store.d.ts +16 -0
  488. package/dist/internal/Store.d.ts.map +1 -1
  489. package/dist/internal/Store.js +59 -8
  490. package/dist/internal/Store.js.map +1 -1
  491. package/dist/internal/Tidx.d.ts +9 -2
  492. package/dist/internal/Tidx.d.ts.map +1 -1
  493. package/dist/internal/Tidx.js +78 -11
  494. package/dist/internal/Tidx.js.map +1 -1
  495. package/dist/internal/Timing.d.ts +26 -0
  496. package/dist/internal/Timing.d.ts.map +1 -1
  497. package/dist/internal/Timing.js +63 -5
  498. package/dist/internal/Timing.js.map +1 -1
  499. package/dist/internal/VercelOidc.d.ts +94 -0
  500. package/dist/internal/VercelOidc.d.ts.map +1 -0
  501. package/dist/internal/VercelOidc.js +128 -0
  502. package/dist/internal/VercelOidc.js.map +1 -0
  503. package/dist/internal/VerifiedTokens.d.ts +2 -8
  504. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  505. package/dist/internal/VerifiedTokens.js +24 -24
  506. package/dist/internal/VerifiedTokens.js.map +1 -1
  507. package/dist/internal/Viem.d.ts +46 -2068
  508. package/dist/internal/Viem.d.ts.map +1 -1
  509. package/dist/internal/Viem.js +171 -11
  510. package/dist/internal/Viem.js.map +1 -1
  511. package/dist/internal/Webhooks.d.ts +12 -4
  512. package/dist/internal/Webhooks.d.ts.map +1 -1
  513. package/dist/internal/Webhooks.js +9 -3
  514. package/dist/internal/Webhooks.js.map +1 -1
  515. package/dist/internal/ZoneRpc.d.ts +7 -0
  516. package/dist/internal/ZoneRpc.d.ts.map +1 -0
  517. package/dist/internal/ZoneRpc.js +49 -0
  518. package/dist/internal/ZoneRpc.js.map +1 -0
  519. package/dist/internal/index.d.ts +3 -1
  520. package/dist/internal/index.d.ts.map +1 -1
  521. package/dist/internal/index.js +3 -1
  522. package/dist/internal/index.js.map +1 -1
  523. package/dist/internal/rewards/Audit.d.ts +88 -0
  524. package/dist/internal/rewards/Audit.d.ts.map +1 -0
  525. package/dist/internal/rewards/Audit.js +134 -0
  526. package/dist/internal/rewards/Audit.js.map +1 -0
  527. package/dist/internal/rewards/Campaigns.d.ts +572 -0
  528. package/dist/internal/rewards/Campaigns.d.ts.map +1 -0
  529. package/dist/internal/rewards/Campaigns.js +531 -0
  530. package/dist/internal/rewards/Campaigns.js.map +1 -0
  531. package/dist/internal/rewards/Credits.d.ts +76 -0
  532. package/dist/internal/rewards/Credits.d.ts.map +1 -0
  533. package/dist/internal/rewards/Credits.js +95 -0
  534. package/dist/internal/rewards/Credits.js.map +1 -0
  535. package/dist/internal/rewards/Events.d.ts +100 -0
  536. package/dist/internal/rewards/Events.d.ts.map +1 -0
  537. package/dist/internal/rewards/Events.js +340 -0
  538. package/dist/internal/rewards/Events.js.map +1 -0
  539. package/dist/internal/rewards/Projection.d.ts +237 -0
  540. package/dist/internal/rewards/Projection.d.ts.map +1 -0
  541. package/dist/internal/rewards/Projection.js +616 -0
  542. package/dist/internal/rewards/Projection.js.map +1 -0
  543. package/dist/internal/rewards/Rates.d.ts +44 -0
  544. package/dist/internal/rewards/Rates.d.ts.map +1 -0
  545. package/dist/internal/rewards/Rates.js +60 -0
  546. package/dist/internal/rewards/Rates.js.map +1 -0
  547. package/dist/internal/rewards/Read.d.ts +158 -0
  548. package/dist/internal/rewards/Read.d.ts.map +1 -0
  549. package/dist/internal/rewards/Read.js +292 -0
  550. package/dist/internal/rewards/Read.js.map +1 -0
  551. package/dist/internal/rewards/Runner.d.ts +164 -0
  552. package/dist/internal/rewards/Runner.d.ts.map +1 -0
  553. package/dist/internal/rewards/Runner.js +1778 -0
  554. package/dist/internal/rewards/Runner.js.map +1 -0
  555. package/dist/internal/rewards/Signer.d.ts +224 -0
  556. package/dist/internal/rewards/Signer.d.ts.map +1 -0
  557. package/dist/internal/rewards/Signer.js +135 -0
  558. package/dist/internal/rewards/Signer.js.map +1 -0
  559. package/dist/internal/rewards/index.d.ts +15 -0
  560. package/dist/internal/rewards/index.d.ts.map +1 -0
  561. package/dist/internal/rewards/index.js +15 -0
  562. package/dist/internal/rewards/index.js.map +1 -0
  563. package/dist/internal/{funding → routes}/Action.d.ts +12 -1
  564. package/dist/internal/routes/Action.d.ts.map +1 -0
  565. package/dist/internal/{funding → routes}/Action.js +17 -9
  566. package/dist/internal/routes/Action.js.map +1 -0
  567. package/dist/internal/{funding → routes}/Catalog.d.ts +20 -20
  568. package/dist/internal/routes/Catalog.d.ts.map +1 -0
  569. package/dist/internal/{funding → routes}/Catalog.js +29 -29
  570. package/dist/internal/routes/Catalog.js.map +1 -0
  571. package/dist/internal/{funding → routes}/Chain.d.ts +10 -10
  572. package/dist/internal/routes/Chain.d.ts.map +1 -0
  573. package/dist/internal/{funding → routes}/Chain.js +3 -3
  574. package/dist/internal/routes/Chain.js.map +1 -0
  575. package/dist/internal/{funding → routes}/Deposit.d.ts +45 -23
  576. package/dist/internal/routes/Deposit.d.ts.map +1 -0
  577. package/dist/internal/{funding → routes}/Deposit.js +60 -27
  578. package/dist/internal/routes/Deposit.js.map +1 -0
  579. package/dist/internal/{funding → routes}/DepositAddress.d.ts +56 -27
  580. package/dist/internal/routes/DepositAddress.d.ts.map +1 -0
  581. package/dist/internal/{funding → routes}/DepositAddress.js +95 -33
  582. package/dist/internal/routes/DepositAddress.js.map +1 -0
  583. package/dist/internal/{funding → routes}/DepositWebhook.d.ts +4 -4
  584. package/dist/internal/routes/DepositWebhook.d.ts.map +1 -0
  585. package/dist/internal/{funding → routes}/DepositWebhook.js +4 -3
  586. package/dist/internal/routes/DepositWebhook.js.map +1 -0
  587. package/dist/internal/routes/Economics.d.ts +25 -0
  588. package/dist/internal/routes/Economics.d.ts.map +1 -0
  589. package/dist/internal/routes/Economics.js +28 -0
  590. package/dist/internal/routes/Economics.js.map +1 -0
  591. package/dist/internal/{funding → routes}/Evidence.d.ts +3 -3
  592. package/dist/internal/routes/Evidence.d.ts.map +1 -0
  593. package/dist/internal/{funding → routes}/Evidence.js +9 -17
  594. package/dist/internal/routes/Evidence.js.map +1 -0
  595. package/dist/internal/routes/Observability.d.ts +154 -0
  596. package/dist/internal/routes/Observability.d.ts.map +1 -0
  597. package/dist/internal/routes/Observability.js +389 -0
  598. package/dist/internal/routes/Observability.js.map +1 -0
  599. package/dist/internal/{funding → routes}/Provider.d.ts +350 -104
  600. package/dist/internal/routes/Provider.d.ts.map +1 -0
  601. package/dist/internal/{funding → routes}/Provider.js +233 -97
  602. package/dist/internal/routes/Provider.js.map +1 -0
  603. package/dist/internal/{funding → routes}/Reconciliation.d.ts +11 -6
  604. package/dist/internal/routes/Reconciliation.d.ts.map +1 -0
  605. package/dist/internal/{funding → routes}/Reconciliation.js +408 -158
  606. package/dist/internal/routes/Reconciliation.js.map +1 -0
  607. package/dist/internal/routes/Route.d.ts +21 -0
  608. package/dist/internal/routes/Route.d.ts.map +1 -0
  609. package/dist/internal/{funding → routes}/Route.js +1 -1
  610. package/dist/internal/routes/Route.js.map +1 -0
  611. package/dist/internal/{funding → routes}/SourceObservation.d.ts +6 -6
  612. package/dist/internal/routes/SourceObservation.d.ts.map +1 -0
  613. package/dist/internal/{funding → routes}/SourceObservation.js +13 -15
  614. package/dist/internal/routes/SourceObservation.js.map +1 -0
  615. package/dist/internal/{funding → routes}/Subsidy.d.ts +48 -3
  616. package/dist/internal/routes/Subsidy.d.ts.map +1 -0
  617. package/dist/internal/{funding → routes}/Subsidy.js +43 -3
  618. package/dist/internal/routes/Subsidy.js.map +1 -0
  619. package/dist/internal/{funding → routes}/Token.d.ts +4 -4
  620. package/dist/internal/routes/Token.d.ts.map +1 -0
  621. package/dist/internal/{funding → routes}/Token.js +3 -3
  622. package/dist/internal/routes/Token.js.map +1 -0
  623. package/dist/internal/{funding → routes}/Transfer.d.ts +324 -34
  624. package/dist/internal/routes/Transfer.d.ts.map +1 -0
  625. package/dist/internal/routes/Transfer.js +728 -0
  626. package/dist/internal/routes/Transfer.js.map +1 -0
  627. package/dist/internal/routes/TransferReconciliation.d.ts +55 -0
  628. package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -0
  629. package/dist/internal/routes/TransferReconciliation.js +422 -0
  630. package/dist/internal/routes/TransferReconciliation.js.map +1 -0
  631. package/dist/internal/routes/TransferSubsidy.d.ts +201 -0
  632. package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -0
  633. package/dist/internal/routes/TransferSubsidy.js +256 -0
  634. package/dist/internal/routes/TransferSubsidy.js.map +1 -0
  635. package/dist/internal/routes/TransferWebhook.d.ts +21 -0
  636. package/dist/internal/routes/TransferWebhook.d.ts.map +1 -0
  637. package/dist/internal/routes/TransferWebhook.js +43 -0
  638. package/dist/internal/routes/TransferWebhook.js.map +1 -0
  639. package/dist/internal/{funding → routes}/index.d.ts +3 -0
  640. package/dist/internal/routes/index.d.ts.map +1 -0
  641. package/dist/internal/{funding → routes}/index.js +3 -0
  642. package/dist/internal/routes/index.js.map +1 -0
  643. package/dist/internal/{funding → routes}/providers/across.d.ts +4 -4
  644. package/dist/internal/routes/providers/across.d.ts.map +1 -0
  645. package/dist/internal/{funding → routes}/providers/across.js +13 -13
  646. package/dist/internal/routes/providers/across.js.map +1 -0
  647. package/dist/internal/{funding → routes}/providers/relay.d.ts +21 -12
  648. package/dist/internal/routes/providers/relay.d.ts.map +1 -0
  649. package/dist/internal/{funding → routes}/providers/relay.js +197 -122
  650. package/dist/internal/routes/providers/relay.js.map +1 -0
  651. package/dist/internal/{funding → routes}/providers/rhino.d.ts +4 -4
  652. package/dist/internal/routes/providers/rhino.d.ts.map +1 -0
  653. package/dist/internal/{funding → routes}/providers/rhino.js +10 -10
  654. package/dist/internal/routes/providers/rhino.js.map +1 -0
  655. package/dist/internal/{funding → routes}/providers/squid.d.ts +4 -4
  656. package/dist/internal/routes/providers/squid.d.ts.map +1 -0
  657. package/dist/internal/{funding → routes}/providers/squid.js +14 -14
  658. package/dist/internal/routes/providers/squid.js.map +1 -0
  659. package/dist/internal/routes/providers/stargate.d.ts +165 -0
  660. package/dist/internal/routes/providers/stargate.d.ts.map +1 -0
  661. package/dist/internal/routes/providers/stargate.js +898 -0
  662. package/dist/internal/routes/providers/stargate.js.map +1 -0
  663. package/dist/internal/{funding → routes}/providers/symbiosis.d.ts +4 -4
  664. package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -0
  665. package/dist/internal/{funding → routes}/providers/symbiosis.js +30 -20
  666. package/dist/internal/routes/providers/symbiosis.js.map +1 -0
  667. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
  668. package/dist/internal/webhookDestinations/slack.js +22 -6
  669. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  670. package/dist/providers/index.d.ts +12 -12
  671. package/dist/providers/index.d.ts.map +1 -1
  672. package/dist/providers/index.js +12 -12
  673. package/dist/providers/index.js.map +1 -1
  674. package/dist/server.d.ts +19 -0
  675. package/dist/server.d.ts.map +1 -0
  676. package/dist/server.js +19 -0
  677. package/dist/server.js.map +1 -0
  678. package/package.json +15 -4
  679. package/src/ApiKey.ts +1 -1
  680. package/src/ApiKeys.test.ts +1033 -3
  681. package/src/ApiKeys.ts +489 -48
  682. package/src/App.test-d.ts +1 -1
  683. package/src/App.test.ts +216 -25
  684. package/src/App.ts +335 -31
  685. package/src/Assets.test.ts +2 -2
  686. package/src/Assets.ts +3 -1
  687. package/src/Auth.test-d.ts +1 -1
  688. package/src/Client.test-d.ts +611 -101
  689. package/src/Handler.ts +0 -1
  690. package/src/OpenApi.contract.test.ts +195 -0
  691. package/src/Scope.ts +17 -5
  692. package/src/Webhooks.ts +2 -2
  693. package/src/admin/App.test.ts +30 -2
  694. package/src/admin/App.ts +42 -6
  695. package/src/admin/apps/api-keys.test.ts +267 -5
  696. package/src/admin/apps/api-keys.ts +100 -50
  697. package/src/admin/apps/earn-vaults.test.ts +158 -0
  698. package/src/admin/apps/earn-vaults.ts +681 -7
  699. package/src/admin/apps/organizations.test.ts +342 -1
  700. package/src/admin/apps/organizations.ts +368 -8
  701. package/src/admin/apps/routes-support.test.ts +200 -0
  702. package/src/admin/apps/routes-support.ts +201 -0
  703. package/src/admin/apps/routes.test.ts +450 -0
  704. package/src/admin/apps/routes.ts +896 -0
  705. package/src/admin/apps/scopes.test.ts +15 -5
  706. package/src/admin/apps/subsidies.test.ts +306 -0
  707. package/src/admin/apps/subsidies.ts +457 -0
  708. package/src/admin/apps/verified-tokens.test.ts +8 -3
  709. package/src/admin/apps/verified-tokens.ts +3 -3
  710. package/src/admin/ui/plugin.ts +1 -2
  711. package/src/admin/ui/src/components/RewardCampaignForm.tsx +1009 -0
  712. package/src/admin/ui/src/lib/config.ts +11 -2
  713. package/src/admin/ui/src/lib/earn-vaults.ts +2 -2
  714. package/src/admin/ui/src/lib/organizations.test.ts +147 -3
  715. package/src/admin/ui/src/lib/organizations.ts +136 -0
  716. package/src/admin/ui/src/lib/query-client.ts +1 -1
  717. package/src/admin/ui/src/lib/reward-campaigns.ts +136 -0
  718. package/src/admin/ui/src/lib/routes.test.ts +88 -0
  719. package/src/admin/ui/src/lib/routes.ts +304 -0
  720. package/src/admin/ui/src/lib/subsidies.ts +75 -0
  721. package/src/admin/ui/src/routeTree.gen.ts +63 -0
  722. package/src/admin/ui/src/routes/__root.tsx +12 -1
  723. package/src/admin/ui/src/routes/api-keys.tsx +196 -84
  724. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +105 -70
  725. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +334 -2
  726. package/src/admin/ui/src/routes/routes.tsx +1615 -0
  727. package/src/admin/ui/src/routes/routes_.support.tsx +248 -0
  728. package/src/admin/ui/src/routes/subsidies.tsx +799 -0
  729. package/src/admin/ui.generated.ts +144 -69
  730. package/src/analytics/Analytics.test.ts +108 -18
  731. package/src/analytics/Analytics.ts +38 -6
  732. package/src/analytics/tables/requestEvents.test.ts +100 -0
  733. package/src/analytics/tables/requestEvents.ts +10 -1
  734. package/src/analytics/tables/routesLifecycleIntervals.ts +140 -0
  735. package/src/apps/Zones.test.ts +18 -3
  736. package/src/apps/Zones.ts +55 -8
  737. package/src/apps/composition.test.ts +68 -59
  738. package/src/apps/data/App.test.ts +154 -2
  739. package/src/apps/data/App.ts +102 -33
  740. package/src/apps/data/routes/activities.test.ts +419 -7
  741. package/src/apps/data/routes/activities.ts +683 -216
  742. package/src/apps/data/routes/balances.test.ts +2 -2
  743. package/src/apps/data/routes/blocks.test.ts +46 -1
  744. package/src/apps/data/routes/coingecko.test.ts +419 -1
  745. package/src/apps/data/routes/coingecko.ts +309 -253
  746. package/src/apps/data/routes/earn.discovery.test.ts +274 -0
  747. package/src/apps/data/routes/earn.test.ts +1661 -29
  748. package/src/apps/data/routes/earn.ts +2314 -328
  749. package/src/apps/data/routes/exchanges.test.ts +752 -30
  750. package/src/apps/data/routes/exchanges.ts +954 -753
  751. package/src/apps/data/routes/fee-amm.test.ts +224 -0
  752. package/src/apps/data/routes/fee-amm.ts +208 -178
  753. package/src/apps/data/routes/indexer.test.ts +389 -0
  754. package/src/apps/data/routes/indexer.ts +165 -69
  755. package/src/apps/data/routes/receipts.test.ts +1 -1
  756. package/src/apps/data/routes/rpc.test.ts +352 -8
  757. package/src/apps/data/routes/rpc.ts +407 -219
  758. package/src/apps/data/routes/rpc.zone.test.ts +340 -0
  759. package/src/apps/data/routes/tokenlist.test.ts +285 -0
  760. package/src/apps/data/routes/tokenlist.ts +173 -88
  761. package/src/apps/data/routes/tokens.test.ts +93 -3
  762. package/src/apps/data/routes/tokens.ts +39 -23
  763. package/src/apps/data/routes/transactions.test.ts +1 -1
  764. package/src/apps/data/routes/transfers.test.ts +56 -24
  765. package/src/apps/data/routes/transfers.ts +19 -8
  766. package/src/apps/data/routes/valuation.test.ts +1 -1
  767. package/src/apps/data/routes/verified-tokens.test.ts +345 -0
  768. package/src/apps/data/routes/verified-tokens.ts +99 -29
  769. package/src/apps/data/routes/webhooks.test.ts +659 -81
  770. package/src/apps/data/routes/webhooks.ts +1104 -817
  771. package/src/apps/data/routes/zones.test.ts +148 -71
  772. package/src/apps/data/routes/zones.ts +71 -55
  773. package/src/apps/docs/reference.ts +2 -2
  774. package/src/apps/index.ts +1 -1
  775. package/src/apps/management/App.ts +9 -51
  776. package/src/apps/management/Billing.test.ts +375 -14
  777. package/src/apps/management/Billing.ts +283 -34
  778. package/src/apps/management/routes/api-keys.test.ts +269 -7
  779. package/src/apps/management/routes/api-keys.ts +61 -20
  780. package/src/apps/management/routes/billing.test.ts +477 -15
  781. package/src/apps/management/routes/billing.ts +377 -50
  782. package/src/apps/management/routes/faucet.test.ts +1 -1
  783. package/src/apps/management/routes/faucet.ts +1 -1
  784. package/src/apps/management/routes/invitations.test.ts +270 -3
  785. package/src/apps/management/routes/invitations.ts +185 -15
  786. package/src/apps/management/routes/invoicePreview.test.ts +549 -0
  787. package/src/apps/management/routes/me.test.ts +246 -5
  788. package/src/apps/management/routes/me.ts +7 -2
  789. package/src/apps/management/routes/members.test.ts +108 -0
  790. package/src/apps/management/routes/orgs.test.ts +191 -3
  791. package/src/apps/management/routes/orgs.ts +58 -11
  792. package/src/apps/management/routes/projects.test.ts +33 -3
  793. package/src/apps/management/routes/projects.ts +6 -1
  794. package/src/apps/management/routes/scopes.test.ts +30 -2
  795. package/src/apps/management/routes/scopes.ts +4 -1
  796. package/src/apps/management/routes/usage.test.ts +32 -0
  797. package/src/apps/management/routes/usage.ts +22 -4
  798. package/src/apps/management/routes/webhooks.test.ts +49 -26
  799. package/src/apps/management/routes/webhooks.ts +93 -38
  800. package/src/apps/mcp.test.ts +6 -6
  801. package/src/apps/mcp.ts +7 -7
  802. package/src/apps/mpp/App.ts +37 -7
  803. package/src/apps/mpp/Mpp.test.ts +49 -8
  804. package/src/apps/mpp/Mpp.zone.test.ts +219 -0
  805. package/src/apps/mpp/internal/Adapter.ts +34 -0
  806. package/src/apps/mpp/internal/Policy.test.ts +86 -31
  807. package/src/apps/mpp/internal/Policy.ts +49 -7
  808. package/src/apps/mpp/internal/Sponsorship.test.ts +207 -0
  809. package/src/apps/mpp/internal/Sponsorship.ts +9 -3
  810. package/src/apps/relay/App.test.ts +1024 -38
  811. package/src/apps/relay/App.ts +228 -32
  812. package/src/apps/relay/App.zone-billing.test.ts +257 -0
  813. package/src/apps/relay/Sponsorships.test.ts +67 -4
  814. package/src/apps/relay/Sponsorships.ts +253 -29
  815. package/src/apps/relay/Sponsorships.zone.test.ts +102 -0
  816. package/src/apps/{funding → routes}/Access.ts +6 -6
  817. package/src/apps/routes/App.test.ts +165 -0
  818. package/src/apps/routes/App.ts +96 -0
  819. package/src/apps/{funding → routes}/Inventory.ts +2 -2
  820. package/src/apps/routes/index.ts +1 -0
  821. package/src/apps/{funding → routes}/routes/chains.test.ts +66 -66
  822. package/src/apps/{funding → routes}/routes/chains.ts +23 -23
  823. package/src/apps/{funding → routes}/routes/deposit-addresses.test.ts +1362 -462
  824. package/src/apps/{funding → routes}/routes/deposit-addresses.ts +568 -248
  825. package/src/apps/{funding → routes}/routes/providers.test.ts +27 -33
  826. package/src/apps/{funding → routes}/routes/providers.ts +23 -23
  827. package/src/apps/{funding → routes}/routes/quotes.test.ts +298 -41
  828. package/src/apps/{funding → routes}/routes/quotes.ts +57 -40
  829. package/src/apps/routes/routes/transfers.test.ts +2876 -0
  830. package/src/apps/routes/routes/transfers.ts +1332 -0
  831. package/src/cli/Cli.test.ts +1 -1
  832. package/src/cli/commands/admin.test.ts +748 -1
  833. package/src/cli/commands/admin.ts +278 -0
  834. package/src/cloudflare.ts +52 -47
  835. package/src/db/Db.test.ts +245 -2
  836. package/src/db/Db.ts +1511 -34
  837. package/src/db/Schema.ts +744 -228
  838. package/src/db/tables/apiKeyAdmissionBootstraps.ts +4 -0
  839. package/src/db/tables/apiKeyAdmissions.ts +218 -0
  840. package/src/db/tables/apiKeyOwnerAdmissions.ts +4 -0
  841. package/src/db/tables/apiKeyOwnerTombstones.ts +81 -0
  842. package/src/db/tables/apiKeyRevocations.test.ts +74 -0
  843. package/src/db/tables/apiKeyRevocations.ts +121 -0
  844. package/src/db/tables/authAccounts.ts +105 -0
  845. package/src/db/tables/authSessions.ts +13 -0
  846. package/src/db/tables/authVerifications.ts +13 -0
  847. package/src/db/tables/invitations.ts +142 -45
  848. package/src/db/tables/memberships.ts +79 -3
  849. package/src/db/tables/organizations.test.ts +725 -7
  850. package/src/db/tables/organizations.ts +182 -49
  851. package/src/db/tables/projects.test.ts +71 -0
  852. package/src/db/tables/projects.ts +59 -4
  853. package/src/db/tables/requestUsage.ts +4 -1
  854. package/src/db/tables/rewardAccounts.ts +178 -0
  855. package/src/db/tables/rewardCampaignDistributors.ts +68 -0
  856. package/src/db/tables/rewardCampaigns.test.ts +1388 -0
  857. package/src/db/tables/rewardCampaigns.ts +647 -0
  858. package/src/db/tables/rewardCredits.ts +110 -0
  859. package/src/db/tables/rewardEligibilityAssociations.test.ts +271 -0
  860. package/src/db/tables/rewardEligibilityAssociations.ts +184 -0
  861. package/src/db/tables/rewardRuns.ts +514 -0
  862. package/src/db/tables/rewardTransactionAttempts.ts +272 -0
  863. package/src/db/tables/routesCatalog.test.ts +185 -0
  864. package/src/db/tables/routesCatalog.ts +163 -0
  865. package/src/db/tables/routesDepositAddresses.test.ts +1048 -0
  866. package/src/db/tables/routesDepositAddresses.ts +1008 -0
  867. package/src/db/tables/{fundingDepositRequestObservations.test.ts → routesDepositRequestObservations.test.ts} +13 -14
  868. package/src/db/tables/{fundingDepositRequestObservations.ts → routesDepositRequestObservations.ts} +10 -10
  869. package/src/db/tables/{fundingDeposits.test.ts → routesDeposits.test.ts} +316 -46
  870. package/src/db/tables/routesDeposits.ts +1062 -0
  871. package/src/db/tables/routesIdempotency.test.ts +796 -0
  872. package/src/db/tables/routesIdempotency.ts +496 -0
  873. package/src/db/tables/routesSubsidies.test.ts +120 -0
  874. package/src/db/tables/routesSubsidies.ts +71 -0
  875. package/src/db/tables/{fundingTransferEvents.ts → routesTransferEvents.ts} +4 -4
  876. package/src/db/tables/routesTransferSubsidies.ts +38 -0
  877. package/src/db/tables/routesTransferSubsidyNonces.ts +124 -0
  878. package/src/db/tables/routesTransferTransactions.test.ts +195 -0
  879. package/src/db/tables/routesTransferTransactions.ts +158 -0
  880. package/src/db/tables/routesTransfers.test.ts +338 -0
  881. package/src/db/tables/routesTransfers.ts +662 -0
  882. package/src/db/tables/sponsoredTransactions.test.ts +563 -1
  883. package/src/db/tables/sponsoredTransactions.ts +386 -42
  884. package/src/db/tables/sponsorshipAttributions.test.ts +160 -0
  885. package/src/db/tables/sponsorshipAttributions.ts +168 -0
  886. package/src/db/tables/sponsorshipReconciliationCursors.test.ts +115 -0
  887. package/src/db/tables/sponsorshipReconciliationCursors.ts +67 -0
  888. package/src/db/tables/stripeCustomers.test.ts +85 -0
  889. package/src/db/tables/stripeCustomers.ts +24 -0
  890. package/src/db/tables/users.test.ts +129 -0
  891. package/src/db/tables/users.ts +49 -3
  892. package/src/db/tables/webhookQueueEvents.ts +8 -1
  893. package/src/db/tables/webhookSubscriptions.ts +103 -21
  894. package/src/handlers/internal/executionError.ts +3 -4
  895. package/src/handlers/internal/multisig.ts +13 -816
  896. package/src/handlers/internal/sponsorship.ts +26 -10
  897. package/src/handlers/relay.test-d.ts +9 -1
  898. package/src/handlers/relay.test.ts +823 -3
  899. package/src/handlers/relay.ts +127 -107
  900. package/src/index.test-d.ts +7 -0
  901. package/src/index.ts +0 -17
  902. package/src/internal/AssetResponse.ts +26 -0
  903. package/src/internal/Auth.test.ts +1564 -9
  904. package/src/internal/Auth.ts +833 -239
  905. package/src/internal/BetterAuth.test.ts +242 -0
  906. package/src/internal/BetterAuth.ts +349 -0
  907. package/src/internal/Cache.ts +2 -0
  908. package/src/internal/EarnRates.test.ts +17 -0
  909. package/src/internal/EarnRates.ts +124 -31
  910. package/src/internal/EarnSharePrices.test.ts +182 -0
  911. package/src/internal/EarnSharePrices.ts +313 -0
  912. package/src/internal/EarnVaults.test.ts +21 -0
  913. package/src/internal/EarnVaults.ts +37 -15
  914. package/src/internal/Email.ts +54 -0
  915. package/src/internal/Idempotency.test.ts +2 -2
  916. package/src/internal/Log.test-d.ts +10 -7
  917. package/src/internal/Log.test.ts +114 -16
  918. package/src/internal/Log.ts +55 -40
  919. package/src/internal/MetricSink.test.ts +313 -26
  920. package/src/internal/MetricSink.ts +73 -8
  921. package/src/internal/Mpp.test.ts +31 -0
  922. package/src/internal/Mpp.ts +21 -24
  923. package/src/internal/OpenApi.ts +45 -12
  924. package/src/internal/Provider.test.ts +1 -1
  925. package/src/internal/Provider.ts +1 -1
  926. package/src/internal/RateLimit.test.ts +21 -3
  927. package/src/internal/RateLimit.ts +12 -1
  928. package/src/internal/Store.test-d.ts +1 -1
  929. package/src/internal/Store.test.ts +151 -8
  930. package/src/internal/Store.ts +76 -9
  931. package/src/internal/Tidx.test.ts +59 -8
  932. package/src/internal/Tidx.ts +86 -14
  933. package/src/internal/Timing.test.ts +77 -0
  934. package/src/internal/Timing.ts +83 -6
  935. package/src/internal/VercelOidc.test.ts +188 -0
  936. package/src/internal/VercelOidc.ts +193 -0
  937. package/src/internal/VerifiedTokens.test.ts +60 -6
  938. package/src/internal/VerifiedTokens.ts +21 -24
  939. package/src/internal/Viem.batch.test.ts +80 -0
  940. package/src/internal/Viem.test.ts +201 -2
  941. package/src/internal/Viem.ts +249 -17
  942. package/src/internal/Webhooks.test.ts +125 -1
  943. package/src/internal/Webhooks.ts +22 -4
  944. package/src/internal/ZoneRpc.ts +50 -0
  945. package/src/internal/exchange/Provider.test.ts +2 -2
  946. package/src/internal/index.test.ts +3 -1
  947. package/src/internal/index.ts +3 -1
  948. package/src/internal/rewards/Audit.ts +231 -0
  949. package/src/internal/rewards/Campaigns.test.ts +1234 -0
  950. package/src/internal/rewards/Campaigns.ts +1205 -0
  951. package/src/internal/rewards/Credits.ts +165 -0
  952. package/src/internal/rewards/Events.local.test.ts +113 -0
  953. package/src/internal/rewards/Events.test.ts +152 -0
  954. package/src/internal/rewards/Events.ts +468 -0
  955. package/src/internal/rewards/Projection.test.ts +681 -0
  956. package/src/internal/rewards/Projection.ts +987 -0
  957. package/src/internal/rewards/Rates.test.ts +171 -0
  958. package/src/internal/rewards/Rates.ts +110 -0
  959. package/src/internal/rewards/Read.test.ts +501 -0
  960. package/src/internal/rewards/Read.ts +476 -0
  961. package/src/internal/rewards/Runner.checkpoint.test.ts +80 -0
  962. package/src/internal/rewards/Runner.states.test.ts +188 -0
  963. package/src/internal/rewards/Runner.test.ts +90 -0
  964. package/src/internal/rewards/Runner.ts +2229 -0
  965. package/src/internal/rewards/Signer.test.ts +79 -0
  966. package/src/internal/rewards/Signer.ts +180 -0
  967. package/src/internal/rewards/index.ts +14 -0
  968. package/src/internal/{funding → routes}/Action.test.ts +63 -14
  969. package/src/internal/{funding → routes}/Action.ts +35 -8
  970. package/src/internal/{funding → routes}/Catalog.test.ts +3 -3
  971. package/src/internal/{funding → routes}/Catalog.ts +39 -39
  972. package/src/internal/{funding → routes}/Chain.test.ts +1 -1
  973. package/src/internal/{funding → routes}/Chain.ts +11 -11
  974. package/src/internal/{funding → routes}/Deposit.test.ts +1 -1
  975. package/src/internal/{funding → routes}/Deposit.ts +93 -45
  976. package/src/internal/{funding → routes}/DepositAddress.test.ts +11 -3
  977. package/src/internal/{funding → routes}/DepositAddress.ts +132 -53
  978. package/src/internal/{funding → routes}/DepositWebhook.test.ts +61 -18
  979. package/src/internal/{funding → routes}/DepositWebhook.ts +9 -8
  980. package/src/internal/routes/Economics.test.ts +42 -0
  981. package/src/internal/routes/Economics.ts +47 -0
  982. package/src/internal/{funding → routes}/Evidence.test.ts +6 -6
  983. package/src/internal/{funding → routes}/Evidence.ts +11 -22
  984. package/src/internal/routes/Observability.test.ts +636 -0
  985. package/src/internal/routes/Observability.ts +668 -0
  986. package/src/internal/{funding → routes}/Provider.test.ts +489 -153
  987. package/src/internal/{funding → routes}/Provider.ts +531 -185
  988. package/src/internal/{funding → routes}/Reconciliation.local.test.ts +84 -12
  989. package/src/internal/{funding → routes}/Reconciliation.test.ts +1213 -279
  990. package/src/internal/{funding → routes}/Reconciliation.ts +501 -203
  991. package/src/internal/routes/Reconciliation.unattributed.test.ts +250 -0
  992. package/src/internal/{funding → routes}/Route.ts +7 -7
  993. package/src/internal/{funding → routes}/SourceObservation.test.ts +13 -14
  994. package/src/internal/{funding → routes}/SourceObservation.ts +18 -22
  995. package/src/internal/{funding → routes}/Subsidy.local.test.ts +7 -7
  996. package/src/internal/routes/Subsidy.test.ts +169 -0
  997. package/src/internal/{funding → routes}/Subsidy.ts +86 -6
  998. package/src/internal/{funding → routes}/Token.test.ts +1 -1
  999. package/src/internal/{funding → routes}/Token.ts +5 -5
  1000. package/src/internal/routes/Transfer.test.ts +1199 -0
  1001. package/src/internal/routes/Transfer.ts +1206 -0
  1002. package/src/internal/routes/TransferReconciliation.test.ts +731 -0
  1003. package/src/internal/routes/TransferReconciliation.ts +527 -0
  1004. package/src/internal/routes/TransferSubsidy.test.ts +172 -0
  1005. package/src/internal/routes/TransferSubsidy.ts +447 -0
  1006. package/src/internal/routes/TransferWebhook.test.ts +128 -0
  1007. package/src/internal/routes/TransferWebhook.ts +67 -0
  1008. package/src/internal/{funding → routes}/index.test.ts +6 -3
  1009. package/src/internal/{funding → routes}/index.ts +3 -0
  1010. package/src/internal/{funding → routes}/providers/across.ts +16 -16
  1011. package/src/internal/{funding → routes}/providers/index.test.ts +157 -44
  1012. package/src/internal/{funding → routes}/providers/relay.test.ts +165 -32
  1013. package/src/internal/{funding → routes}/providers/relay.ts +231 -136
  1014. package/src/internal/{funding → routes}/providers/rhino.ts +16 -16
  1015. package/src/internal/{funding → routes}/providers/squid.ts +17 -17
  1016. package/src/internal/routes/providers/stargate.test.ts +728 -0
  1017. package/src/internal/routes/providers/stargate.ts +1276 -0
  1018. package/src/internal/{funding → routes}/providers/symbiosis.ts +64 -24
  1019. package/src/internal/webhookDestinations/slack.ts +20 -5
  1020. package/src/providers/index.ts +13 -13
  1021. package/src/server.ts +18 -0
  1022. package/dist/apps/funding/Access.d.ts.map +0 -1
  1023. package/dist/apps/funding/Access.js.map +0 -1
  1024. package/dist/apps/funding/App.js.map +0 -1
  1025. package/dist/apps/funding/Inventory.d.ts.map +0 -1
  1026. package/dist/apps/funding/Inventory.js.map +0 -1
  1027. package/dist/apps/funding/ProviderAttempts.d.ts +0 -5
  1028. package/dist/apps/funding/ProviderAttempts.d.ts.map +0 -1
  1029. package/dist/apps/funding/ProviderAttempts.js +0 -24
  1030. package/dist/apps/funding/ProviderAttempts.js.map +0 -1
  1031. package/dist/apps/funding/index.d.ts +0 -2
  1032. package/dist/apps/funding/index.d.ts.map +0 -1
  1033. package/dist/apps/funding/index.js +0 -2
  1034. package/dist/apps/funding/index.js.map +0 -1
  1035. package/dist/apps/funding/routes/chains.d.ts.map +0 -1
  1036. package/dist/apps/funding/routes/chains.js.map +0 -1
  1037. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +0 -1
  1038. package/dist/apps/funding/routes/deposit-addresses.js.map +0 -1
  1039. package/dist/apps/funding/routes/providers.d.ts.map +0 -1
  1040. package/dist/apps/funding/routes/providers.js +0 -57
  1041. package/dist/apps/funding/routes/providers.js.map +0 -1
  1042. package/dist/apps/funding/routes/quotes.d.ts.map +0 -1
  1043. package/dist/apps/funding/routes/quotes.js.map +0 -1
  1044. package/dist/apps/funding/routes/transfers.d.ts.map +0 -1
  1045. package/dist/apps/funding/routes/transfers.js +0 -622
  1046. package/dist/apps/funding/routes/transfers.js.map +0 -1
  1047. package/dist/db/tables/fundingCatalog.d.ts +0 -58
  1048. package/dist/db/tables/fundingCatalog.d.ts.map +0 -1
  1049. package/dist/db/tables/fundingCatalog.js.map +0 -1
  1050. package/dist/db/tables/fundingDepositAddresses.d.ts +0 -192
  1051. package/dist/db/tables/fundingDepositAddresses.d.ts.map +0 -1
  1052. package/dist/db/tables/fundingDepositAddresses.js +0 -235
  1053. package/dist/db/tables/fundingDepositAddresses.js.map +0 -1
  1054. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +0 -1
  1055. package/dist/db/tables/fundingDepositRequestObservations.js.map +0 -1
  1056. package/dist/db/tables/fundingDeposits.d.ts.map +0 -1
  1057. package/dist/db/tables/fundingDeposits.js +0 -295
  1058. package/dist/db/tables/fundingDeposits.js.map +0 -1
  1059. package/dist/db/tables/fundingIdempotency.d.ts +0 -91
  1060. package/dist/db/tables/fundingIdempotency.d.ts.map +0 -1
  1061. package/dist/db/tables/fundingIdempotency.js +0 -103
  1062. package/dist/db/tables/fundingIdempotency.js.map +0 -1
  1063. package/dist/db/tables/fundingTransferEvents.d.ts.map +0 -1
  1064. package/dist/db/tables/fundingTransferEvents.js.map +0 -1
  1065. package/dist/db/tables/fundingTransferTransactions.d.ts +0 -15
  1066. package/dist/db/tables/fundingTransferTransactions.d.ts.map +0 -1
  1067. package/dist/db/tables/fundingTransferTransactions.js +0 -27
  1068. package/dist/db/tables/fundingTransferTransactions.js.map +0 -1
  1069. package/dist/db/tables/fundingTransfers.d.ts +0 -82
  1070. package/dist/db/tables/fundingTransfers.d.ts.map +0 -1
  1071. package/dist/db/tables/fundingTransfers.js +0 -89
  1072. package/dist/db/tables/fundingTransfers.js.map +0 -1
  1073. package/dist/internal/funding/Action.d.ts.map +0 -1
  1074. package/dist/internal/funding/Action.js.map +0 -1
  1075. package/dist/internal/funding/Catalog.d.ts.map +0 -1
  1076. package/dist/internal/funding/Catalog.js.map +0 -1
  1077. package/dist/internal/funding/Chain.d.ts.map +0 -1
  1078. package/dist/internal/funding/Chain.js.map +0 -1
  1079. package/dist/internal/funding/Deposit.d.ts.map +0 -1
  1080. package/dist/internal/funding/Deposit.js.map +0 -1
  1081. package/dist/internal/funding/DepositAddress.d.ts.map +0 -1
  1082. package/dist/internal/funding/DepositAddress.js.map +0 -1
  1083. package/dist/internal/funding/DepositWebhook.d.ts.map +0 -1
  1084. package/dist/internal/funding/DepositWebhook.js.map +0 -1
  1085. package/dist/internal/funding/Evidence.d.ts.map +0 -1
  1086. package/dist/internal/funding/Evidence.js.map +0 -1
  1087. package/dist/internal/funding/Provider.d.ts.map +0 -1
  1088. package/dist/internal/funding/Provider.js.map +0 -1
  1089. package/dist/internal/funding/Reconciliation.d.ts.map +0 -1
  1090. package/dist/internal/funding/Reconciliation.js.map +0 -1
  1091. package/dist/internal/funding/Route.d.ts +0 -21
  1092. package/dist/internal/funding/Route.d.ts.map +0 -1
  1093. package/dist/internal/funding/Route.js.map +0 -1
  1094. package/dist/internal/funding/SourceObservation.d.ts.map +0 -1
  1095. package/dist/internal/funding/SourceObservation.js.map +0 -1
  1096. package/dist/internal/funding/Subsidy.d.ts.map +0 -1
  1097. package/dist/internal/funding/Subsidy.js.map +0 -1
  1098. package/dist/internal/funding/Token.d.ts.map +0 -1
  1099. package/dist/internal/funding/Token.js.map +0 -1
  1100. package/dist/internal/funding/Transfer.d.ts.map +0 -1
  1101. package/dist/internal/funding/Transfer.js +0 -334
  1102. package/dist/internal/funding/Transfer.js.map +0 -1
  1103. package/dist/internal/funding/index.d.ts.map +0 -1
  1104. package/dist/internal/funding/index.js.map +0 -1
  1105. package/dist/internal/funding/providers/across.d.ts.map +0 -1
  1106. package/dist/internal/funding/providers/across.js.map +0 -1
  1107. package/dist/internal/funding/providers/relay.d.ts.map +0 -1
  1108. package/dist/internal/funding/providers/relay.js.map +0 -1
  1109. package/dist/internal/funding/providers/rhino.d.ts.map +0 -1
  1110. package/dist/internal/funding/providers/rhino.js.map +0 -1
  1111. package/dist/internal/funding/providers/squid.d.ts.map +0 -1
  1112. package/dist/internal/funding/providers/squid.js.map +0 -1
  1113. package/dist/internal/funding/providers/stargate.d.ts +0 -38
  1114. package/dist/internal/funding/providers/stargate.d.ts.map +0 -1
  1115. package/dist/internal/funding/providers/stargate.js +0 -277
  1116. package/dist/internal/funding/providers/stargate.js.map +0 -1
  1117. package/dist/internal/funding/providers/symbiosis.d.ts.map +0 -1
  1118. package/dist/internal/funding/providers/symbiosis.js.map +0 -1
  1119. package/src/apps/funding/App.test.ts +0 -78
  1120. package/src/apps/funding/App.ts +0 -78
  1121. package/src/apps/funding/ProviderAttempts.ts +0 -26
  1122. package/src/apps/funding/index.ts +0 -1
  1123. package/src/apps/funding/routes/transfers.test.ts +0 -1638
  1124. package/src/apps/funding/routes/transfers.ts +0 -721
  1125. package/src/db/tables/fundingCatalog.test.ts +0 -122
  1126. package/src/db/tables/fundingCatalog.ts +0 -163
  1127. package/src/db/tables/fundingDepositAddresses.test.ts +0 -608
  1128. package/src/db/tables/fundingDepositAddresses.ts +0 -443
  1129. package/src/db/tables/fundingDeposits.ts +0 -572
  1130. package/src/db/tables/fundingIdempotency.test.ts +0 -182
  1131. package/src/db/tables/fundingIdempotency.ts +0 -180
  1132. package/src/db/tables/fundingTransferTransactions.test.ts +0 -69
  1133. package/src/db/tables/fundingTransferTransactions.ts +0 -37
  1134. package/src/db/tables/fundingTransfers.test.ts +0 -89
  1135. package/src/db/tables/fundingTransfers.ts +0 -153
  1136. package/src/handlers/internal/multisig.test.ts +0 -818
  1137. package/src/internal/funding/Subsidy.test.ts +0 -75
  1138. package/src/internal/funding/Transfer.test.ts +0 -369
  1139. package/src/internal/funding/Transfer.ts +0 -545
  1140. package/src/internal/funding/providers/stargate.test.ts +0 -163
  1141. package/src/internal/funding/providers/stargate.ts +0 -409
  1142. /package/dist/apps/{funding → routes}/Inventory.d.ts +0 -0
  1143. /package/src/internal/{funding → routes}/Route.test.ts +0 -0
@@ -1,12 +1,22 @@
1
- import { Schema } from 'tapimo'
1
+ import { Db, Schema } from 'tapimo/server'
2
2
  import type * as z from 'zod/mini'
3
3
 
4
+ import * as Scope from '../../../Scope.js'
4
5
  import * as core_EarnVaults from '../../../db/tables/earnVaults.js'
6
+ import * as RewardAccounts from '../../../db/tables/rewardAccounts.js'
7
+ import * as RewardCampaigns from '../../../db/tables/rewardCampaigns.js'
8
+ import * as RewardEligibilityAssociations from '../../../db/tables/rewardEligibilityAssociations.js'
5
9
  import * as TestApp from '../../../../test/App.js'
6
10
  import * as TestEarn from '../../../../test/Earn.js'
7
11
  import * as Runtime from '../../../../test/runtime.js'
8
12
  import * as Cursor from '../../../internal/Cursor.js'
13
+ import * as EarnSharePrices from '../../../internal/EarnSharePrices.js'
14
+ import type * as Log from '../../../internal/Log.js'
9
15
  import * as Store from '../../../internal/Store.js'
16
+ import * as Tidx from '../../../internal/Tidx.js'
17
+ import * as Ttl from '../../../internal/Ttl.js'
18
+ import * as Value from '../../../internal/Value.js'
19
+ import * as Viem from '../../../internal/Viem.js'
10
20
  import * as Earn from './earn.js'
11
21
 
12
22
  /**
@@ -27,12 +37,21 @@ type OpenApiSchema = { $ref?: string }
27
37
 
28
38
  type OpenApiOperation = {
29
39
  description?: string
40
+ operationId?: string
30
41
  parameters?: readonly {
31
42
  description?: string
32
43
  name?: string
33
- schema?: { enum?: readonly unknown[]; examples?: readonly unknown[]; pattern?: string }
44
+ schema?: {
45
+ const?: unknown
46
+ enum?: readonly unknown[]
47
+ examples?: readonly unknown[]
48
+ pattern?: string
49
+ }
34
50
  }[]
35
- responses?: Record<string, { content?: { 'application/json'?: { schema?: OpenApiSchema } } }>
51
+ responses?: Record<
52
+ string,
53
+ { content?: { 'application/json'?: { schema?: OpenApiSchema } }; description?: string }
54
+ >
36
55
  }
37
56
 
38
57
  type OpenApiDocument = {
@@ -41,6 +60,7 @@ type OpenApiDocument = {
41
60
  string,
42
61
  {
43
62
  get?: OpenApiOperation
63
+ post?: OpenApiOperation
44
64
  }
45
65
  >
46
66
  }
@@ -57,6 +77,435 @@ function errorSchemas(document: OpenApiDocument, operation: OpenApiOperation | u
57
77
  )
58
78
  }
59
79
 
80
+ function successSchema(operation: OpenApiOperation | undefined) {
81
+ return operation?.responses?.['200']?.content?.['application/json']?.schema
82
+ }
83
+
84
+ function resolveSchema(
85
+ document: OpenApiDocument,
86
+ value: unknown,
87
+ seen: ReadonlySet<string> = new Set(),
88
+ ): unknown {
89
+ if (Array.isArray(value)) return value.map((item) => resolveSchema(document, item, seen))
90
+ if (!value || typeof value !== 'object') return value
91
+ const object = value as Record<string, unknown>
92
+ if (typeof object['$ref'] === 'string') {
93
+ const name = object['$ref'].split('/').at(-1)
94
+ if (!name || seen.has(name)) return value
95
+ return resolveSchema(document, document.components?.schemas?.[name], new Set([...seen, name]))
96
+ }
97
+ return Object.fromEntries(
98
+ Object.entries(object).map(([key, item]) => [key, resolveSchema(document, item, seen)]),
99
+ )
100
+ }
101
+
102
+ describe('schema.registerRewardEligibility.Body', () => {
103
+ test('parses a reward eligibility registration', () => {
104
+ expect(
105
+ Earn.schema.registerRewardEligibility.Body.parse({
106
+ chainId: 4217,
107
+ vaultAddress: `0x${'11'.repeat(20)}`,
108
+ walletAddress: `0x${'22'.repeat(20)}`,
109
+ }),
110
+ ).toMatchInlineSnapshot(`
111
+ {
112
+ "chainId": 4217,
113
+ "vaultAddress": "0x1111111111111111111111111111111111111111",
114
+ "walletAddress": "0x2222222222222222222222222222222222222222",
115
+ }
116
+ `)
117
+ })
118
+
119
+ test('rejects missing, invalid, and unknown fields', () => {
120
+ expect({
121
+ invalidAddress: Earn.schema.registerRewardEligibility.Body.safeParse({
122
+ chainId: 4217,
123
+ vaultAddress: `0x${'11'.repeat(20)}`,
124
+ walletAddress: 'invalid',
125
+ }).success,
126
+ invalidChainId: Earn.schema.registerRewardEligibility.Body.safeParse({
127
+ chainId: 0,
128
+ vaultAddress: `0x${'11'.repeat(20)}`,
129
+ walletAddress: `0x${'22'.repeat(20)}`,
130
+ }).success,
131
+ invalidTransactionHash: Earn.schema.registerRewardEligibility.Body.safeParse({
132
+ chainId: 4217,
133
+ transactionHash: '0x1234',
134
+ vaultAddress: `0x${'11'.repeat(20)}`,
135
+ walletAddress: `0x${'22'.repeat(20)}`,
136
+ }).success,
137
+ missing: Earn.schema.registerRewardEligibility.Body.safeParse({
138
+ chainId: 4217,
139
+ vaultAddress: `0x${'11'.repeat(20)}`,
140
+ }).success,
141
+ unknown: Earn.schema.registerRewardEligibility.Body.safeParse({
142
+ chainId: 4217,
143
+ source: 'other',
144
+ sourceInteractionId: 'interaction_1',
145
+ transactionHash: `0x${'33'.repeat(32)}`,
146
+ vaultAddress: `0x${'11'.repeat(20)}`,
147
+ walletAddress: `0x${'22'.repeat(20)}`,
148
+ }).success,
149
+ }).toEqual({
150
+ invalidAddress: false,
151
+ invalidChainId: false,
152
+ invalidTransactionHash: false,
153
+ missing: false,
154
+ unknown: false,
155
+ })
156
+ })
157
+ })
158
+
159
+ describe('schema.getRewardEligibility.Query', () => {
160
+ test('accepts up to 100 items per page', () => {
161
+ expect([
162
+ Earn.schema.getRewardEligibility.Query.parse({
163
+ chainId: 4217,
164
+ limit: '100',
165
+ vaultAddress: `0x${'11'.repeat(20)}`,
166
+ }).limit,
167
+ Earn.schema.getRewardEligibility.Query.parse({
168
+ chainId: 4217,
169
+ limit: '101',
170
+ vaultAddress: `0x${'11'.repeat(20)}`,
171
+ }).limit,
172
+ ]).toMatchInlineSnapshot(`
173
+ [
174
+ 100,
175
+ 100,
176
+ ]
177
+ `)
178
+ })
179
+ })
180
+
181
+ describe('POST /v1/earn/rewards/eligibility', () => {
182
+ const body = {
183
+ chainId: Viem.chainId.mainnet,
184
+ transactionHash: Schema.Hash.parse(`0x${'33'.repeat(32)}`),
185
+ vaultAddress: `0x${'Aa'.repeat(20)}`,
186
+ walletAddress: `0x${'Bb'.repeat(20)}`,
187
+ }
188
+ const legacyKey = {
189
+ id: 'key_rewards_writer',
190
+ orgId: 'org_test',
191
+ scopes: ['rewards:write'],
192
+ token: 'secret_rewards_writer',
193
+ } satisfies TestApp.kvStore.Key
194
+ const superAdminSecret = 'secret_rewards_super_admin'
195
+ const auth = { headers: { authorization: `Bearer ${superAdminSecret}` } }
196
+
197
+ test('is omitted from public OpenAPI documentation', async () => {
198
+ const app = TestApp.create()
199
+ // SAFETY: `/openapi.json` is the app's generated OpenAPI document.
200
+ const document = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
201
+
202
+ expect(document.paths['/v1/earn/rewards/eligibility']?.post).toBeUndefined()
203
+ })
204
+
205
+ test('requires authentication', async () => {
206
+ const response = await TestApp.create().request('/v1/earn/rewards/eligibility', {
207
+ body: JSON.stringify(body),
208
+ headers: { 'content-type': 'application/json' },
209
+ method: 'POST',
210
+ })
211
+
212
+ expect(response.status).toBe(401)
213
+ })
214
+
215
+ test('rejects an ordinary authenticated key', async () => {
216
+ const response = await TestApp.create().request('/v1/earn/rewards/eligibility', {
217
+ ...TestApp.auth,
218
+ body: JSON.stringify(body),
219
+ headers: { ...TestApp.auth.headers, 'content-type': 'application/json' },
220
+ method: 'POST',
221
+ })
222
+
223
+ expect(response.status).toBe(403)
224
+ })
225
+
226
+ test('rejects a legacy rewards writer key', async () => {
227
+ const response = await TestApp.create({ auth: { keys: [legacyKey] } }).request(
228
+ '/v1/earn/rewards/eligibility',
229
+ {
230
+ body: JSON.stringify(body),
231
+ headers: {
232
+ authorization: `Bearer ${legacyKey.token}`,
233
+ 'content-type': 'application/json',
234
+ },
235
+ method: 'POST',
236
+ },
237
+ )
238
+
239
+ expect(response.status).toBe(403)
240
+ })
241
+
242
+ test('accepts a provisioned wildcard key', async () => {
243
+ const key = {
244
+ id: 'key_rewards_operator',
245
+ orgId: 'org_tempo',
246
+ scopes: [Scope.wildcard],
247
+ token: 'secret_rewards_operator',
248
+ } satisfies TestApp.kvStore.Key
249
+ const response = await TestApp.create({ auth: { keys: [key] } }).request(
250
+ '/v1/earn/rewards/eligibility',
251
+ {
252
+ body: JSON.stringify(body),
253
+ headers: {
254
+ authorization: `Bearer ${key.token}`,
255
+ 'content-type': 'application/json',
256
+ },
257
+ method: 'POST',
258
+ },
259
+ )
260
+
261
+ expect(response.status).toBe(200)
262
+ })
263
+
264
+ test('persists the first write and idempotently refreshes a replay', async () => {
265
+ const db = TestApp.database()
266
+ const app = TestApp.create({ auth: { superAdmin: { secret: superAdminSecret } }, db })
267
+ const request = (input = body) =>
268
+ app.request('/v1/earn/rewards/eligibility', {
269
+ ...auth,
270
+ body: JSON.stringify(input),
271
+ headers: { ...auth.headers, 'content-type': 'application/json' },
272
+ method: 'POST',
273
+ })
274
+
275
+ const first = await TestApp.json(
276
+ await request(),
277
+ Earn.schema.registerRewardEligibility.Response,
278
+ )
279
+ const replay = await TestApp.json(
280
+ await request({
281
+ ...body,
282
+ transactionHash: `0x${'44'.repeat(32)}`,
283
+ }),
284
+ Earn.schema.registerRewardEligibility.Response,
285
+ )
286
+
287
+ expect({ first, replay }).toMatchObject({
288
+ first: {
289
+ association: {
290
+ vaultAddress: body.vaultAddress.toLowerCase(),
291
+ walletAddress: body.walletAddress.toLowerCase(),
292
+ },
293
+ created: true,
294
+ },
295
+ replay: {
296
+ association: {
297
+ firstRegisteredAt: first.association.firstRegisteredAt,
298
+ transactionHash: `0x${'44'.repeat(32)}`,
299
+ },
300
+ created: false,
301
+ },
302
+ })
303
+ })
304
+
305
+ test('rejects invalid and unsupported chain input', async () => {
306
+ const app = TestApp.create({ auth: { superAdmin: { secret: superAdminSecret } } })
307
+ const invalid = await app.request('/v1/earn/rewards/eligibility', {
308
+ ...auth,
309
+ body: JSON.stringify({ ...body, walletAddress: 'invalid' }),
310
+ headers: { ...auth.headers, 'content-type': 'application/json' },
311
+ method: 'POST',
312
+ })
313
+ const unsupported = await app.request('/v1/earn/rewards/eligibility', {
314
+ ...auth,
315
+ body: JSON.stringify({ ...body, chainId: 9_999_999 }),
316
+ headers: { ...auth.headers, 'content-type': 'application/json' },
317
+ method: 'POST',
318
+ })
319
+
320
+ expect([invalid.status, unsupported.status]).toEqual([400, 400])
321
+ })
322
+
323
+ test('accepts supported chains without data upstreams', async () => {
324
+ const chainId = 31_337
325
+ const response = await TestApp.create({
326
+ auth: { superAdmin: { secret: superAdminSecret } },
327
+ rpc: { url: {} },
328
+ supportedChainIds: [chainId],
329
+ tidx: { baseUrl: {} },
330
+ }).request('/v1/earn/rewards/eligibility', {
331
+ ...auth,
332
+ body: JSON.stringify({ ...body, chainId }),
333
+ headers: { ...auth.headers, 'content-type': 'application/json' },
334
+ method: 'POST',
335
+ })
336
+
337
+ expect(response.status).toBe(200)
338
+ })
339
+
340
+ test('accepts configured Zones for super admins', async () => {
341
+ const zone = TestApp.zone({
342
+ chainId: 421_700_001,
343
+ rpcUrl: 'https://zone.example',
344
+ })
345
+ const response = await TestApp.create({
346
+ auth: { superAdmin: { secret: superAdminSecret } },
347
+ zones: [zone],
348
+ }).request('/v1/earn/rewards/eligibility', {
349
+ ...auth,
350
+ body: JSON.stringify({ ...body, chainId: zone.id }),
351
+ headers: { ...auth.headers, 'content-type': 'application/json' },
352
+ method: 'POST',
353
+ })
354
+
355
+ expect(response.status).toBe(200)
356
+ })
357
+
358
+ test('surfaces database failures as operational errors', async () => {
359
+ const app = TestApp.create({
360
+ auth: { superAdmin: { secret: superAdminSecret } },
361
+ db: () => {
362
+ throw new Error('database unavailable')
363
+ },
364
+ })
365
+ const response = await app.request('/v1/earn/rewards/eligibility', {
366
+ ...auth,
367
+ body: JSON.stringify(body),
368
+ headers: { ...auth.headers, 'content-type': 'application/json' },
369
+ method: 'POST',
370
+ })
371
+
372
+ expect(response.status).toBe(500)
373
+ })
374
+ })
375
+
376
+ describe('GET /v1/earn/rewards/eligibility', () => {
377
+ const readKey = {
378
+ id: 'key_rewards_reader',
379
+ orgId: 'org_test',
380
+ scopes: ['rewards:read'],
381
+ token: 'secret_rewards_reader',
382
+ } satisfies TestApp.kvStore.Key
383
+ const vaultAddress = Schema.Address.parse(`0x${'Aa'.repeat(20)}`)
384
+ const input = {
385
+ chainId: Viem.chainId.mainnet,
386
+ transactionHash: Schema.Hash.parse(`0x${'33'.repeat(32)}`),
387
+ vaultAddress,
388
+ walletAddress: Schema.Address.parse(`0x${'Bb'.repeat(20)}`),
389
+ } as const
390
+
391
+ test('is omitted from public OpenAPI documentation', async () => {
392
+ const app = TestApp.create({ auth: { keys: [readKey] } })
393
+ // SAFETY: `/openapi.json` is the app's generated OpenAPI document.
394
+ const document = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
395
+
396
+ expect(document.paths['/v1/earn/rewards/eligibility']?.get).toBeUndefined()
397
+ })
398
+
399
+ test('requires the read scope', async () => {
400
+ const response = await TestApp.create().request(
401
+ `/v1/earn/rewards/eligibility?chainId=4217&vaultAddress=${vaultAddress}`,
402
+ TestApp.auth,
403
+ )
404
+
405
+ expect(response.status).toBe(403)
406
+ })
407
+
408
+ test('reads supported chains without data upstreams', async () => {
409
+ const chainId = 31_337
410
+ const db = TestApp.database()
411
+ await RewardEligibilityAssociations.upsert(db, { ...input, chainId })
412
+ const response = await TestApp.create({
413
+ auth: { keys: [readKey] },
414
+ db,
415
+ rpc: { url: {} },
416
+ supportedChainIds: [chainId],
417
+ tidx: { baseUrl: {} },
418
+ }).request(`/v1/earn/rewards/eligibility?chainId=${chainId}&vaultAddress=${vaultAddress}`, {
419
+ headers: { authorization: `Bearer ${readKey.token}` },
420
+ })
421
+
422
+ expect(response.status).toBe(200)
423
+ })
424
+
425
+ test('rejects sandbox reads from mainnet-backed Zones', async () => {
426
+ const zone = TestApp.zone({
427
+ chainId: 421_700_001,
428
+ rpcUrl: 'https://mainnet-zone.example',
429
+ sourceChainId: Viem.chainId.mainnet,
430
+ })
431
+ const key = {
432
+ ...readKey,
433
+ environment: 'sandbox',
434
+ scopes: [...readKey.scopes, `zone:${zone.id}:read`],
435
+ } satisfies TestApp.kvStore.Key
436
+ const response = await TestApp.create({ auth: { keys: [key] }, zones: [zone] }).request(
437
+ `/v1/earn/rewards/eligibility?chainId=${zone.id}&vaultAddress=${vaultAddress}`,
438
+ { headers: { authorization: `Bearer ${key.token}` } },
439
+ )
440
+
441
+ expect(response.status).toBe(403)
442
+ })
443
+
444
+ test('rejects sandbox reads from Zones without a source chain', async () => {
445
+ const { sourceId: _sourceId, ...zone } = TestApp.zone({
446
+ chainId: 421_700_001,
447
+ rpcUrl: 'https://unknown-zone.example',
448
+ })
449
+ const key = {
450
+ ...readKey,
451
+ environment: 'sandbox',
452
+ scopes: [...readKey.scopes, `zone:${zone.id}:read`],
453
+ } satisfies TestApp.kvStore.Key
454
+ const response = await TestApp.create({ auth: { keys: [key] }, zones: [zone] }).request(
455
+ `/v1/earn/rewards/eligibility?chainId=${zone.id}&vaultAddress=${vaultAddress}`,
456
+ { headers: { authorization: `Bearer ${key.token}` } },
457
+ )
458
+
459
+ expect(response.status).toBe(403)
460
+ })
461
+
462
+ test('returns checkpointed pages that exclude later registrations', async () => {
463
+ const db = TestApp.database()
464
+ const app = TestApp.create({ auth: { keys: [readKey] }, db })
465
+ const wallets = ['bb', 'cc', 'dd', 'ee', 'ff', '11']
466
+ for (const wallet of wallets)
467
+ await RewardEligibilityAssociations.upsert(db, {
468
+ ...input,
469
+ walletAddress: Schema.Address.parse(`0x${wallet.repeat(20)}`),
470
+ })
471
+ const request = (cursor?: string) => {
472
+ const query = new URLSearchParams({
473
+ chainId: String(input.chainId),
474
+ limit: '5',
475
+ vaultAddress,
476
+ })
477
+ if (cursor) query.set('cursor', cursor)
478
+ return app.request(`/v1/earn/rewards/eligibility?${query}`, {
479
+ headers: { authorization: `Bearer ${readKey.token}` },
480
+ })
481
+ }
482
+
483
+ const first = await TestApp.json(await request(), Earn.schema.getRewardEligibility.Response)
484
+ await RewardEligibilityAssociations.upsert(db, {
485
+ ...input,
486
+ walletAddress: Schema.Address.parse(`0x${'22'.repeat(20)}`),
487
+ })
488
+ if (!first.nextCursor) throw new Error('Expected another eligibility page.')
489
+ const next = await TestApp.json(
490
+ await request(first.nextCursor),
491
+ Earn.schema.getRewardEligibility.Response,
492
+ )
493
+
494
+ expect({ first, next }).toMatchObject({
495
+ first: {
496
+ chainId: input.chainId,
497
+ data: wallets.slice(0, 5).map((wallet) => `0x${wallet.repeat(20)}`),
498
+ vaultAddress,
499
+ },
500
+ next: {
501
+ chainId: input.chainId,
502
+ data: [`0x${'11'.repeat(20)}`],
503
+ nextCursor: null,
504
+ vaultAddress,
505
+ },
506
+ })
507
+ })
508
+ })
60
509
  describe('schema.getEarnVaults.Query', () => {
61
510
  test('parses collection filters', () => {
62
511
  const query = Earn.schema.getEarnVaults.Query.parse({
@@ -106,6 +555,64 @@ describe('schema.getEarnVaults.Query', () => {
106
555
  })
107
556
  })
108
557
 
558
+ describe('parseIndexedBlockAtOrBefore', () => {
559
+ test('rejects an empty upstream result before classifying the boundary', () => {
560
+ expect(() =>
561
+ Earn.parseIndexedBlockAtOrBefore({
562
+ asOf: '2026-07-21T09:00:00.000Z',
563
+ rows: [],
564
+ }),
565
+ ).toThrowErrorMatchingInlineSnapshot(
566
+ `[Error: TIDX returned no indexed head for a historical Earn position.]`,
567
+ )
568
+ })
569
+
570
+ test('classifies a missing boundary only after validating the indexed head', () => {
571
+ expect(() =>
572
+ Earn.parseIndexedBlockAtOrBefore({
573
+ asOf: '2026-07-21T09:00:00.000Z',
574
+ rows: [{ kind: 'head', num: 10, timestamp: '2026-07-21T10:00:00.000Z' }],
575
+ }),
576
+ ).toThrowErrorMatchingInlineSnapshot(
577
+ `[Earn.IndexedBlockNotFoundError: No indexed block exists at or before 2026-07-21T09:00:00.000Z.]`,
578
+ )
579
+ })
580
+ })
581
+
582
+ describe('schema.getEarnVaultSharePrices.Query', () => {
583
+ test('defaults to daily observations and accepts a 31-point range', () => {
584
+ expect(
585
+ Earn.schema.getEarnVaultSharePrices.Query.parse({
586
+ from: '2026-01-01T00:00:00Z',
587
+ to: '2026-01-31T00:00:00Z',
588
+ }).interval,
589
+ ).toBe('day')
590
+ })
591
+
592
+ test('rejects reversed and oversized ranges', () => {
593
+ expect({
594
+ oversized: Earn.schema.getEarnVaultSharePrices.Query.safeParse({
595
+ from: '2026-01-01T00:00:00Z',
596
+ to: '2026-02-01T00:00:00Z',
597
+ }).success,
598
+ reversed: Earn.schema.getEarnVaultSharePrices.Query.safeParse({
599
+ from: '2026-01-02T00:00:00Z',
600
+ to: '2026-01-01T00:00:00Z',
601
+ }).success,
602
+ }).toEqual({ oversized: false, reversed: false })
603
+ })
604
+ })
605
+
606
+ describe('schema.getEarnVaultPosition.Query', () => {
607
+ test('accepts offset-bearing ISO 8601 timestamps', () => {
608
+ expect(
609
+ Earn.schema.getEarnVaultPosition.Query.parse({
610
+ asOf: '2026-07-21T10:00:00+01:00',
611
+ }).asOf,
612
+ ).toBe('2026-07-21T10:00:00+01:00')
613
+ })
614
+ })
615
+
109
616
  describe('schema.getEarnAddressPositions.Query', () => {
110
617
  test('parses collection filters', () => {
111
618
  const query = Earn.schema.getEarnAddressPositions.Query.parse({
@@ -133,6 +640,32 @@ describe('schema.getEarnAddressPositions.Query', () => {
133
640
  })
134
641
  })
135
642
 
643
+ describe('schema.getEarnVaultPosition.Response', () => {
644
+ test('accepts distinct position and value tokens', () => {
645
+ const position = {
646
+ account: TestEarn.positionAddress,
647
+ assetAllowance: '0',
648
+ assetBalance: '250000000',
649
+ assetToken: TestEarn.assetTokenAddress,
650
+ chainId: TestEarn.chain.id,
651
+ id: TestEarn.positionAddress,
652
+ shareAllowance: '0',
653
+ shareBalance: '99500000',
654
+ shareToken: `0x20c0${'11'.repeat(18)}`,
655
+ value: '100000000',
656
+ valueToken: TestEarn.assetTokenAddress,
657
+ }
658
+
659
+ expect(
660
+ Earn.schema.getEarnVaultPosition.Response.safeParse({
661
+ ...position,
662
+ chainId: TestEarn.zone.id,
663
+ valueToken: `0x20c0${'22'.repeat(18)}`,
664
+ }).success,
665
+ ).toBe(true)
666
+ })
667
+ })
668
+
136
669
  describe('schema.getEarnVaultEarnings.Response', () => {
137
670
  test('enforces status-specific earnings fields', () => {
138
671
  const response = {
@@ -181,6 +714,8 @@ describe('schema.getEarnVaultEarnings.Response', () => {
181
714
  lifetimeEarnings: '0',
182
715
  period: 'lifetime',
183
716
  status: 'complete',
717
+ totalDeposited: '1000000',
718
+ totalWithdrawn: '500000',
184
719
  }).success,
185
720
  pending: Earn.schema.getEarnVaultEarnings.Response.safeParse({
186
721
  ...response,
@@ -199,9 +734,10 @@ describe('schema.getEarnVaultEarnings.Response', () => {
199
734
  }).success,
200
735
  pendingWithEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
201
736
  ...response,
202
- lifetimeEarnings: '0',
203
737
  period: 'lifetime',
204
738
  status: 'pending_redemption',
739
+ totalDeposited: '1000000',
740
+ totalWithdrawn: '500000',
205
741
  }).success,
206
742
  window: Earn.schema.getEarnVaultEarnings.Response.safeParse({
207
743
  ...response,
@@ -229,10 +765,10 @@ describe('schema.getEarnVaultEarnings.Response', () => {
229
765
  "incompleteWindow": true,
230
766
  "incompleteWithEarnings": false,
231
767
  "lifetime": true,
232
- "pending": true,
768
+ "pending": false,
233
769
  "pendingActive": false,
234
770
  "pendingWindow": true,
235
- "pendingWithEarnings": false,
771
+ "pendingWithEarnings": true,
236
772
  "window": true,
237
773
  "windowWithActiveEarnings": false,
238
774
  "windowWithoutEarnings": false,
@@ -241,6 +777,197 @@ describe('schema.getEarnVaultEarnings.Response', () => {
241
777
  })
242
778
  })
243
779
 
780
+ describe('alignZoneSourceBlock', () => {
781
+ test('caps Zone reports at the L1 portal watermark', () => {
782
+ expect([
783
+ Earn.alignZoneSourceBlock({ reported: 122n, verified: 123n }),
784
+ Earn.alignZoneSourceBlock({ reported: 124n, verified: 123n }),
785
+ Earn.alignZoneSourceBlock({ verified: 123n }),
786
+ ]).toStrictEqual([122n, 123n, 123n])
787
+ })
788
+ })
789
+
790
+ describe('serializeVault', () => {
791
+ test('includes valuations and uses the reward target as the net APY floor', () => {
792
+ const assetToken = {
793
+ address: TestEarn.assetTokenAddress,
794
+ currency: 'USD',
795
+ decimals: 6,
796
+ id: TestEarn.assetTokenAddress,
797
+ name: 'Bridge Test PATHUSD',
798
+ symbol: 'btPATHUSD',
799
+ verified: true,
800
+ } as const
801
+ const shareTokenAddress = `0x20c0${'11'.repeat(18)}` as const
802
+ const options = {
803
+ apy: {
804
+ asOf: '2026-08-26T14:00:00.000Z',
805
+ methodology: 'share-price-growth-annualized:v1',
806
+ net: '0.041200',
807
+ window: '7d',
808
+ },
809
+ assetToken,
810
+ discovery: {
811
+ assetToken,
812
+ chainId: TestEarn.chain.id,
813
+ engine: { address: TestEarn.vaultAddress, type: 'erc4626', venue: null },
814
+ instantLiquidity: '500000000',
815
+ sharePrice: '1000000',
816
+ shareToken: {
817
+ address: shareTokenAddress,
818
+ currency: 'USD',
819
+ decimals: 6,
820
+ name: 'Bridge Test PATHUSD Earn',
821
+ symbol: 'btPATHUSDE',
822
+ },
823
+ state: {
824
+ depositsPaused: false,
825
+ engineShares: '1000000000',
826
+ feesActive: true,
827
+ isAccountingAligned: true,
828
+ openRedeemRequestCount: 0,
829
+ totalAssets: '1000000000',
830
+ totalEarnShares: '1000000000',
831
+ },
832
+ vaultAddress: TestEarn.vaultAddress,
833
+ },
834
+ include: ['apy', 'tvl'],
835
+ instantLiquidityValue: {
836
+ amount: '500000000',
837
+ currency: 'USD',
838
+ decimals: 6,
839
+ formatted: '500',
840
+ },
841
+ rewards: [
842
+ {
843
+ asset: {
844
+ address: shareTokenAddress,
845
+ chainId: TestEarn.chain.id,
846
+ decimals: 6,
847
+ },
848
+ calculatedThrough: 1788134700,
849
+ campaignId: 'rewards-test-moderato-boost-rewards',
850
+ currentRewardsAprBps: 195,
851
+ endsAt: 1790812800,
852
+ startsAt: 1788134400,
853
+ targetTotalAprBps: 700,
854
+ },
855
+ ],
856
+ shareToken: {
857
+ address: shareTokenAddress,
858
+ currency: 'USD',
859
+ decimals: 6,
860
+ id: shareTokenAddress,
861
+ name: 'Bridge Test PATHUSD Earn',
862
+ symbol: 'btPATHUSDE',
863
+ verified: false,
864
+ },
865
+ tvl: { amount: '1000000000', currency: 'USD', decimals: 6, formatted: '1000' },
866
+ zoneRoutes: [],
867
+ zones: [],
868
+ } satisfies Earn.serializeVault.Options
869
+ const vault = Earn.serializeVault(options)
870
+
871
+ expect(vault.instantLiquidityValue).toStrictEqual({
872
+ amount: '500000000',
873
+ currency: 'USD',
874
+ decimals: 6,
875
+ formatted: '500',
876
+ })
877
+ expect(vault.apy).toMatchInlineSnapshot(`
878
+ {
879
+ "asOf": "2026-08-26T14:00:00.000Z",
880
+ "methodology": "share-price-growth-annualized:v1",
881
+ "net": "0.0700",
882
+ "rewards": [
883
+ {
884
+ "apr": "0.0195",
885
+ "asset": {
886
+ "address": "0x20c0111111111111111111111111111111111111",
887
+ "chainId": 42431,
888
+ "decimals": 6,
889
+ },
890
+ "calculatedThrough": 1788134700,
891
+ "campaignId": "rewards-test-moderato-boost-rewards",
892
+ "endsAt": 1790812800,
893
+ "startsAt": 1788134400,
894
+ "targetTotalApr": "0.0700",
895
+ },
896
+ ],
897
+ "vault": "0.041200",
898
+ "window": "7d",
899
+ }
900
+ `)
901
+ const reward = options.rewards[0]
902
+ if (!reward) throw new Error('Expected reward fixture.')
903
+ // Rewards cannot produce a complete APY without a measured vault component.
904
+ expect(
905
+ Earn.serializeVault({
906
+ ...options,
907
+ apy: null,
908
+ rewards: [{ ...reward, currentRewardsAprBps: 700 }],
909
+ }).apy,
910
+ ).toBeNull()
911
+ expect(
912
+ Earn.serializeVault({
913
+ ...options,
914
+ apy: null,
915
+ rewards: [{ ...reward, currentRewardsAprBps: null }],
916
+ }).apy,
917
+ ).toBeNull()
918
+ expect(Earn.serializeVault({ ...options, apy: null, rewards: undefined }).apy).toBeNull()
919
+ expect(
920
+ Earn.serializeVault({
921
+ ...options,
922
+ rewards: [
923
+ ...options.rewards,
924
+ {
925
+ ...reward,
926
+ campaignId: 'second-reward',
927
+ currentRewardsAprBps: 5,
928
+ targetTotalAprBps: 800,
929
+ },
930
+ ],
931
+ }).apy?.net,
932
+ ).toBe('0.0800')
933
+ expect(Earn.serializeVault({ ...options, rewards: undefined }).apy).toMatchObject({
934
+ net: '0.041200',
935
+ vault: '0.041200',
936
+ })
937
+ expect(
938
+ Earn.serializeVault({
939
+ ...options,
940
+ rewards: [{ ...reward, currentRewardsAprBps: null }],
941
+ }).apy,
942
+ ).toMatchObject({
943
+ net: '0.0700',
944
+ rewards: [{ apr: null, targetTotalApr: '0.0700' }],
945
+ vault: '0.041200',
946
+ })
947
+ expect(
948
+ Earn.serializeVault({
949
+ ...options,
950
+ apy: { ...options.apy, net: '0.080000' },
951
+ }).apy?.net,
952
+ ).toBe('0.080000')
953
+ vi.useFakeTimers({ now: new Date('2026-09-01T01:30:00.000Z'), toFake: ['Date'] })
954
+ try {
955
+ expect(
956
+ Earn.serializeVault({
957
+ ...options,
958
+ rewards: [{ ...reward, startsAt: 1_788_229_800 }],
959
+ }).apy,
960
+ ).toMatchObject({
961
+ net: '0.041200',
962
+ rewards: [{ targetTotalApr: '0.0700' }],
963
+ vault: '0.041200',
964
+ })
965
+ } finally {
966
+ vi.useRealTimers()
967
+ }
968
+ })
969
+ })
970
+
244
971
  describe('schema.getEarnVaultEarnings.Query', () => {
245
972
  test('defaults to lifetime and accepts active and trailing earnings', () => {
246
973
  expect([
@@ -267,6 +994,19 @@ describe('calculateWindowEarnings', () => {
267
994
  })
268
995
 
269
996
  describe('calculateActiveCostBasis', () => {
997
+ test('ignores zero-share transfers', () => {
998
+ expect(
999
+ Earn.calculateActiveCostBasis({
1000
+ actions: [
1001
+ { assets: 100n, kind: 'deposit', shares: 100n },
1002
+ { kind: 'unknown', shares: 0n },
1003
+ { kind: 'redeem', shares: 0n },
1004
+ ],
1005
+ shareBalance: 100n,
1006
+ }),
1007
+ ).toBe(100n)
1008
+ })
1009
+
270
1010
  test('allocates weighted-average basis across partial exits', () => {
271
1011
  expect(
272
1012
  Earn.calculateActiveCostBasis({
@@ -280,6 +1020,29 @@ describe('calculateActiveCostBasis', () => {
280
1020
  ).toBe(200n)
281
1021
  })
282
1022
 
1023
+ test('allocates zero-cost reward shares across partial exits without erasing unknown basis', () => {
1024
+ const reward = { assets: 0n, kind: 'deposit', shares: 20n } as const
1025
+ expect([
1026
+ Earn.calculateActiveCostBasis({
1027
+ actions: [
1028
+ { assets: 100n, kind: 'deposit', shares: 100n },
1029
+ reward,
1030
+ { kind: 'redeem', shares: 60n },
1031
+ ],
1032
+ shareBalance: 60n,
1033
+ }),
1034
+ Earn.calculateActiveCostBasis({
1035
+ actions: [{ kind: 'unknown', shares: 100n }, reward],
1036
+ shareBalance: 120n,
1037
+ }),
1038
+ ]).toMatchInlineSnapshot(`
1039
+ [
1040
+ 50n,
1041
+ undefined,
1042
+ ]
1043
+ `)
1044
+ })
1045
+
283
1046
  test('calculates remaining basis while an async redemption is pending', () => {
284
1047
  expect(
285
1048
  Earn.calculateActiveCostBasis({
@@ -721,6 +1484,79 @@ describe('GET /v1/earn/vaults/verified', () => {
721
1484
  })
722
1485
 
723
1486
  describe('GET /v1/earn/addresses/:address/positions', () => {
1487
+ test.runIf(Runtime.get().mode === 'testnet')(
1488
+ 'batches lifetime cash flows for multiple positions into one query',
1489
+ async () => {
1490
+ const tidx = Tidx.getClient({
1491
+ chainId: TestEarn.chain.id,
1492
+ tidx: { auth: process.env.TIDX_AUTH, baseUrl: Runtime.get().tidxUrl },
1493
+ })
1494
+ let queryCount = 0
1495
+ const client = new Proxy(tidx, {
1496
+ get(target, property, receiver) {
1497
+ if (property !== 'fetch') return Reflect.get(target, property, receiver)
1498
+ return (...options: Parameters<Tidx.Client['fetch']>) => {
1499
+ queryCount += 1
1500
+ return target.fetch(...options)
1501
+ }
1502
+ },
1503
+ })
1504
+ const emptyVault = Schema.Address.parse(`0x${'11'.repeat(20)}`)
1505
+ const cashFlows = await Earn.getVaultLifetimeCashFlows(client, {
1506
+ account: TestEarn.earningsAddress,
1507
+ blockNumber: 1_000_000_000,
1508
+ vaults: [TestEarn.vaultAddress, emptyVault],
1509
+ })
1510
+
1511
+ expect(queryCount).toBe(1)
1512
+ expect(cashFlows.get(TestEarn.vaultAddress)).toMatchObject({
1513
+ status: 'complete',
1514
+ totalDeposited: expect.stringMatching(/^[1-9]\d*$/),
1515
+ totalWithdrawn: expect.stringMatching(/^\d+$/),
1516
+ })
1517
+ expect(cashFlows.get(emptyVault)).toStrictEqual({
1518
+ status: 'complete',
1519
+ totalDeposited: '0',
1520
+ totalWithdrawn: '0',
1521
+ })
1522
+ },
1523
+ 60_000,
1524
+ )
1525
+ test('requires read access to a selected Zone', async () => {
1526
+ const app = TestApp.create({ cache, zones: [TestEarn.zone] })
1527
+ const response = await app.request(
1528
+ `/v1/earn/addresses/${TestEarn.positionAddress}/positions?chainId=${TestEarn.zone.id}`,
1529
+ { headers: { 'tempo-api-key': TestApp.key.token } },
1530
+ )
1531
+
1532
+ expect(response.status).toBe(403)
1533
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
1534
+ error: { code: 'api_key_forbidden' },
1535
+ })
1536
+ })
1537
+
1538
+ test('rejects Zone earnings includes', async () => {
1539
+ const key = {
1540
+ ...TestApp.key,
1541
+ id: 'key_earn_zone_positions',
1542
+ scopes: ['data:read', `zone:${TestEarn.zone.id}:read`],
1543
+ token: 'secret_earn_zone_positions',
1544
+ } satisfies TestApp.kvStore.Key
1545
+ const app = TestApp.create({ auth: { keys: [key] }, cache, zones: [TestEarn.zone] })
1546
+ const response = await app.request(
1547
+ `/v1/earn/addresses/${TestEarn.positionAddress}/positions?chainId=${TestEarn.zone.id}&include=earnings`,
1548
+ { headers: { 'tempo-api-key': key.token } },
1549
+ )
1550
+
1551
+ expect(response.status).toBe(400)
1552
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
1553
+ error: {
1554
+ code: 'query_invalid',
1555
+ message: 'Zone positions do not support the earnings include.',
1556
+ },
1557
+ })
1558
+ })
1559
+
724
1560
  test('returns chain selection errors', async () => {
725
1561
  const app = TestApp.create({ auth: false, cache })
726
1562
  const invalid = await app.request(
@@ -750,7 +1586,7 @@ describe('GET /v1/earn/addresses/:address/positions', () => {
750
1586
  test('returns an empty verified page without curated vaults', async () => {
751
1587
  const app = TestApp.create({ auth: false, cache })
752
1588
  const response = await app.request(
753
- `/v1/earn/addresses/${TestEarn.positionAddress}/positions?verified=true`,
1589
+ `/v1/earn/addresses/${TestEarn.positionAddress}/positions?include=earnings&verified=true`,
754
1590
  )
755
1591
 
756
1592
  expect(response.status).toBe(200)
@@ -774,7 +1610,7 @@ describe('GET /v1/earn/addresses/:address/positions', () => {
774
1610
  })
775
1611
  const app = TestApp.create({ auth: false, cache, db, zones: [TestEarn.zone] })
776
1612
  const response = await app.request(
777
- `/v1/earn/addresses/${TestEarn.activeEarningsAddress}/positions?chainId=${TestEarn.chain.id}&verified=true&valuation.currency=USD`,
1613
+ `/v1/earn/addresses/${TestEarn.activeEarningsAddress}/positions?chainId=${TestEarn.chain.id}&include=earnings&verified=true&valuation.currency=USD`,
778
1614
  )
779
1615
 
780
1616
  expect(response.status).toBe(200)
@@ -790,6 +1626,7 @@ describe('GET /v1/earn/addresses/:address/positions', () => {
790
1626
  expect(row.shareAmount.decimals).toBe(row.shareToken.decimals)
791
1627
  expect(row.assetAmount.currency).toBe(row.assetToken.currency)
792
1628
  expect(row.assetAmount.decimals).toBe(row.assetToken.decimals)
1629
+ expect(row.lifetimeCashFlows?.status).toMatch(/^(complete|incomplete_history)$/)
793
1630
  // A USD-denominated asset values identically without consulting rates.
794
1631
  expect(row.valuation).toStrictEqual({ amount: row.assetAmount.formatted, currency: 'USD' })
795
1632
  expect(body.meta).toBeUndefined()
@@ -1008,6 +1845,7 @@ describe('GET /v1/earn/vaults/:vaultId', () => {
1008
1845
  // The pinned venue exposes no withdrawal-limit view, so liquidity is
1009
1846
  // absent rather than a restatement of the backing.
1010
1847
  instantLiquidity: vault.instantLiquidity,
1848
+ instantLiquidityValue: vault.instantLiquidityValue,
1011
1849
  isAccountingAligned: vault.state.isAccountingAligned,
1012
1850
  openRedeemRequestCount: vault.state.openRedeemRequestCount,
1013
1851
  pricedPerShare: vault.sharePrice !== null && BigInt(vault.sharePrice.amount) > 0n,
@@ -1019,6 +1857,7 @@ describe('GET /v1/earn/vaults/:vaultId', () => {
1019
1857
  "engineType": "erc4626",
1020
1858
  "feesActive": true,
1021
1859
  "instantLiquidity": null,
1860
+ "instantLiquidityValue": null,
1022
1861
  "isAccountingAligned": true,
1023
1862
  "openRedeemRequestCount": 0,
1024
1863
  "pricedPerShare": true,
@@ -1069,7 +1908,7 @@ describe('GET /v1/earn/vaults/:vaultId', () => {
1069
1908
  const apy = (await TestApp.json(response, Earn.schema.getEarnVault.Response)).apy
1070
1909
  expect(apy).toBeDefined()
1071
1910
  expect(apy).not.toBeNull()
1072
- if (!apy) return
1911
+ if (!apy?.asOf || apy.net === null) throw new Error('Expected a measured APY.')
1073
1912
  expect({
1074
1913
  bucketed: apy.asOf === new Date(apy.asOf).toISOString() && apy.asOf.endsWith(':00.000Z'),
1075
1914
  measured: /^-?\d+\.\d{6}$/.test(apy.net),
@@ -1089,22 +1928,28 @@ describe('GET /v1/earn/vaults/:vaultId', () => {
1089
1928
  )
1090
1929
 
1091
1930
  test.runIf(Runtime.get().mode === 'testnet')(
1092
- 'leaves the rate unmeasured before the vault was deployed',
1931
+ 'leaves the rate unmeasured when the window starts before deployment',
1093
1932
  async () => {
1094
- const app = TestApp.create({ auth: false, cache, zones: [TestEarn.zone] })
1095
- const response = await app.request(
1096
- `/v1/earn/vaults/${TestEarn.vaultAddress}?chainId=${TestEarn.chain.id}&apy.window=30d`,
1097
- )
1933
+ // This vault was deployed on 2026-07-23, so the pinned window begins before deployment.
1934
+ vi.useFakeTimers({ now: new Date('2026-07-24T10:00:00Z'), toFake: ['Date'] })
1935
+ try {
1936
+ const app = TestApp.create({ auth: false, cache, zones: [TestEarn.zone] })
1937
+ const response = await app.request(
1938
+ `/v1/earn/vaults/${TestEarn.vaultAddress}?chainId=${TestEarn.chain.id}&apy.window=30d`,
1939
+ )
1098
1940
 
1099
- expect(response.status).toBe(200)
1100
- const vault = await TestApp.json(response, Earn.schema.getEarnVault.Response)
1101
- expect(vault.apy).toBeNull()
1941
+ expect(response.status).toBe(200)
1942
+ const vault = await TestApp.json(response, Earn.schema.getEarnVault.Response)
1943
+ expect(vault.apy).toBeNull()
1944
+ } finally {
1945
+ vi.useRealTimers()
1946
+ }
1102
1947
  },
1103
1948
  60_000,
1104
1949
  )
1105
1950
 
1106
1951
  test.runIf(Runtime.get().mode === 'testnet')(
1107
- 'defaults to a seven-day window',
1952
+ 'uses a seven-day window when it is measurable',
1108
1953
  async () => {
1109
1954
  const app = TestApp.create({ auth: false, cache, zones: [TestEarn.zone] })
1110
1955
  const path = `/v1/earn/vaults/${TestEarn.vaultAddress}?chainId=${TestEarn.chain.id}`
@@ -1122,13 +1967,46 @@ describe('GET /v1/earn/vaults/:vaultId', () => {
1122
1967
  )
1123
1968
 
1124
1969
  test.runIf(Runtime.get().mode === 'testnet')(
1125
- 'measures rates and values assets only when requested',
1970
+ 'falls back to one hour when the preferred window is unavailable',
1126
1971
  async () => {
1127
- const app = TestApp.create({ auth: false, cache, zones: [TestEarn.zone] })
1128
- const path = `/v1/earn/vaults/${TestEarn.vaultAddress}?chainId=${TestEarn.chain.id}`
1129
- const request = { headers: { 'cache-control': 'no-store' } }
1130
- const [bare, requested] = await Promise.all([
1131
- app.request(path, request),
1972
+ // The vault was deployed on 2026-07-23, so seven days has no opening
1973
+ // quote while the current one-hour rate is measurable.
1974
+ vi.useFakeTimers({ now: new Date('2026-07-24T10:00:00Z'), toFake: ['Date'] })
1975
+ try {
1976
+ const app = TestApp.create({
1977
+ auth: false,
1978
+ cache: { edge: false, store: Store.memory() },
1979
+ zones: [TestEarn.zone],
1980
+ })
1981
+ const path = `/v1/earn/vaults/${TestEarn.vaultAddress}?chainId=${TestEarn.chain.id}`
1982
+ const request = { headers: { 'cache-control': 'no-store' } }
1983
+ const [defaulted, explicit] = await Promise.all([
1984
+ app.request(`${path}&include=apy`, request),
1985
+ app.request(`${path}&apy.window=7d`, request),
1986
+ ])
1987
+
1988
+ expect([defaulted.status, explicit.status]).toStrictEqual([200, 200])
1989
+ const [fallbackVault, explicitVault] = await Promise.all([
1990
+ TestApp.json(defaulted, Earn.schema.getEarnVault.Response),
1991
+ TestApp.json(explicit, Earn.schema.getEarnVault.Response),
1992
+ ])
1993
+ expect(fallbackVault.apy).toMatchObject({ window: '1h' })
1994
+ expect(explicitVault.apy).toBeNull()
1995
+ } finally {
1996
+ vi.useRealTimers()
1997
+ }
1998
+ },
1999
+ 90_000,
2000
+ )
2001
+
2002
+ test.runIf(Runtime.get().mode === 'testnet')(
2003
+ 'measures rates and values assets only when requested',
2004
+ async () => {
2005
+ const app = TestApp.create({ auth: false, cache, zones: [TestEarn.zone] })
2006
+ const path = `/v1/earn/vaults/${TestEarn.vaultAddress}?chainId=${TestEarn.chain.id}`
2007
+ const request = { headers: { 'cache-control': 'no-store' } }
2008
+ const [bare, requested] = await Promise.all([
2009
+ app.request(path, request),
1132
2010
  app.request(`${path}&include=apy,tvl`, request),
1133
2011
  ])
1134
2012
 
@@ -1299,7 +2177,202 @@ describe('GET /v1/earn/vaults/:vaultId', () => {
1299
2177
  )
1300
2178
  })
1301
2179
 
2180
+ describe('GET /v1/earn/vaults/:vaultId/share-prices', () => {
2181
+ test('classifies TIDX failures for dependency alerting', () => {
2182
+ expect([
2183
+ Earn.sharePriceProviderFailure({
2184
+ cause: new EarnSharePrices.MalformedResponseError('malformed row'),
2185
+ chainId: TestEarn.chain.id,
2186
+ }),
2187
+ Earn.sharePriceProviderFailure({
2188
+ cause: new EarnSharePrices.TidxRequestError(new TypeError('network failed')),
2189
+ chainId: TestEarn.chain.id,
2190
+ }),
2191
+ ]).toMatchInlineSnapshot(`
2192
+ [
2193
+ {
2194
+ "chainId": 42431,
2195
+ "failure": "payload",
2196
+ "id": "tidx",
2197
+ "operation": "query",
2198
+ },
2199
+ {
2200
+ "chainId": 42431,
2201
+ "failure": "network",
2202
+ "id": "tidx",
2203
+ "operation": "query",
2204
+ },
2205
+ ]
2206
+ `)
2207
+ })
2208
+
2209
+ test('validates the vault and daily range before reading upstreams', async () => {
2210
+ const app = TestApp.create({ auth: false, cache })
2211
+ const [range, vault] = await Promise.all([
2212
+ app.request(
2213
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/share-prices?from=2026-07-02T00%3A00Z&to=2026-07-01T00%3A00Z`,
2214
+ ),
2215
+ app.request(
2216
+ '/v1/earn/vaults/not-an-address/share-prices?from=2026-07-01T00%3A00Z&to=2026-07-02T00%3A00Z',
2217
+ ),
2218
+ ])
2219
+
2220
+ expect([
2221
+ (await TestApp.json(range, Schema.ErrorResponse)).error.code,
2222
+ (await TestApp.json(vault, Schema.ErrorResponse)).error.code,
2223
+ ]).toStrictEqual(['query_invalid', 'vault_id_invalid'])
2224
+ })
2225
+
2226
+ test('requires both range boundaries', async () => {
2227
+ const response = await TestApp.create({ auth: false, cache }).request(
2228
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/share-prices?from=2026-07-01T00%3A00Z`,
2229
+ )
2230
+
2231
+ expect(response.status).toBe(400)
2232
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2233
+ error: { code: 'query_invalid' },
2234
+ })
2235
+ })
2236
+
2237
+ test.runIf(Runtime.get().mode === 'testnet')(
2238
+ 'negative-caches incompatible vault discovery',
2239
+ async () => {
2240
+ const app = TestApp.create({ auth: false, cache })
2241
+ const url = `/v1/earn/vaults/${TestEarn.assetTokenAddress}/share-prices?chainId=${TestEarn.chain.id}&from=2026-07-01T00%3A00Z&to=2026-07-02T00%3A00Z`
2242
+ const first = await app.request(url)
2243
+ const second = await app.request(url)
2244
+
2245
+ expect({
2246
+ first: {
2247
+ discovery: first.headers.get('server-timing')?.includes('earn_vault_discovery'),
2248
+ status: first.status,
2249
+ },
2250
+ second: {
2251
+ discovery: second.headers.get('server-timing')?.includes('earn_vault_discovery'),
2252
+ status: second.status,
2253
+ },
2254
+ }).toMatchInlineSnapshot(`
2255
+ {
2256
+ "first": {
2257
+ "discovery": true,
2258
+ "status": 404,
2259
+ },
2260
+ "second": {
2261
+ "discovery": false,
2262
+ "status": 404,
2263
+ },
2264
+ }
2265
+ `)
2266
+ },
2267
+ 60_000,
2268
+ )
2269
+
2270
+ test.runIf(Runtime.get().mode === 'testnet')(
2271
+ 'reads historical share prices through a deployed JSON-RPC batch',
2272
+ async () => {
2273
+ const runtime = Runtime.get()
2274
+ const tidx = Tidx.getClient({
2275
+ chainId: runtime.chainId,
2276
+ tidx: { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl },
2277
+ })
2278
+ const indexed = await tidx.fetch({
2279
+ engine: 'clickhouse',
2280
+ query: 'SELECT timestamp FROM blocks ORDER BY num DESC LIMIT 1 OFFSET 100',
2281
+ })
2282
+ const timestamp = Value.toIsoDateTime(indexed.rows[0]?.['timestamp'])
2283
+ if (timestamp === undefined) throw new Error('expected an indexed block')
2284
+ const from = new Date(Date.parse(timestamp) - Ttl.days(1)).toISOString()
2285
+ const response = await TestApp.create({ auth: false, cache }).request(
2286
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/share-prices?chainId=${TestEarn.chain.id}&from=${encodeURIComponent(from)}&to=${encodeURIComponent(timestamp)}`,
2287
+ )
2288
+
2289
+ expect(response.status).toBe(200)
2290
+ const body = await TestApp.json(response, Earn.schema.getEarnVaultSharePrices.Response)
2291
+ expect(body.data).toEqual(
2292
+ expect.arrayContaining([
2293
+ expect.objectContaining({
2294
+ sharePrice: expect.objectContaining({
2295
+ amount: expect.stringMatching(/^\d+$/),
2296
+ decimals: 6,
2297
+ }),
2298
+ timestamp,
2299
+ }),
2300
+ ]),
2301
+ )
2302
+ },
2303
+ 60_000,
2304
+ )
2305
+ })
2306
+
1302
2307
  describe('GET /v1/earn/vaults/:vaultId/positions/:address', () => {
2308
+ test('accepts and validates the wallet APY include', () => {
2309
+ expect(Earn.schema.getEarnVaultPosition.Query.safeParse({ include: ['apy'] }).success).toBe(
2310
+ true,
2311
+ )
2312
+ expect(
2313
+ Earn.schema.getEarnVaultPosition.Response.safeParse({
2314
+ account: TestEarn.positionAddress,
2315
+ apy: {
2316
+ breakdown: [
2317
+ {
2318
+ assetAmount: { baseUnits: '25000000000', decimals: 6, formatted: '25000' },
2319
+ net: '0.07',
2320
+ type: 'boost',
2321
+ },
2322
+ {
2323
+ assetAmount: { baseUnits: '5000000000', decimals: 6, formatted: '5000' },
2324
+ net: '0.02',
2325
+ type: 'base',
2326
+ },
2327
+ ],
2328
+ net: '0.0616666666666667',
2329
+ },
2330
+ assetAllowance: '0',
2331
+ assetBalance: '0',
2332
+ assetToken: TestEarn.assetTokenAddress,
2333
+ chainId: TestEarn.chain.id,
2334
+ id: TestEarn.positionAddress,
2335
+ shareAllowance: '0',
2336
+ shareBalance: '30000000000',
2337
+ shareToken: '0x20c000000000000000000000300e14ab91a10769',
2338
+ value: '30000000000',
2339
+ valueToken: TestEarn.assetTokenAddress,
2340
+ }).success,
2341
+ ).toBe(true)
2342
+ })
2343
+
2344
+ test('requires read access to a selected Zone', async () => {
2345
+ const app = TestApp.create({ cache, zones: [TestEarn.zone] })
2346
+ const response = await app.request(
2347
+ `/v1/earn/vaults/${TestEarn.zoneVaultAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.zone.id}`,
2348
+ { headers: { 'tempo-api-key': TestApp.key.token } },
2349
+ )
2350
+
2351
+ expect(response.status).toBe(403)
2352
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2353
+ error: { code: 'api_key_forbidden' },
2354
+ })
2355
+ })
2356
+
2357
+ test('rejects invalid as-of timestamps', async () => {
2358
+ const app = TestApp.create({ auth: false, cache })
2359
+ const [malformed, unix] = await Promise.all([
2360
+ app.request(
2361
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?asOf=not-a-time`,
2362
+ ),
2363
+ app.request(
2364
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?asOf=1787312400`,
2365
+ ),
2366
+ ])
2367
+
2368
+ for (const response of [malformed, unix]) {
2369
+ expect(response.status).toBe(400)
2370
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2371
+ error: { code: 'query_invalid' },
2372
+ })
2373
+ }
2374
+ })
2375
+
1303
2376
  test('returns chain selection errors', async () => {
1304
2377
  const app = TestApp.create({ auth: false, cache })
1305
2378
  const invalid = await app.request(
@@ -1329,6 +2402,78 @@ describe('GET /v1/earn/vaults/:vaultId/positions/:address', () => {
1329
2402
  })
1330
2403
  })
1331
2404
 
2405
+ test('requires TIDX for historical reads on an RPC-only chain', async () => {
2406
+ const response = await TestApp.create({
2407
+ auth: false,
2408
+ cache,
2409
+ rpc: { url: { 31_337: 'http://127.0.0.1:1' } },
2410
+ tidx: {},
2411
+ }).request(
2412
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=31337&asOf=2026-07-21T09%3A00Z`,
2413
+ )
2414
+
2415
+ expect(response.status).toBe(400)
2416
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2417
+ error: { code: 'chain_id_unsupported' },
2418
+ })
2419
+ })
2420
+
2421
+ test('validates malformed historical timestamps on an RPC-only chain', async () => {
2422
+ const response = await TestApp.create({
2423
+ auth: false,
2424
+ cache,
2425
+ rpc: { url: { 31_337: 'http://127.0.0.1:1' } },
2426
+ tidx: {},
2427
+ }).request(
2428
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=31337&asOf=not-a-time`,
2429
+ )
2430
+
2431
+ expect(response.status).toBe(400)
2432
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2433
+ error: { code: 'query_invalid' },
2434
+ })
2435
+ })
2436
+
2437
+ test('validates the complete historical query on an RPC-only chain', async () => {
2438
+ const app = TestApp.create({
2439
+ auth: false,
2440
+ cache,
2441
+ rpc: { url: { 31_337: 'http://127.0.0.1:1' } },
2442
+ tidx: {},
2443
+ })
2444
+ const path = `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=31337&asOf=2026-07-21T09%3A00Z`
2445
+ const [duplicate, unknown] = await Promise.all([
2446
+ app.request(`${path}&asOf=2026-07-21T10%3A00Z`),
2447
+ app.request(`${path}&unknown=1`),
2448
+ ])
2449
+
2450
+ for (const response of [duplicate, unknown]) {
2451
+ expect(response.status).toBe(400)
2452
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2453
+ error: { code: 'query_invalid' },
2454
+ })
2455
+ }
2456
+ })
2457
+
2458
+ test('rejects reward includes on historical positions', async () => {
2459
+ const response = await TestApp.create({
2460
+ auth: false,
2461
+ cache,
2462
+ rpc: { url: { 31_337: 'http://127.0.0.1:1' } },
2463
+ tidx: { baseUrl: { 31_337: 'http://127.0.0.1:1' } },
2464
+ }).request(
2465
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=31337&asOf=2026-07-21T09%3A00Z&include=rewards`,
2466
+ )
2467
+
2468
+ expect(response.status).toBe(400)
2469
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2470
+ error: {
2471
+ code: 'query_invalid',
2472
+ message: 'Historical position enrichments are not available.',
2473
+ },
2474
+ })
2475
+ })
2476
+
1332
2477
  test('rejects malformed vault position parameters', async () => {
1333
2478
  const response = await TestApp.create({ auth: false, cache }).request(
1334
2479
  '/v1/earn/vaults/not-an-address/positions/not-an-address',
@@ -1349,6 +2494,7 @@ describe('GET /v1/earn/vaults/:vaultId/positions/:address', () => {
1349
2494
  )
1350
2495
 
1351
2496
  expect(response.status).toBe(200)
2497
+ expect(response.headers.get('cache-control')).toBe('no-store')
1352
2498
  const position = await TestApp.json(response, Earn.schema.getEarnVaultPosition.Response)
1353
2499
  expect({
1354
2500
  account: position.account,
@@ -1371,6 +2517,157 @@ describe('GET /v1/earn/vaults/:vaultId/positions/:address', () => {
1371
2517
  "shareToken": true,
1372
2518
  }
1373
2519
  `)
2520
+ expect(position).not.toHaveProperty('apy')
2521
+ },
2522
+ 60_000,
2523
+ )
2524
+
2525
+ test.runIf(Runtime.get().mode === 'testnet')(
2526
+ 'preserves the current position when APY resolution fails',
2527
+ async () => {
2528
+ const entries: Log.Entry[] = []
2529
+ const app = TestApp.create({
2530
+ auth: false,
2531
+ cache: { edge: false, store: Store.memory() },
2532
+ logger: (entry) => void entries.push(entry),
2533
+ tidx: { baseUrl: 'http://127.0.0.1:1' },
2534
+ })
2535
+ const path = `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.chain.id}`
2536
+ const [bareResponse, response] = await Promise.all([
2537
+ app.request(path),
2538
+ app.request(`${path}&include=apy`),
2539
+ ])
2540
+
2541
+ expect(bareResponse.status).toBe(200)
2542
+ expect(response.status).toBe(200)
2543
+ expect(response.headers.get('cache-control')).toBe('no-store')
2544
+ const [bare, position] = await Promise.all([
2545
+ TestApp.json(bareResponse, Earn.schema.getEarnVaultPosition.Response),
2546
+ TestApp.json(response, Earn.schema.getEarnVaultPosition.Response),
2547
+ ])
2548
+ expect(position.apy).toBeNull()
2549
+ expect({ shareBalance: position.shareBalance, value: position.value }).toStrictEqual({
2550
+ shareBalance: bare.shareBalance,
2551
+ value: bare.value,
2552
+ })
2553
+ expect(entries.find((entry) => entry.providerFailures)?.providerFailures).toEqual([
2554
+ {
2555
+ chainId: TestEarn.chain.id,
2556
+ failure: 'network',
2557
+ id: 'tidx',
2558
+ operation: 'query',
2559
+ },
2560
+ ])
2561
+ },
2562
+ 60_000,
2563
+ )
2564
+
2565
+ test.runIf(Runtime.get().mode === 'testnet')(
2566
+ 'preserves the current position when reward APY resolution fails',
2567
+ async () => {
2568
+ const entries: Log.Entry[] = []
2569
+ const app = TestApp.create({
2570
+ auth: false,
2571
+ cache,
2572
+ db: Db.postgres({ connectionString: 'postgresql://127.0.0.1:1/unavailable' }),
2573
+ logger: (entry) => void entries.push(entry),
2574
+ })
2575
+ const path = `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.chain.id}`
2576
+ const [bareResponse, response] = await Promise.all([
2577
+ app.request(path),
2578
+ app.request(`${path}&include=apy`),
2579
+ ])
2580
+
2581
+ expect(bareResponse.status).toBe(200)
2582
+ expect(response.status).toBe(200)
2583
+ expect(response.headers.get('cache-control')).toBe('no-store')
2584
+ const [bare, position] = await Promise.all([
2585
+ TestApp.json(bareResponse, Earn.schema.getEarnVaultPosition.Response),
2586
+ TestApp.json(response, Earn.schema.getEarnVaultPosition.Response),
2587
+ ])
2588
+ expect(position.apy).toBeNull()
2589
+ expect({ shareBalance: position.shareBalance, value: position.value }).toStrictEqual({
2590
+ shareBalance: bare.shareBalance,
2591
+ value: bare.value,
2592
+ })
2593
+ expect(entries.find((entry) => entry.providerFailures)?.providerFailures).toEqual([
2594
+ {
2595
+ chainId: TestEarn.chain.id,
2596
+ failure: 'unknown',
2597
+ id: 'earn-rewards',
2598
+ operation: 'getPositionApy',
2599
+ },
2600
+ ])
2601
+ },
2602
+ 60_000,
2603
+ )
2604
+
2605
+ test.runIf(Runtime.get().mode === 'testnet')(
2606
+ 'includes current reward state from Postgres',
2607
+ async () => {
2608
+ const db = TestApp.database()
2609
+ const now = Math.floor(Date.now() / 1_000)
2610
+ const startsAt = Math.floor(now / 300) * 300
2611
+ await core_EarnVaults.upsert(db, TestEarn.verifiedVault)
2612
+ await RewardCampaigns.upsert(db, {
2613
+ assetAddress: TestEarn.assetTokenAddress,
2614
+ assetDecimals: 6,
2615
+ chainId: TestEarn.chain.id,
2616
+ config: {
2617
+ boostRewards: {
2618
+ endTimestamp: startsAt + 3_600,
2619
+ excludedAddresses: [],
2620
+ funding: { wallet: '0x5000000000000000000000000000000000000005' },
2621
+ intervalSeconds: 300,
2622
+ perUserPrincipalCapAssets: '1000000',
2623
+ startTimestamp: startsAt,
2624
+ targetAnnualRateBps: 700,
2625
+ totalPrincipalCapAssets: '10000000',
2626
+ treasury: '0x6000000000000000000000000000000000000006',
2627
+ },
2628
+ },
2629
+ earnShareAddress: '0x2000000000000000000000000000000000000002',
2630
+ earnShareDecimals: 18,
2631
+ now: startsAt - 1,
2632
+ vaultAddress: TestEarn.vaultAddress,
2633
+ })
2634
+ await RewardAccounts.replace(db, {
2635
+ accounts: [
2636
+ {
2637
+ accrualRemainder: '0',
2638
+ allocatedPrincipalAssets: '100',
2639
+ cumulativeEntitlement: '15',
2640
+ cumulativePaid: '10',
2641
+ deferral: null,
2642
+ eligibilityRegisteredAt: null,
2643
+ lots: [],
2644
+ pendingRewardAssets: '0',
2645
+ publicEarnShares: '100',
2646
+ qualifiedEarnShares: '100',
2647
+ recipient: TestEarn.positionAddress,
2648
+ registrationOrder: '1',
2649
+ updatedAt: new Date().toISOString(),
2650
+ },
2651
+ ],
2652
+ chainId: TestEarn.chain.id,
2653
+ vaultAddress: TestEarn.vaultAddress,
2654
+ })
2655
+ const response = await TestApp.create({ auth: false, cache, db }).request(
2656
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.chain.id}&include=apy,rewards.proof`,
2657
+ )
2658
+
2659
+ expect(response.status).toBe(200)
2660
+ expect(response.headers.get('cache-control')).toBe('no-store')
2661
+ const position = await TestApp.json(response, Earn.schema.getEarnVaultPosition.Response)
2662
+ expect(position.apy).toMatchObject({
2663
+ breakdown: expect.any(Array),
2664
+ net: expect.any(String),
2665
+ })
2666
+ expect(position.rewards).toStrictEqual({
2667
+ cumulativeEntitlement: '15',
2668
+ cumulativePaid: '10',
2669
+ pending: '5',
2670
+ })
1374
2671
  },
1375
2672
  60_000,
1376
2673
  )
@@ -1389,9 +2686,212 @@ describe('GET /v1/earn/vaults/:vaultId/positions/:address', () => {
1389
2686
  },
1390
2687
  60_000,
1391
2688
  )
2689
+
2690
+ test.runIf(Runtime.get().mode === 'testnet')(
2691
+ 'returns a position as of an indexed ISO 8601 timestamp',
2692
+ async () => {
2693
+ const runtime = Runtime.get()
2694
+ const tidx = Tidx.getClient({
2695
+ chainId: runtime.chainId,
2696
+ tidx: { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl },
2697
+ })
2698
+ const indexed = await tidx.fetch({
2699
+ engine: 'clickhouse',
2700
+ query: 'SELECT num, timestamp FROM blocks ORDER BY num DESC LIMIT 1 OFFSET 100',
2701
+ })
2702
+ const blockNumber = Value.toNumber(indexed.rows[0]?.['num'])
2703
+ const asOf = Value.toIsoDateTime(indexed.rows[0]?.['timestamp'])
2704
+ if (blockNumber === undefined || asOf === undefined)
2705
+ throw new Error('expected an indexed block')
2706
+ const response = await TestApp.create({ auth: false, cache }).request(
2707
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.chain.id}&asOf=${encodeURIComponent(asOf)}`,
2708
+ )
2709
+
2710
+ expect(response.status).toBe(200)
2711
+ const position = await TestApp.json(response, Earn.schema.getEarnVaultPosition.Response)
2712
+ expect(position.asOf).toBe(asOf)
2713
+ expect(position.block?.number).toBeGreaterThanOrEqual(blockNumber)
2714
+ expect(position.block?.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/)
2715
+ expect(Date.parse(position.block?.timestamp ?? '')).toBeLessThanOrEqual(Date.parse(asOf))
2716
+ },
2717
+ 60_000,
2718
+ )
2719
+
2720
+ test.runIf(Runtime.get().mode === 'testnet')(
2721
+ 'returns not found before the vault was deployed',
2722
+ async () => {
2723
+ const runtime = Runtime.get()
2724
+ const tidx = Tidx.getClient({
2725
+ chainId: runtime.chainId,
2726
+ tidx: { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl },
2727
+ })
2728
+ const indexed = await tidx.fetch({
2729
+ engine: 'clickhouse',
2730
+ query: 'SELECT timestamp FROM blocks ORDER BY num ASC LIMIT 1',
2731
+ })
2732
+ const asOf = Value.toIsoDateTime(indexed.rows[0]?.['timestamp'])
2733
+ if (asOf === undefined) throw new Error('expected the pre-deployment block to be indexed')
2734
+ const response = await TestApp.create({ auth: false, cache }).request(
2735
+ `/v1/earn/vaults/${TestEarn.indexedDeployment.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.chain.id}&asOf=${encodeURIComponent(asOf)}`,
2736
+ )
2737
+
2738
+ expect(response.status).toBe(404)
2739
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2740
+ error: { code: 'earn_vault_not_found' },
2741
+ })
2742
+ },
2743
+ 60_000,
2744
+ )
2745
+
2746
+ test.runIf(Runtime.get().mode === 'testnet')(
2747
+ 'returns not found for a deployed incompatible contract at a historical block',
2748
+ async () => {
2749
+ const runtime = Runtime.get()
2750
+ const tidx = Tidx.getClient({
2751
+ chainId: runtime.chainId,
2752
+ tidx: { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl },
2753
+ })
2754
+ const indexed = await tidx.fetch({
2755
+ engine: 'clickhouse',
2756
+ query: 'SELECT timestamp FROM blocks ORDER BY num DESC LIMIT 1',
2757
+ })
2758
+ const asOf = Value.toIsoDateTime(indexed.rows[0]?.['timestamp'])
2759
+ if (asOf === undefined) throw new Error('expected an indexed block')
2760
+ const response = await TestApp.create({ auth: false, cache }).request(
2761
+ `/v1/earn/vaults/${TestEarn.assetTokenAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.chain.id}&asOf=${encodeURIComponent(asOf)}`,
2762
+ )
2763
+
2764
+ expect(response.status).toBe(404)
2765
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2766
+ error: { code: 'earn_vault_not_found' },
2767
+ })
2768
+ },
2769
+ 60_000,
2770
+ )
2771
+
2772
+ test.runIf(Runtime.get().mode === 'testnet')(
2773
+ 'rejects timestamps beyond indexed coverage',
2774
+ async () => {
2775
+ const asOf = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString()
2776
+ const response = await TestApp.create({ auth: false, cache }).request(
2777
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/positions/${TestEarn.positionAddress}?chainId=${TestEarn.chain.id}&asOf=${encodeURIComponent(asOf)}`,
2778
+ )
2779
+
2780
+ expect(response.status).toBe(400)
2781
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
2782
+ error: {
2783
+ code: 'query_invalid',
2784
+ message: `Indexed history does not yet cover ${asOf}.`,
2785
+ },
2786
+ })
2787
+ },
2788
+ 60_000,
2789
+ )
1392
2790
  })
1393
2791
 
1394
2792
  describe('GET /v1/earn/vaults/:vaultId/earnings/:address', () => {
2793
+ test.runIf(Runtime.get().mode === 'testnet')(
2794
+ 'counts registered reward payouts without treating external shares as deposits',
2795
+ async () => {
2796
+ const account = '0xa3b9eb50b9d1a3f584fbe1122a7bc82084eaae2f'
2797
+ const chainId = Viem.chainId.mainnet
2798
+ const previousDistributorAddress = '0x6c857bf1fe7de1bc1d5c639c721b2633f8fe34f3'
2799
+ const distributorAddress = '0xa65c6ed737b52543123817977d8d2c0e554bfc2d'
2800
+ const vaultAddress = '0xd730394f3bb85a4828e35fc5e361dcc9f894fa4f'
2801
+ const db = TestApp.database()
2802
+ const errors: Error[] = []
2803
+ const app = TestApp.create({
2804
+ auth: false,
2805
+ cache,
2806
+ db,
2807
+ logger: (_, error) => {
2808
+ if (error) errors.push(error)
2809
+ },
2810
+ tidx: {
2811
+ auth: process.env.TIDX_AUTH,
2812
+ baseUrl: Viem.resolveUrl(process.env.TIDX_URL, chainId) ?? Tidx.url[chainId],
2813
+ },
2814
+ })
2815
+ const read = async (period: 'lifetime' | '30d' | 'active') => {
2816
+ const response = await app.request(
2817
+ `/v1/earn/vaults/${vaultAddress}/earnings/${account}?chainId=${chainId}&period=${period}`,
2818
+ )
2819
+ expect(response.status, errors.map((error) => error.message).join('\n')).toBe(200)
2820
+ return TestApp.json(response, Earn.schema.getEarnVaultEarnings.Response)
2821
+ }
2822
+
2823
+ // This account's indexed share receipts include both vault mints and distributor payouts.
2824
+ expect((await read('lifetime')).status).toBe('incomplete_cost_basis')
2825
+ await core_EarnVaults.upsert(db, {
2826
+ chainId,
2827
+ description: null,
2828
+ label: 'Tempo Cash',
2829
+ privateInputTokens: [],
2830
+ privateOutputTokens: [],
2831
+ vaultAddress,
2832
+ zones: [],
2833
+ })
2834
+ await RewardCampaigns.upsert(db, {
2835
+ assetAddress: '0x20c0000000000000000000000000000000000000',
2836
+ assetDecimals: 6,
2837
+ chainId,
2838
+ config: {
2839
+ boostRewards: {
2840
+ enabled: false,
2841
+ endTimestamp: 1790812800,
2842
+ excludedAddresses: [],
2843
+ funding: { wallet: account },
2844
+ intervalSeconds: 300,
2845
+ perUserPrincipalCapAssets: '25000000000',
2846
+ startTimestamp: 1788206400,
2847
+ targetAnnualRateBps: 700,
2848
+ totalPrincipalCapAssets: '10000000000000',
2849
+ treasury: account,
2850
+ },
2851
+ },
2852
+ earnShareAddress: '0x20c000000000000000000000baac91f6ca72f768',
2853
+ earnShareDecimals: 6,
2854
+ now: 1788206399,
2855
+ vaultAddress,
2856
+ })
2857
+ await RewardCampaigns.setBindings(db, {
2858
+ chainId,
2859
+ distributorAddress: previousDistributorAddress,
2860
+ vaultAddress,
2861
+ })
2862
+ await RewardCampaigns.setBindings(db, { chainId, distributorAddress, vaultAddress })
2863
+ await RewardCampaigns.setPaused(db, { chainId, paused: true, vaultAddress })
2864
+
2865
+ // Pausing new rewards must not erase the cost-basis treatment of already paid rewards.
2866
+ const earnings = await Promise.all((['lifetime', '30d', 'active'] as const).map(read))
2867
+ expect(earnings.map(({ period, status }) => ({ period, status }))).toMatchInlineSnapshot(`
2868
+ [
2869
+ {
2870
+ "period": "lifetime",
2871
+ "status": "complete",
2872
+ },
2873
+ {
2874
+ "period": "30d",
2875
+ "status": "complete",
2876
+ },
2877
+ {
2878
+ "period": "active",
2879
+ "status": "complete",
2880
+ },
2881
+ ]
2882
+ `)
2883
+ const lifetime = earnings[0]!
2884
+ if (lifetime.status !== 'complete' || lifetime.period !== 'lifetime')
2885
+ throw new Error('Expected complete lifetime earnings.')
2886
+ expect(BigInt(lifetime.lifetimeEarnings)).toBe(
2887
+ BigInt(lifetime.currentValue) +
2888
+ BigInt(lifetime.totalWithdrawn) -
2889
+ BigInt(lifetime.totalDeposited),
2890
+ )
2891
+ },
2892
+ 120_000,
2893
+ )
2894
+
1395
2895
  test('returns chain selection errors', async () => {
1396
2896
  const app = TestApp.create({ auth: false, cache })
1397
2897
  const invalid = await app.request(
@@ -1437,6 +2937,8 @@ describe('GET /v1/earn/vaults/:vaultId/earnings/:address', () => {
1437
2937
  lifetimeEarnings: expect.stringMatching(/^-?\d+$/),
1438
2938
  period: 'lifetime',
1439
2939
  status: 'complete',
2940
+ totalDeposited: expect.stringMatching(/^\d+$/),
2941
+ totalWithdrawn: expect.stringMatching(/^\d+$/),
1440
2942
  })
1441
2943
  },
1442
2944
  60_000,
@@ -1684,12 +3186,98 @@ describe('GET /v1/earn/vaults', () => {
1684
3186
  })
1685
3187
 
1686
3188
  describe('OpenAPI', () => {
3189
+ test('publishes stable Earn components', async () => {
3190
+ const app = TestApp.create({ auth: false, cache })
3191
+ const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
3192
+
3193
+ expect(
3194
+ Object.fromEntries(
3195
+ Object.entries(spec.paths)
3196
+ .filter(([path]) => path.startsWith('/v1/earn/'))
3197
+ .map(([path, item]) => [item.get?.operationId, [path, successSchema(item.get)?.$ref]]),
3198
+ ),
3199
+ ).toMatchInlineSnapshot(`
3200
+ {
3201
+ "getEarnAddressPositions": [
3202
+ "/v1/earn/addresses/{address}/positions",
3203
+ "#/components/schemas/EarnAddressPositionList",
3204
+ ],
3205
+ "getEarnVault": [
3206
+ "/v1/earn/vaults/{vaultId}",
3207
+ "#/components/schemas/EarnVaultDetail",
3208
+ ],
3209
+ "getEarnVaultEarnings": [
3210
+ "/v1/earn/vaults/{vaultId}/earnings/{address}",
3211
+ "#/components/schemas/EarnVaultEarnings",
3212
+ ],
3213
+ "getEarnVaultPosition": [
3214
+ "/v1/earn/vaults/{vaultId}/positions/{address}",
3215
+ "#/components/schemas/EarnVaultPosition",
3216
+ ],
3217
+ "getEarnVaultSharePrices": [
3218
+ "/v1/earn/vaults/{vaultId}/share-prices",
3219
+ "#/components/schemas/EarnVaultSharePriceList",
3220
+ ],
3221
+ "getEarnVaults": [
3222
+ "/v1/earn/vaults",
3223
+ "#/components/schemas/EarnVaultList",
3224
+ ],
3225
+ "getVerifiedEarnVaults": [
3226
+ "/v1/earn/vaults/verified",
3227
+ "#/components/schemas/VerifiedEarnVaultList",
3228
+ ],
3229
+ }
3230
+ `)
3231
+ expect(
3232
+ Object.keys(spec.components?.schemas ?? {})
3233
+ .filter((name) => name.startsWith('Earn') && !name.endsWith('Error'))
3234
+ .sort(),
3235
+ ).toMatchInlineSnapshot(`
3236
+ [
3237
+ "EarnAddressPosition",
3238
+ "EarnAddressPositionList",
3239
+ "EarnVault",
3240
+ "EarnVaultDetail",
3241
+ "EarnVaultEarnings",
3242
+ "EarnVaultEngine",
3243
+ "EarnVaultList",
3244
+ "EarnVaultPosition",
3245
+ "EarnVaultSharePriceList",
3246
+ "EarnVaultZone",
3247
+ "EarnVaultZoneRoute",
3248
+ ]
3249
+ `)
3250
+ expect(spec.components?.schemas).toHaveProperty('VerifiedEarnVault')
3251
+ expect(spec.components?.schemas).toHaveProperty('VerifiedEarnVaultList')
3252
+ expect(JSON.stringify(spec.components?.schemas?.['EarnAddressPosition'])).toContain(
3253
+ 'An account’s position in one earn vault.',
3254
+ )
3255
+ expect({
3256
+ detail:
3257
+ spec.paths['/v1/earn/vaults/{vaultId}']?.get?.responses?.['400']?.content?.[
3258
+ 'application/json'
3259
+ ]?.schema?.$ref,
3260
+ list: spec.paths['/v1/earn/vaults']?.get?.responses?.['400']?.content?.['application/json']
3261
+ ?.schema?.$ref,
3262
+ verified:
3263
+ spec.paths['/v1/earn/vaults/verified']?.get?.responses?.['400']?.content?.[
3264
+ 'application/json'
3265
+ ]?.schema?.$ref,
3266
+ }).toMatchInlineSnapshot(`
3267
+ {
3268
+ "detail": "#/components/schemas/ApiKeyMalformedOrChainIdInvalidOrChainIdUnsupportedOrQueryInvalidOrVaultIdInvalidError",
3269
+ "list": "#/components/schemas/ApiKeyMalformedOrChainIdInvalidOrChainIdUnsupportedOrQueryInvalidError",
3270
+ "verified": "#/components/schemas/ApiKeyMalformedOrChainIdInvalidOrChainIdUnsupportedOrQueryInvalidError",
3271
+ }
3272
+ `)
3273
+ })
3274
+
1687
3275
  test('publishes the flattened vault shape', async () => {
1688
3276
  const app = TestApp.create({ auth: false, cache })
1689
3277
  const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
1690
3278
  const operation = spec.paths['/v1/earn/vaults']?.get
1691
3279
  const parameters = operation?.parameters?.map((parameter) => parameter.name)
1692
- const response = JSON.stringify(operation?.responses?.['200'])
3280
+ const response = JSON.stringify(resolveSchema(spec, successSchema(operation)))
1693
3281
 
1694
3282
  expect(Object.keys(spec.paths)).toEqual(
1695
3283
  expect.arrayContaining([
@@ -1699,6 +3287,7 @@ describe('OpenAPI', () => {
1699
3287
  '/v1/earn/vaults/{vaultId}',
1700
3288
  '/v1/earn/vaults/{vaultId}/earnings/{address}',
1701
3289
  '/v1/earn/vaults/{vaultId}/positions/{address}',
3290
+ '/v1/earn/vaults/{vaultId}/share-prices',
1702
3291
  ]),
1703
3292
  )
1704
3293
  expect(parameters).toContain('apy.window')
@@ -1747,6 +3336,11 @@ describe('OpenAPI', () => {
1747
3336
  expect(JSON.stringify(include?.schema)).toContain('"apy"')
1748
3337
  expect(JSON.stringify(include?.schema)).toContain('"tvl"')
1749
3338
  expect(JSON.stringify(include?.schema)).toContain('"zones"')
3339
+ expect(
3340
+ JSON.stringify(
3341
+ resolveSchema(spec, successSchema(spec.paths['/v1/earn/vaults/{vaultId}']?.get)),
3342
+ ),
3343
+ ).toContain('Instant liquidity valued in USD')
1750
3344
  })
1751
3345
 
1752
3346
  test('documents vault-position inputs and responses', async () => {
@@ -1754,8 +3348,9 @@ describe('OpenAPI', () => {
1754
3348
  const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
1755
3349
  const operation = spec.paths['/v1/earn/vaults/{vaultId}/positions/{address}']?.get
1756
3350
  const address = operation?.parameters?.find((parameter) => parameter.name === 'address')
3351
+ const asOf = operation?.parameters?.find((parameter) => parameter.name === 'asOf')
1757
3352
  const errors = errorSchemas(spec, operation)
1758
- const response = JSON.stringify(operation?.responses)
3353
+ const response = JSON.stringify(resolveSchema(spec, successSchema(operation)))
1759
3354
  const vaultId = operation?.parameters?.find((parameter) => parameter.name === 'vaultId')
1760
3355
 
1761
3356
  expect(errors).toContain('"chain_id_invalid"')
@@ -1764,12 +3359,40 @@ describe('OpenAPI', () => {
1764
3359
  expect(errors).toContain('"query_invalid"')
1765
3360
  expect(errors).toContain('"earn_vault_not_found"')
1766
3361
  expect(response).toContain('Stable resource ID for this vault position')
3362
+ expect(response).toContain('Chain containing the account position')
3363
+ expect(response).toContain('Always zero for Zone-routed positions')
1767
3364
  expect(response).toContain('0x20c000000000000000000000b9537d11c60e8b50')
1768
3365
  expect(response).toContain('0x20c000000000000000000000300e14ab91a10769')
1769
3366
  expect(address?.schema?.examples).toStrictEqual([TestEarn.positionAddress])
3367
+ expect(asOf?.description).toContain('latest indexed block at or before')
3368
+ expect(asOf?.schema?.examples).toStrictEqual(['2026-07-21T09:00:00.000Z'])
3369
+ expect(response).toContain('Indexed block used for a historical Earn position observation')
3370
+ expect(response).toContain('Assets held by this account at the observation block')
3371
+ expect(response).toContain('Earn shares held by this account at the observation block')
3372
+ expect(response).toContain('Asset value of this account’s earn shares at the observation block')
3373
+ expect(response).toContain('TIP-20 asset denominating `value`')
3374
+ expect(response).not.toContain('Assets this account currently holds')
3375
+ expect(response).not.toContain('Earn shares this account currently holds')
3376
+ expect(operation?.responses?.['200']?.description).toBe(
3377
+ 'Current or historical earn position for one account in one vault.',
3378
+ )
1770
3379
  expect(vaultId?.schema?.examples).toStrictEqual([TestEarn.mainnetVaultAddress])
1771
3380
  })
1772
3381
 
3382
+ test('documents vault share-price inputs and responses', async () => {
3383
+ const app = TestApp.create({ auth: false, cache })
3384
+ const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
3385
+ const operation = spec.paths['/v1/earn/vaults/{vaultId}/share-prices']?.get
3386
+ const interval = operation?.parameters?.find((parameter) => parameter.name === 'interval')
3387
+ const response = JSON.stringify(resolveSchema(spec, successSchema(operation)))
3388
+
3389
+ expect(interval?.schema?.const).toBe('day')
3390
+ expect(interval?.schema?.examples).toStrictEqual(['day'])
3391
+ expect(response).toContain('Assets returned for one whole Earn share')
3392
+ expect(response).toContain('Daily Earn vault share prices')
3393
+ expect(response).toContain('"formatted":"1"')
3394
+ })
3395
+
1773
3396
  test('documents address-position inputs and responses', async () => {
1774
3397
  const app = TestApp.create({ auth: false, cache })
1775
3398
  const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
@@ -1778,14 +3401,16 @@ describe('OpenAPI', () => {
1778
3401
  const denomination = operation?.parameters?.find(
1779
3402
  (parameter) => parameter.name === 'valuation.currency',
1780
3403
  )
3404
+ const include = operation?.parameters?.find((parameter) => parameter.name === 'include')
1781
3405
  const verified = operation?.parameters?.find((parameter) => parameter.name === 'verified')
1782
3406
  const errors = errorSchemas(spec, operation)
1783
- const response = JSON.stringify(operation?.responses)
3407
+ const response = JSON.stringify(resolveSchema(spec, successSchema(operation)))
1784
3408
  const tokenReference = JSON.stringify(spec.components?.schemas?.['TokenReference'])
1785
3409
 
1786
3410
  expect(operation?.description).toContain('currently holds shares')
1787
3411
  expect(address?.schema?.examples).toStrictEqual([TestEarn.positionAddress])
1788
3412
  expect(denomination?.description).toContain('nominal value')
3413
+ expect(include?.description).toContain('`earnings`')
1789
3414
  expect(verified?.description).toContain('registry-curated')
1790
3415
  expect(verified?.schema?.examples).toStrictEqual([true])
1791
3416
  expect(errors).toContain('"chain_id_invalid"')
@@ -1794,12 +3419,17 @@ describe('OpenAPI', () => {
1794
3419
  expect(errors).toContain('"query_invalid"')
1795
3420
  expect(response).toContain('Stable resource ID for this position')
1796
3421
  expect(response).toContain('Current asset value of the held shares')
3422
+ expect(response).toContain('Chain containing the held shares')
1797
3423
  expect(response).toContain('"assetAmount"')
1798
3424
  expect(response).toContain('"shareAmount"')
3425
+ expect(response).toContain('"lifetimeCashFlows"')
1799
3426
  expect(response).toContain('"valuation"')
3427
+ expect(response).toContain('"valueToken"')
1800
3428
  expect(response).toContain('"vaultAddress"')
1801
3429
  expect(response).toContain('"nextCursor"')
1802
- expect(response).toContain('#/components/schemas/TokenReference')
3430
+ expect(JSON.stringify(spec.components?.schemas?.['EarnAddressPosition'])).toContain(
3431
+ '#/components/schemas/TokenReference',
3432
+ )
1803
3433
  // Token references retain display metadata through the shared component.
1804
3434
  expect(tokenReference).toContain('"symbol"')
1805
3435
  })
@@ -1810,7 +3440,7 @@ describe('OpenAPI', () => {
1810
3440
  const operation = spec.paths['/v1/earn/vaults/{vaultId}/earnings/{address}']?.get
1811
3441
  const errors = errorSchemas(spec, operation)
1812
3442
  const period = operation?.parameters?.find((parameter) => parameter.name === 'period')
1813
- const response = JSON.stringify(operation?.responses)
3443
+ const response = JSON.stringify(resolveSchema(spec, successSchema(operation)))
1814
3444
 
1815
3445
  expect(operation?.description).toContain('the trailing 30 days')
1816
3446
  expect(period?.description).toContain('trailing 30 days')
@@ -1819,6 +3449,8 @@ describe('OpenAPI', () => {
1819
3449
  expect(errors).toContain('"earn_vault_not_found"')
1820
3450
  expect(response).toContain('Stable resource ID for these vault earnings')
1821
3451
  expect(response).toContain('Current share value plus completed redemptions')
3452
+ expect(response).toContain('Assets deposited with this account as receiver')
3453
+ expect(response).toContain('Assets returned by completed redemptions')
1822
3454
  expect(response).toContain('weighted-average asset cost basis')
1823
3455
  expect(response).toContain('Ending share value plus realized assets')
1824
3456
  expect(response).toContain('cannot reconstruct the asset cost basis for the requested period')