tapimo 0.7.1 → 0.8.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 (786) hide show
  1. package/README.md +10 -7
  2. package/dist/ApiKey.d.ts +3 -0
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +10 -0
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +29 -8
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +24 -5
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +20680 -15487
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +250 -129
  13. package/dist/App.js.map +1 -1
  14. package/dist/Assets.d.ts +10 -0
  15. package/dist/Assets.d.ts.map +1 -1
  16. package/dist/Assets.js +17 -4
  17. package/dist/Assets.js.map +1 -1
  18. package/dist/Scope.d.ts +19 -8
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +25 -9
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +1 -1
  23. package/dist/admin/App.d.ts +667 -76
  24. package/dist/admin/App.d.ts.map +1 -1
  25. package/dist/admin/App.js +12 -2
  26. package/dist/admin/App.js.map +1 -1
  27. package/dist/admin/apps/api-keys.d.ts +107 -11
  28. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  29. package/dist/admin/apps/api-keys.js +145 -32
  30. package/dist/admin/apps/api-keys.js.map +1 -1
  31. package/dist/admin/apps/earn-vaults.d.ts +1069 -0
  32. package/dist/admin/apps/earn-vaults.d.ts.map +1 -0
  33. package/dist/admin/apps/earn-vaults.js +409 -0
  34. package/dist/admin/apps/earn-vaults.js.map +1 -0
  35. package/dist/admin/apps/organizations.d.ts +2 -0
  36. package/dist/admin/apps/organizations.d.ts.map +1 -1
  37. package/dist/admin/apps/verified-tokens.d.ts +74 -74
  38. package/dist/admin/ui.generated.d.ts +1 -1
  39. package/dist/admin/ui.generated.d.ts.map +1 -1
  40. package/dist/admin/ui.generated.js +99 -43
  41. package/dist/admin/ui.generated.js.map +1 -1
  42. package/dist/analytics/Analytics.d.ts +4 -0
  43. package/dist/analytics/Analytics.d.ts.map +1 -1
  44. package/dist/analytics/Analytics.js +21 -2
  45. package/dist/analytics/Analytics.js.map +1 -1
  46. package/dist/analytics/tables/requestEvents.d.ts +22 -1
  47. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  48. package/dist/analytics/tables/requestEvents.js +59 -39
  49. package/dist/analytics/tables/requestEvents.js.map +1 -1
  50. package/dist/apps/Zones.d.ts +8335 -0
  51. package/dist/apps/Zones.d.ts.map +1 -0
  52. package/dist/apps/Zones.js +38 -0
  53. package/dist/apps/Zones.js.map +1 -0
  54. package/dist/apps/data/App.d.ts +5666 -3934
  55. package/dist/apps/data/App.d.ts.map +1 -1
  56. package/dist/apps/data/App.js +90 -22
  57. package/dist/apps/data/App.js.map +1 -1
  58. package/dist/apps/data/FxOracle.d.ts +1 -1
  59. package/dist/apps/data/FxOracle.d.ts.map +1 -1
  60. package/dist/apps/data/index.d.ts +0 -1
  61. package/dist/apps/data/index.d.ts.map +1 -1
  62. package/dist/apps/data/index.js +0 -1
  63. package/dist/apps/data/index.js.map +1 -1
  64. package/dist/apps/data/routes/activities.d.ts +56 -56
  65. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  66. package/dist/apps/data/routes/activities.js +130 -65
  67. package/dist/apps/data/routes/activities.js.map +1 -1
  68. package/dist/apps/data/routes/addresses.d.ts +65 -65
  69. package/dist/apps/data/routes/balances.d.ts +14 -14
  70. package/dist/apps/data/routes/balances.js +5 -5
  71. package/dist/apps/data/routes/balances.js.map +1 -1
  72. package/dist/apps/data/routes/blocks.d.ts +39 -39
  73. package/dist/apps/data/routes/coingecko.d.ts +54 -54
  74. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  75. package/dist/apps/data/routes/coingecko.js +62 -35
  76. package/dist/apps/data/routes/coingecko.js.map +1 -1
  77. package/dist/apps/data/routes/earn.d.ts +2574 -0
  78. package/dist/apps/data/routes/earn.d.ts.map +1 -0
  79. package/dist/apps/data/routes/earn.js +1574 -0
  80. package/dist/apps/data/routes/earn.js.map +1 -0
  81. package/dist/apps/data/routes/exchanges.d.ts +1626 -719
  82. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  83. package/dist/apps/data/routes/exchanges.js +656 -193
  84. package/dist/apps/data/routes/exchanges.js.map +1 -1
  85. package/dist/apps/data/routes/fee-amm.d.ts +108 -108
  86. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  87. package/dist/apps/data/routes/fee-amm.js +59 -8
  88. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  89. package/dist/apps/data/routes/indexer.d.ts +35 -35
  90. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  91. package/dist/apps/data/routes/indexer.js +23 -3
  92. package/dist/apps/data/routes/indexer.js.map +1 -1
  93. package/dist/apps/data/routes/receipts.d.ts +33 -33
  94. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  95. package/dist/apps/data/routes/receipts.js +11 -11
  96. package/dist/apps/data/routes/receipts.js.map +1 -1
  97. package/dist/apps/data/routes/rpc.d.ts +6 -6
  98. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  99. package/dist/apps/data/routes/rpc.js +214 -22
  100. package/dist/apps/data/routes/rpc.js.map +1 -1
  101. package/dist/apps/data/routes/tokenlist.d.ts +1 -1
  102. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  103. package/dist/apps/data/routes/tokenlist.js +39 -8
  104. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  105. package/dist/apps/data/routes/tokens.d.ts +115 -87
  106. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  107. package/dist/apps/data/routes/tokens.js +194 -91
  108. package/dist/apps/data/routes/tokens.js.map +1 -1
  109. package/dist/apps/data/routes/transactions.d.ts +54 -54
  110. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  111. package/dist/apps/data/routes/transactions.js +78 -39
  112. package/dist/apps/data/routes/transactions.js.map +1 -1
  113. package/dist/apps/data/routes/transfers.d.ts +76 -27
  114. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  115. package/dist/apps/data/routes/transfers.js +165 -67
  116. package/dist/apps/data/routes/transfers.js.map +1 -1
  117. package/dist/apps/data/routes/valuation.d.ts +42 -42
  118. package/dist/apps/data/routes/valuation.js +2 -2
  119. package/dist/apps/data/routes/valuation.js.map +1 -1
  120. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  121. package/dist/apps/data/routes/webhooks.d.ts +370 -368
  122. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  123. package/dist/apps/data/routes/webhooks.js +161 -59
  124. package/dist/apps/data/routes/webhooks.js.map +1 -1
  125. package/dist/apps/data/routes/zones.d.ts +281 -0
  126. package/dist/apps/data/routes/zones.d.ts.map +1 -0
  127. package/dist/apps/data/routes/zones.js +252 -0
  128. package/dist/apps/data/routes/zones.js.map +1 -0
  129. package/dist/apps/docs/reference.js +6 -5
  130. package/dist/apps/docs/reference.js.map +1 -1
  131. package/dist/apps/funding/Access.d.ts +16 -0
  132. package/dist/apps/funding/Access.d.ts.map +1 -0
  133. package/dist/apps/funding/Access.js +22 -0
  134. package/dist/apps/funding/Access.js.map +1 -0
  135. package/dist/apps/funding/App.d.ts +3183 -0
  136. package/dist/apps/funding/App.d.ts.map +1 -0
  137. package/dist/apps/funding/App.js +46 -0
  138. package/dist/apps/funding/App.js.map +1 -0
  139. package/dist/apps/funding/Inventory.d.ts +5 -0
  140. package/dist/apps/funding/Inventory.d.ts.map +1 -0
  141. package/dist/apps/funding/Inventory.js +25 -0
  142. package/dist/apps/funding/Inventory.js.map +1 -0
  143. package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
  144. package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
  145. package/dist/apps/funding/ProviderAttempts.js +24 -0
  146. package/dist/apps/funding/ProviderAttempts.js.map +1 -0
  147. package/dist/apps/funding/index.d.ts +2 -0
  148. package/dist/apps/funding/index.d.ts.map +1 -0
  149. package/dist/apps/funding/index.js +2 -0
  150. package/dist/apps/funding/index.js.map +1 -0
  151. package/dist/apps/funding/routes/chains.d.ts +169 -0
  152. package/dist/apps/funding/routes/chains.d.ts.map +1 -0
  153. package/dist/apps/funding/routes/chains.js +80 -0
  154. package/dist/apps/funding/routes/chains.js.map +1 -0
  155. package/dist/apps/funding/routes/deposit-addresses.d.ts +2388 -0
  156. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
  157. package/dist/apps/funding/routes/deposit-addresses.js +860 -0
  158. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
  159. package/dist/apps/funding/routes/providers.d.ts +91 -0
  160. package/dist/apps/funding/routes/providers.d.ts.map +1 -0
  161. package/dist/apps/funding/routes/providers.js +56 -0
  162. package/dist/apps/funding/routes/providers.js.map +1 -0
  163. package/dist/apps/{data/routes/funding.d.ts → funding/routes/quotes.d.ts} +26 -396
  164. package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
  165. package/dist/apps/funding/routes/quotes.js +188 -0
  166. package/dist/apps/funding/routes/quotes.js.map +1 -0
  167. package/dist/apps/funding/routes/transfers.d.ts +1999 -0
  168. package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
  169. package/dist/apps/funding/routes/transfers.js +614 -0
  170. package/dist/apps/funding/routes/transfers.js.map +1 -0
  171. package/dist/apps/index.d.ts +3 -1
  172. package/dist/apps/index.d.ts.map +1 -1
  173. package/dist/apps/index.js +3 -1
  174. package/dist/apps/index.js.map +1 -1
  175. package/dist/apps/management/App.d.ts +839 -574
  176. package/dist/apps/management/App.d.ts.map +1 -1
  177. package/dist/apps/management/Billing.d.ts +8 -0
  178. package/dist/apps/management/Billing.d.ts.map +1 -1
  179. package/dist/apps/management/Billing.js +12 -0
  180. package/dist/apps/management/Billing.js.map +1 -1
  181. package/dist/apps/management/Error.d.ts +12 -0
  182. package/dist/apps/management/Error.d.ts.map +1 -0
  183. package/dist/apps/management/Error.js +10 -0
  184. package/dist/apps/management/Error.js.map +1 -0
  185. package/dist/apps/management/routes/api-keys.d.ts +296 -19
  186. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  187. package/dist/apps/management/routes/api-keys.js +77 -9
  188. package/dist/apps/management/routes/api-keys.js.map +1 -1
  189. package/dist/apps/management/routes/billing.d.ts +119 -119
  190. package/dist/apps/management/routes/faucet.d.ts +35 -35
  191. package/dist/apps/management/routes/invitations.d.ts +17 -17
  192. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  193. package/dist/apps/management/routes/invitations.js +4 -10
  194. package/dist/apps/management/routes/invitations.js.map +1 -1
  195. package/dist/apps/management/routes/invite-links.d.ts +38 -38
  196. package/dist/apps/management/routes/me.d.ts +6 -6
  197. package/dist/apps/management/routes/members.d.ts +69 -69
  198. package/dist/apps/management/routes/orgs.d.ts +16 -16
  199. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  200. package/dist/apps/management/routes/orgs.js +2 -8
  201. package/dist/apps/management/routes/orgs.js.map +1 -1
  202. package/dist/apps/management/routes/projects.d.ts +19 -19
  203. package/dist/apps/management/routes/scopes.d.ts +1 -1
  204. package/dist/apps/management/routes/scopes.js +2 -2
  205. package/dist/apps/management/routes/scopes.js.map +1 -1
  206. package/dist/apps/management/routes/usage.d.ts +72 -72
  207. package/dist/apps/management/routes/verified-token-requests.d.ts +18 -18
  208. package/dist/apps/management/routes/webhooks.d.ts +122 -122
  209. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  210. package/dist/apps/management/routes/webhooks.js +15 -3
  211. package/dist/apps/management/routes/webhooks.js.map +1 -1
  212. package/dist/apps/mcp.d.ts +2 -3
  213. package/dist/apps/mcp.d.ts.map +1 -1
  214. package/dist/apps/mcp.js +16 -14
  215. package/dist/apps/mcp.js.map +1 -1
  216. package/dist/apps/metadata.d.ts +4 -0
  217. package/dist/apps/metadata.d.ts.map +1 -1
  218. package/dist/apps/metadata.js +10 -0
  219. package/dist/apps/metadata.js.map +1 -1
  220. package/dist/apps/mpp/App.d.ts +57 -58
  221. package/dist/apps/mpp/App.d.ts.map +1 -1
  222. package/dist/apps/mpp/App.js +192 -27
  223. package/dist/apps/mpp/App.js.map +1 -1
  224. package/dist/apps/mpp/Mpp.d.ts +0 -20
  225. package/dist/apps/mpp/Mpp.d.ts.map +1 -1
  226. package/dist/apps/mpp/Mpp.js +0 -24
  227. package/dist/apps/mpp/Mpp.js.map +1 -1
  228. package/dist/apps/mpp/internal/Adapter.d.ts +40 -0
  229. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -0
  230. package/dist/apps/mpp/internal/Adapter.js +66 -0
  231. package/dist/apps/mpp/internal/Adapter.js.map +1 -0
  232. package/dist/apps/mpp/internal/Error.d.ts +4 -0
  233. package/dist/apps/mpp/internal/Error.d.ts.map +1 -0
  234. package/dist/apps/mpp/internal/Error.js +41 -0
  235. package/dist/apps/mpp/internal/Error.js.map +1 -0
  236. package/dist/apps/mpp/internal/Idempotency.d.ts +33 -0
  237. package/dist/apps/mpp/internal/Idempotency.d.ts.map +1 -0
  238. package/dist/apps/mpp/internal/Idempotency.js +28 -0
  239. package/dist/apps/mpp/internal/Idempotency.js.map +1 -0
  240. package/dist/apps/mpp/internal/Policy.d.ts +46 -0
  241. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -0
  242. package/dist/apps/mpp/internal/Policy.js +57 -0
  243. package/dist/apps/mpp/internal/Policy.js.map +1 -0
  244. package/dist/apps/mpp/internal/ScreenManager.d.ts +27 -0
  245. package/dist/apps/mpp/internal/ScreenManager.d.ts.map +1 -0
  246. package/dist/apps/mpp/internal/ScreenManager.js +18 -0
  247. package/dist/apps/mpp/internal/ScreenManager.js.map +1 -0
  248. package/dist/apps/mpp/internal/Screening.d.ts +35 -0
  249. package/dist/apps/mpp/internal/Screening.d.ts.map +1 -0
  250. package/dist/apps/mpp/internal/Screening.js +20 -0
  251. package/dist/apps/mpp/internal/Screening.js.map +1 -0
  252. package/dist/apps/mpp/internal/Sponsorship.d.ts +36 -0
  253. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -0
  254. package/dist/apps/mpp/internal/Sponsorship.js +103 -0
  255. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -0
  256. package/dist/apps/mpp/internal/Utils.d.ts +7 -0
  257. package/dist/apps/mpp/internal/Utils.d.ts.map +1 -0
  258. package/dist/apps/mpp/internal/Utils.js +24 -0
  259. package/dist/apps/mpp/internal/Utils.js.map +1 -0
  260. package/dist/apps/relay/App.js +183 -23
  261. package/dist/apps/relay/App.js.map +1 -1
  262. package/dist/apps/relay/Sponsorships.d.ts +4 -0
  263. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  264. package/dist/apps/relay/Sponsorships.js +15 -6
  265. package/dist/apps/relay/Sponsorships.js.map +1 -1
  266. package/dist/cloudflare.d.ts +50 -0
  267. package/dist/cloudflare.d.ts.map +1 -1
  268. package/dist/cloudflare.js +178 -0
  269. package/dist/cloudflare.js.map +1 -1
  270. package/dist/db/Db.d.ts +22 -0
  271. package/dist/db/Db.d.ts.map +1 -1
  272. package/dist/db/Db.js +379 -0
  273. package/dist/db/Db.js.map +1 -1
  274. package/dist/db/Schema.d.ts +933 -1
  275. package/dist/db/Schema.d.ts.map +1 -1
  276. package/dist/db/Schema.js +289 -1
  277. package/dist/db/Schema.js.map +1 -1
  278. package/dist/db/tables/earnVaults.d.ts +68 -0
  279. package/dist/db/tables/earnVaults.d.ts.map +1 -0
  280. package/dist/db/tables/earnVaults.js +72 -0
  281. package/dist/db/tables/earnVaults.js.map +1 -0
  282. package/dist/db/tables/fundingCatalog.d.ts +56 -0
  283. package/dist/db/tables/fundingCatalog.d.ts.map +1 -0
  284. package/dist/db/tables/fundingCatalog.js +85 -0
  285. package/dist/db/tables/fundingCatalog.js.map +1 -0
  286. package/dist/db/tables/fundingDepositAddresses.d.ts +172 -0
  287. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -0
  288. package/dist/db/tables/fundingDepositAddresses.js +218 -0
  289. package/dist/db/tables/fundingDepositAddresses.js.map +1 -0
  290. package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
  291. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
  292. package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
  293. package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
  294. package/dist/db/tables/fundingDeposits.d.ts +171 -0
  295. package/dist/db/tables/fundingDeposits.d.ts.map +1 -0
  296. package/dist/db/tables/fundingDeposits.js +182 -0
  297. package/dist/db/tables/fundingDeposits.js.map +1 -0
  298. package/dist/db/tables/fundingIdempotency.d.ts +91 -0
  299. package/dist/db/tables/fundingIdempotency.d.ts.map +1 -0
  300. package/dist/db/tables/fundingIdempotency.js +103 -0
  301. package/dist/db/tables/fundingIdempotency.js.map +1 -0
  302. package/dist/db/tables/fundingTransferEvents.d.ts +12 -0
  303. package/dist/db/tables/fundingTransferEvents.d.ts.map +1 -0
  304. package/dist/db/tables/fundingTransferEvents.js +18 -0
  305. package/dist/db/tables/fundingTransferEvents.js.map +1 -0
  306. package/dist/db/tables/fundingTransferTransactions.d.ts +15 -0
  307. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -0
  308. package/dist/db/tables/fundingTransferTransactions.js +27 -0
  309. package/dist/db/tables/fundingTransferTransactions.js.map +1 -0
  310. package/dist/db/tables/fundingTransfers.d.ts +82 -0
  311. package/dist/db/tables/fundingTransfers.d.ts.map +1 -0
  312. package/dist/db/tables/fundingTransfers.js +89 -0
  313. package/dist/db/tables/fundingTransfers.js.map +1 -0
  314. package/dist/db/tables/organizations.d.ts +8 -0
  315. package/dist/db/tables/organizations.d.ts.map +1 -1
  316. package/dist/db/tables/organizations.js +16 -0
  317. package/dist/db/tables/organizations.js.map +1 -1
  318. package/dist/db/tables/projects.d.ts +8 -0
  319. package/dist/db/tables/projects.d.ts.map +1 -1
  320. package/dist/db/tables/projects.js +16 -0
  321. package/dist/db/tables/projects.js.map +1 -1
  322. package/dist/db/tables/requestUsage.d.ts +13 -0
  323. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  324. package/dist/db/tables/requestUsage.js +16 -0
  325. package/dist/db/tables/requestUsage.js.map +1 -1
  326. package/dist/db/tables/sponsoredTransactions.d.ts +2 -2
  327. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  328. package/dist/db/tables/sponsoredTransactions.js +1 -1
  329. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  330. package/dist/db/tables/users.d.ts +8 -0
  331. package/dist/db/tables/users.d.ts.map +1 -1
  332. package/dist/db/tables/users.js +16 -0
  333. package/dist/db/tables/users.js.map +1 -1
  334. package/dist/db/tables/verifiedTokens.d.ts +14 -7
  335. package/dist/db/tables/verifiedTokens.d.ts.map +1 -1
  336. package/dist/db/tables/verifiedTokens.js +9 -7
  337. package/dist/db/tables/verifiedTokens.js.map +1 -1
  338. package/dist/db/tables/webhookSubscriptions.d.ts +31 -1
  339. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  340. package/dist/db/tables/webhookSubscriptions.js +24 -3
  341. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  342. package/dist/handlers/relay.js +108 -108
  343. package/dist/handlers/relay.js.map +1 -1
  344. package/dist/internal/Auth.d.ts +37 -11
  345. package/dist/internal/Auth.d.ts.map +1 -1
  346. package/dist/internal/Auth.js +75 -16
  347. package/dist/internal/Auth.js.map +1 -1
  348. package/dist/internal/Cache.d.ts.map +1 -1
  349. package/dist/internal/Cache.js +17 -11
  350. package/dist/internal/Cache.js.map +1 -1
  351. package/dist/internal/Cursor.js +1 -1
  352. package/dist/internal/Cursor.js.map +1 -1
  353. package/dist/internal/EarnRates.d.ts +89 -0
  354. package/dist/internal/EarnRates.d.ts.map +1 -0
  355. package/dist/internal/EarnRates.js +266 -0
  356. package/dist/internal/EarnRates.js.map +1 -0
  357. package/dist/internal/EarnVaults.d.ts +171 -0
  358. package/dist/internal/EarnVaults.d.ts.map +1 -0
  359. package/dist/internal/EarnVaults.js +547 -0
  360. package/dist/internal/EarnVaults.js.map +1 -0
  361. package/dist/internal/EdgeCache.d.ts +10 -3
  362. package/dist/internal/EdgeCache.d.ts.map +1 -1
  363. package/dist/internal/EdgeCache.js +43 -11
  364. package/dist/internal/EdgeCache.js.map +1 -1
  365. package/dist/internal/Id.d.ts +11 -0
  366. package/dist/internal/Id.d.ts.map +1 -1
  367. package/dist/internal/Id.js +13 -0
  368. package/dist/internal/Id.js.map +1 -1
  369. package/dist/internal/Idempotency.d.ts +83 -0
  370. package/dist/internal/Idempotency.d.ts.map +1 -0
  371. package/dist/internal/Idempotency.js +113 -0
  372. package/dist/internal/Idempotency.js.map +1 -0
  373. package/dist/internal/IpAllowlist.d.ts +15 -0
  374. package/dist/internal/IpAllowlist.d.ts.map +1 -0
  375. package/dist/internal/IpAllowlist.js +27 -0
  376. package/dist/internal/IpAllowlist.js.map +1 -0
  377. package/dist/internal/Log.d.ts +64 -9
  378. package/dist/internal/Log.d.ts.map +1 -1
  379. package/dist/internal/Log.js +29 -7
  380. package/dist/internal/Log.js.map +1 -1
  381. package/dist/internal/MetricSink.d.ts.map +1 -1
  382. package/dist/internal/MetricSink.js +47 -1
  383. package/dist/internal/MetricSink.js.map +1 -1
  384. package/dist/internal/OpenApi.d.ts +2 -2
  385. package/dist/internal/OpenApi.d.ts.map +1 -1
  386. package/dist/internal/OpenApi.js +4 -1
  387. package/dist/internal/OpenApi.js.map +1 -1
  388. package/dist/internal/Provider.d.ts +14 -0
  389. package/dist/internal/Provider.d.ts.map +1 -0
  390. package/dist/internal/Provider.js +5 -0
  391. package/dist/internal/Provider.js.map +1 -0
  392. package/dist/internal/Schema.d.ts +6 -1
  393. package/dist/internal/Schema.d.ts.map +1 -1
  394. package/dist/internal/Schema.js +20 -11
  395. package/dist/internal/Schema.js.map +1 -1
  396. package/dist/internal/Store.d.ts +6 -0
  397. package/dist/internal/Store.d.ts.map +1 -1
  398. package/dist/internal/Store.js +35 -0
  399. package/dist/internal/Store.js.map +1 -1
  400. package/dist/internal/Tidx.d.ts +49 -24
  401. package/dist/internal/Tidx.d.ts.map +1 -1
  402. package/dist/internal/Tidx.js +90 -12
  403. package/dist/internal/Tidx.js.map +1 -1
  404. package/dist/internal/Viem.d.ts +90 -65
  405. package/dist/internal/Viem.d.ts.map +1 -1
  406. package/dist/internal/Viem.js +90 -12
  407. package/dist/internal/Viem.js.map +1 -1
  408. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  409. package/dist/internal/WebhookDestination.js +34 -28
  410. package/dist/internal/WebhookDestination.js.map +1 -1
  411. package/dist/internal/Webhooks.d.ts +9 -0
  412. package/dist/internal/Webhooks.d.ts.map +1 -1
  413. package/dist/internal/Webhooks.js +15 -10
  414. package/dist/internal/Webhooks.js.map +1 -1
  415. package/dist/internal/exchange/Provider.d.ts +219 -0
  416. package/dist/internal/exchange/Provider.d.ts.map +1 -0
  417. package/dist/internal/exchange/Provider.js +117 -0
  418. package/dist/internal/exchange/Provider.js.map +1 -0
  419. package/dist/internal/exchange/index.d.ts +2 -0
  420. package/dist/internal/exchange/index.d.ts.map +1 -0
  421. package/dist/internal/exchange/index.js +2 -0
  422. package/dist/internal/exchange/index.js.map +1 -0
  423. package/dist/internal/exchange/providers/uniswap.d.ts +57 -0
  424. package/dist/internal/exchange/providers/uniswap.d.ts.map +1 -0
  425. package/dist/internal/exchange/providers/uniswap.js +553 -0
  426. package/dist/internal/exchange/providers/uniswap.js.map +1 -0
  427. package/dist/internal/funding/Action.d.ts +55 -0
  428. package/dist/internal/funding/Action.d.ts.map +1 -0
  429. package/dist/internal/funding/Action.js +74 -0
  430. package/dist/internal/funding/Action.js.map +1 -0
  431. package/dist/internal/funding/Catalog.d.ts +107 -0
  432. package/dist/internal/funding/Catalog.d.ts.map +1 -0
  433. package/dist/internal/funding/Catalog.js +270 -0
  434. package/dist/internal/funding/Catalog.js.map +1 -0
  435. package/dist/internal/funding/Chain.d.ts +76 -0
  436. package/dist/internal/funding/Chain.d.ts.map +1 -0
  437. package/dist/internal/funding/Chain.js +38 -0
  438. package/dist/internal/funding/Chain.js.map +1 -0
  439. package/dist/internal/funding/Deposit.d.ts +408 -0
  440. package/dist/internal/funding/Deposit.d.ts.map +1 -0
  441. package/dist/internal/funding/Deposit.js +318 -0
  442. package/dist/internal/funding/Deposit.js.map +1 -0
  443. package/dist/internal/funding/DepositAddress.d.ts +345 -0
  444. package/dist/internal/funding/DepositAddress.d.ts.map +1 -0
  445. package/dist/internal/funding/DepositAddress.js +189 -0
  446. package/dist/internal/funding/DepositAddress.js.map +1 -0
  447. package/dist/internal/funding/Evidence.d.ts +48 -0
  448. package/dist/internal/funding/Evidence.d.ts.map +1 -0
  449. package/dist/internal/funding/Evidence.js +315 -0
  450. package/dist/internal/funding/Evidence.js.map +1 -0
  451. package/dist/internal/funding/Provider.d.ts +1288 -0
  452. package/dist/internal/funding/Provider.d.ts.map +1 -0
  453. package/dist/internal/funding/Provider.js +1086 -0
  454. package/dist/internal/funding/Provider.js.map +1 -0
  455. package/dist/internal/funding/Reconciliation.d.ts +127 -0
  456. package/dist/internal/funding/Reconciliation.d.ts.map +1 -0
  457. package/dist/internal/funding/Reconciliation.js +885 -0
  458. package/dist/internal/funding/Reconciliation.js.map +1 -0
  459. package/dist/internal/funding/Route.d.ts +21 -0
  460. package/dist/internal/funding/Route.d.ts.map +1 -0
  461. package/dist/internal/funding/Route.js +5 -0
  462. package/dist/internal/funding/Route.js.map +1 -0
  463. package/dist/internal/funding/SourceObservation.d.ts +108 -0
  464. package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
  465. package/dist/internal/funding/SourceObservation.js +143 -0
  466. package/dist/internal/funding/SourceObservation.js.map +1 -0
  467. package/dist/internal/funding/Subsidy.d.ts +113 -0
  468. package/dist/internal/funding/Subsidy.d.ts.map +1 -0
  469. package/dist/internal/funding/Subsidy.js +138 -0
  470. package/dist/internal/funding/Subsidy.js.map +1 -0
  471. package/dist/internal/funding/Token.d.ts +41 -0
  472. package/dist/internal/funding/Token.d.ts.map +1 -0
  473. package/dist/internal/funding/Token.js +22 -0
  474. package/dist/internal/funding/Token.js.map +1 -0
  475. package/dist/internal/funding/Transfer.d.ts +643 -0
  476. package/dist/internal/funding/Transfer.d.ts.map +1 -0
  477. package/dist/internal/funding/Transfer.js +332 -0
  478. package/dist/internal/funding/Transfer.js.map +1 -0
  479. package/dist/internal/funding/index.d.ts +14 -0
  480. package/dist/internal/funding/index.d.ts.map +1 -0
  481. package/dist/internal/funding/index.js +14 -0
  482. package/dist/internal/funding/index.js.map +1 -0
  483. package/dist/internal/funding/providers/across.d.ts +21 -0
  484. package/dist/internal/funding/providers/across.d.ts.map +1 -0
  485. package/dist/internal/funding/providers/across.js +117 -0
  486. package/dist/internal/funding/providers/across.js.map +1 -0
  487. package/dist/internal/funding/providers/relay.d.ts +88 -0
  488. package/dist/internal/funding/providers/relay.d.ts.map +1 -0
  489. package/dist/internal/funding/providers/relay.js +871 -0
  490. package/dist/internal/funding/providers/relay.js.map +1 -0
  491. package/dist/internal/funding/providers/rhino.d.ts +15 -0
  492. package/dist/internal/funding/providers/rhino.d.ts.map +1 -0
  493. package/dist/internal/funding/providers/rhino.js +116 -0
  494. package/dist/internal/funding/providers/rhino.js.map +1 -0
  495. package/dist/internal/funding/providers/squid.d.ts +19 -0
  496. package/dist/internal/funding/providers/squid.d.ts.map +1 -0
  497. package/dist/internal/funding/providers/squid.js +120 -0
  498. package/dist/internal/funding/providers/squid.js.map +1 -0
  499. package/dist/internal/funding/providers/symbiosis.d.ts +20 -0
  500. package/dist/internal/funding/providers/symbiosis.d.ts.map +1 -0
  501. package/dist/internal/funding/providers/symbiosis.js +176 -0
  502. package/dist/internal/funding/providers/symbiosis.js.map +1 -0
  503. package/dist/internal/index.d.ts +5 -1
  504. package/dist/internal/index.d.ts.map +1 -1
  505. package/dist/internal/index.js +5 -1
  506. package/dist/internal/index.js.map +1 -1
  507. package/dist/internal/webhookDestinations/url.js +61 -57
  508. package/dist/internal/webhookDestinations/url.js.map +1 -1
  509. package/dist/providers/index.d.ts +43 -0
  510. package/dist/providers/index.d.ts.map +1 -0
  511. package/dist/providers/index.js +19 -0
  512. package/dist/providers/index.js.map +1 -0
  513. package/package.json +10 -5
  514. package/src/ApiKey.test.ts +35 -0
  515. package/src/ApiKey.ts +27 -0
  516. package/src/ApiKeys.test.ts +40 -7
  517. package/src/ApiKeys.ts +46 -10
  518. package/src/App.test.ts +108 -15
  519. package/src/App.ts +296 -138
  520. package/src/Assets.ts +29 -4
  521. package/src/Client.test-d.ts +809 -37
  522. package/src/Scope.test.ts +19 -0
  523. package/src/Scope.ts +28 -11
  524. package/src/Webhooks.test.ts +25 -0
  525. package/src/Webhooks.ts +1 -1
  526. package/src/admin/App.test.ts +9 -2
  527. package/src/admin/App.ts +32 -9
  528. package/src/admin/apps/api-keys.test.ts +190 -22
  529. package/src/admin/apps/api-keys.ts +170 -28
  530. package/src/admin/apps/earn-vaults.test.ts +338 -0
  531. package/src/admin/apps/earn-vaults.ts +572 -0
  532. package/src/admin/apps/scopes.test.ts +16 -1
  533. package/src/admin/ui/plugin.ts +2 -2
  534. package/src/admin/ui/src/components/EarnVaultForm.tsx +137 -0
  535. package/src/admin/ui/src/components/MobileProfile.tsx +2 -2
  536. package/src/admin/ui/src/components/Page.tsx +1 -1
  537. package/src/admin/ui/src/components/ThemeToggle.tsx +1 -1
  538. package/src/admin/ui/src/lib/api-keys.ts +37 -7
  539. package/src/admin/ui/src/lib/client.ts +1 -1
  540. package/src/admin/ui/src/lib/config.ts +1 -1
  541. package/src/admin/ui/src/lib/early-access.ts +8 -9
  542. package/src/admin/ui/src/lib/earn-vaults.ts +207 -0
  543. package/src/admin/ui/src/lib/format.ts +13 -0
  544. package/src/admin/ui/src/lib/organizations.test.ts +14 -4
  545. package/src/admin/ui/src/lib/organizations.ts +7 -1
  546. package/src/admin/ui/src/lib/response.ts +22 -0
  547. package/src/admin/ui/src/lib/scopes.test.ts +19 -11
  548. package/src/admin/ui/src/lib/scopes.ts +28 -7
  549. package/src/admin/ui/src/lib/verified-tokens.ts +10 -9
  550. package/src/admin/ui/src/routeTree.gen.ts +70 -7
  551. package/src/admin/ui/src/router.tsx +2 -2
  552. package/src/admin/ui/src/routes/__root.tsx +9 -8
  553. package/src/admin/ui/src/routes/api-keys.tsx +475 -64
  554. package/src/admin/ui/src/routes/early-access.tsx +5 -12
  555. package/src/admin/ui/src/routes/earn.vaults.tsx +348 -0
  556. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +154 -0
  557. package/src/admin/ui/src/routes/earn.vaults_.new.tsx +190 -0
  558. package/src/admin/ui/src/routes/organizations.tsx +4 -7
  559. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +56 -54
  560. package/src/admin/ui/src/routes/verified-tokens.tsx +4 -4
  561. package/src/admin/ui/vite.config.ts +4 -1
  562. package/src/admin/ui.generated.ts +113 -43
  563. package/src/analytics/Analytics.test.ts +69 -10
  564. package/src/analytics/Analytics.ts +24 -1
  565. package/src/analytics/tables/requestEvents.ts +79 -37
  566. package/src/apps/Zones.test.ts +33 -0
  567. package/src/apps/Zones.ts +40 -0
  568. package/src/apps/composition.test.ts +184 -3
  569. package/src/apps/data/App.test.ts +28 -23
  570. package/src/apps/data/App.ts +95 -36
  571. package/src/apps/data/FxOracle.ts +1 -1
  572. package/src/apps/data/index.ts +0 -1
  573. package/src/apps/data/routes/activities.test.ts +82 -6
  574. package/src/apps/data/routes/activities.ts +131 -71
  575. package/src/apps/data/routes/balances.test.ts +50 -37
  576. package/src/apps/data/routes/balances.ts +5 -5
  577. package/src/apps/data/routes/blocks.test.ts +2 -0
  578. package/src/apps/data/routes/coingecko.test.ts +22 -0
  579. package/src/apps/data/routes/coingecko.ts +84 -48
  580. package/src/apps/data/routes/earn.test.ts +1391 -0
  581. package/src/apps/data/routes/earn.ts +2188 -0
  582. package/src/apps/data/routes/exchanges.test.ts +82 -33
  583. package/src/apps/data/routes/exchanges.ts +842 -227
  584. package/src/apps/data/routes/fee-amm.test.ts +4 -6
  585. package/src/apps/data/routes/fee-amm.ts +69 -12
  586. package/src/apps/data/routes/indexer.test.ts +43 -34
  587. package/src/apps/data/routes/indexer.ts +21 -3
  588. package/src/apps/data/routes/receipts.test.ts +5 -3
  589. package/src/apps/data/routes/receipts.ts +13 -11
  590. package/src/apps/data/routes/rpc.test.ts +446 -28
  591. package/src/apps/data/routes/rpc.ts +245 -25
  592. package/src/apps/data/routes/tokenlist.ts +72 -19
  593. package/src/apps/data/routes/tokens.test.ts +6 -8
  594. package/src/apps/data/routes/tokens.ts +258 -86
  595. package/src/apps/data/routes/transactions.test.ts +2 -0
  596. package/src/apps/data/routes/transactions.ts +77 -42
  597. package/src/apps/data/routes/transfers.test.ts +166 -4
  598. package/src/apps/data/routes/transfers.ts +216 -78
  599. package/src/apps/data/routes/valuation.test.ts +62 -9
  600. package/src/apps/data/routes/valuation.ts +2 -2
  601. package/src/apps/data/routes/webhooks.test.ts +386 -43
  602. package/src/apps/data/routes/webhooks.ts +172 -71
  603. package/src/apps/data/routes/zones.test.ts +473 -0
  604. package/src/apps/data/routes/zones.ts +338 -0
  605. package/src/apps/docs/reference.ts +8 -6
  606. package/src/apps/funding/Access.ts +26 -0
  607. package/src/apps/funding/App.test.ts +78 -0
  608. package/src/apps/funding/App.ts +63 -0
  609. package/src/apps/funding/Inventory.ts +30 -0
  610. package/src/apps/funding/ProviderAttempts.ts +26 -0
  611. package/src/apps/funding/index.ts +1 -0
  612. package/src/apps/funding/routes/chains.test.ts +78 -0
  613. package/src/apps/funding/routes/chains.ts +101 -0
  614. package/src/apps/funding/routes/deposit-addresses.test.ts +1628 -0
  615. package/src/apps/funding/routes/deposit-addresses.ts +1015 -0
  616. package/src/apps/funding/routes/providers.test.ts +165 -0
  617. package/src/apps/funding/routes/providers.ts +73 -0
  618. package/src/apps/funding/routes/quotes.test.ts +511 -0
  619. package/src/apps/funding/routes/quotes.ts +205 -0
  620. package/src/apps/funding/routes/transfers.test.ts +1255 -0
  621. package/src/apps/funding/routes/transfers.ts +704 -0
  622. package/src/apps/index.ts +3 -1
  623. package/src/apps/management/Billing.test.ts +29 -0
  624. package/src/apps/management/Billing.ts +23 -0
  625. package/src/apps/management/Error.ts +12 -0
  626. package/src/apps/management/routes/api-keys.test.ts +103 -3
  627. package/src/apps/management/routes/api-keys.ts +98 -10
  628. package/src/apps/management/routes/invitations.ts +4 -11
  629. package/src/apps/management/routes/orgs.ts +2 -9
  630. package/src/apps/management/routes/scopes.test.ts +7 -2
  631. package/src/apps/management/routes/scopes.ts +2 -2
  632. package/src/apps/management/routes/webhooks.test.ts +6 -1
  633. package/src/apps/management/routes/webhooks.ts +15 -3
  634. package/src/apps/mcp.test.ts +17 -2
  635. package/src/apps/mcp.ts +18 -13
  636. package/src/apps/metadata.ts +12 -0
  637. package/src/apps/mpp/App.ts +276 -29
  638. package/src/apps/mpp/Mpp.test.ts +631 -26
  639. package/src/apps/mpp/Mpp.ts +0 -42
  640. package/src/apps/mpp/internal/Adapter.ts +108 -3
  641. package/src/apps/mpp/internal/Error.test.ts +33 -0
  642. package/src/apps/mpp/internal/Error.ts +44 -6
  643. package/src/apps/mpp/internal/Idempotency.test.ts +213 -0
  644. package/src/apps/mpp/internal/Idempotency.ts +59 -5
  645. package/src/apps/mpp/internal/Policy.test.ts +128 -0
  646. package/src/apps/mpp/internal/Policy.ts +98 -0
  647. package/src/apps/mpp/internal/ScreenManager.test.ts +68 -0
  648. package/src/apps/mpp/internal/ScreenManager.ts +41 -0
  649. package/src/apps/mpp/internal/Screening.ts +40 -3
  650. package/src/apps/mpp/internal/Sponsorship.ts +144 -0
  651. package/src/apps/mpp/internal/Utils.ts +30 -0
  652. package/src/apps/relay/App.test.ts +493 -62
  653. package/src/apps/relay/App.ts +233 -23
  654. package/src/apps/relay/Sponsorships.test.ts +49 -0
  655. package/src/apps/relay/Sponsorships.ts +18 -6
  656. package/src/cloudflare.ts +279 -0
  657. package/src/db/Db.ts +501 -0
  658. package/src/db/Schema.ts +349 -2
  659. package/src/db/tables/earnVaults.test.ts +185 -0
  660. package/src/db/tables/earnVaults.ts +151 -0
  661. package/src/db/tables/fundingCatalog.test.ts +108 -0
  662. package/src/db/tables/fundingCatalog.ts +157 -0
  663. package/src/db/tables/fundingDepositAddresses.test.ts +580 -0
  664. package/src/db/tables/fundingDepositAddresses.ts +404 -0
  665. package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
  666. package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
  667. package/src/db/tables/fundingDeposits.test.ts +399 -0
  668. package/src/db/tables/fundingDeposits.ts +373 -0
  669. package/src/db/tables/fundingIdempotency.test.ts +182 -0
  670. package/src/db/tables/fundingIdempotency.ts +180 -0
  671. package/src/db/tables/fundingTransferEvents.ts +28 -0
  672. package/src/db/tables/fundingTransferTransactions.test.ts +69 -0
  673. package/src/db/tables/fundingTransferTransactions.ts +37 -0
  674. package/src/db/tables/fundingTransfers.test.ts +89 -0
  675. package/src/db/tables/fundingTransfers.ts +153 -0
  676. package/src/db/tables/organizations.ts +16 -0
  677. package/src/db/tables/projects.ts +16 -0
  678. package/src/db/tables/requestUsage.ts +23 -0
  679. package/src/db/tables/sponsoredTransactions.test.ts +11 -0
  680. package/src/db/tables/sponsoredTransactions.ts +3 -3
  681. package/src/db/tables/users.ts +16 -0
  682. package/src/db/tables/verifiedTokens.ts +19 -14
  683. package/src/db/tables/webhookSubscriptions.ts +58 -3
  684. package/src/handlers/relay.test.ts +2 -0
  685. package/src/handlers/relay.ts +95 -99
  686. package/src/internal/Auth.test.ts +225 -5
  687. package/src/internal/Auth.ts +109 -20
  688. package/src/internal/Cache.test.ts +37 -3
  689. package/src/internal/Cache.ts +14 -10
  690. package/src/internal/Cursor.test.ts +2 -2
  691. package/src/internal/Cursor.ts +1 -1
  692. package/src/internal/EarnRates.test.ts +125 -0
  693. package/src/internal/EarnRates.ts +409 -0
  694. package/src/internal/EarnVaults.test.ts +195 -0
  695. package/src/internal/EarnVaults.ts +876 -0
  696. package/src/internal/EdgeCache.test.ts +26 -7
  697. package/src/internal/EdgeCache.ts +48 -9
  698. package/src/internal/Id.ts +14 -0
  699. package/src/internal/Idempotency.test.ts +109 -0
  700. package/src/internal/Idempotency.ts +237 -0
  701. package/src/internal/IpAllowlist.test.ts +52 -0
  702. package/src/internal/IpAllowlist.ts +39 -0
  703. package/src/internal/Log.test-d.ts +41 -8
  704. package/src/internal/Log.test.ts +196 -9
  705. package/src/internal/Log.ts +124 -17
  706. package/src/internal/MetricSink.test.ts +244 -4
  707. package/src/internal/MetricSink.ts +51 -1
  708. package/src/internal/OpenApi.ts +4 -1
  709. package/src/internal/Provider.test.ts +17 -0
  710. package/src/internal/Provider.ts +17 -0
  711. package/src/internal/Schema.ts +27 -17
  712. package/src/internal/Store.test.ts +25 -0
  713. package/src/internal/Store.ts +46 -0
  714. package/src/internal/Tidx.local.test.ts +78 -3
  715. package/src/internal/Tidx.test.ts +240 -18
  716. package/src/internal/Tidx.ts +129 -33
  717. package/src/internal/VerifiedTokens.test.ts +15 -0
  718. package/src/internal/Viem.test.ts +198 -17
  719. package/src/internal/Viem.ts +127 -28
  720. package/src/internal/WebhookDestination.ts +25 -23
  721. package/src/internal/Webhooks.test.ts +59 -0
  722. package/src/internal/Webhooks.ts +34 -10
  723. package/src/internal/exchange/Provider.test.ts +216 -0
  724. package/src/internal/exchange/Provider.ts +326 -0
  725. package/src/internal/exchange/index.test.ts +6 -0
  726. package/src/internal/exchange/index.ts +1 -0
  727. package/src/internal/exchange/providers/uniswap.test.ts +122 -0
  728. package/src/internal/exchange/providers/uniswap.ts +682 -0
  729. package/src/internal/funding/Action.test.ts +138 -0
  730. package/src/internal/funding/Action.ts +117 -0
  731. package/src/internal/funding/Catalog.test.ts +311 -0
  732. package/src/internal/funding/Catalog.ts +347 -0
  733. package/src/internal/funding/Chain.test.ts +79 -0
  734. package/src/internal/funding/Chain.ts +120 -0
  735. package/src/internal/funding/Deposit.test.ts +26 -0
  736. package/src/internal/funding/Deposit.ts +522 -0
  737. package/src/internal/funding/DepositAddress.test.ts +18 -0
  738. package/src/internal/funding/DepositAddress.ts +312 -0
  739. package/src/internal/funding/Evidence.test.ts +297 -0
  740. package/src/internal/funding/Evidence.ts +425 -0
  741. package/src/internal/funding/Provider.test.ts +1328 -0
  742. package/src/internal/funding/Provider.ts +1911 -0
  743. package/src/internal/funding/Reconciliation.local.test.ts +511 -0
  744. package/src/internal/funding/Reconciliation.test.ts +3037 -0
  745. package/src/internal/funding/Reconciliation.ts +1202 -0
  746. package/src/internal/funding/Route.test.ts +66 -0
  747. package/src/internal/funding/Route.ts +29 -0
  748. package/src/internal/funding/SourceObservation.test.ts +301 -0
  749. package/src/internal/funding/SourceObservation.ts +267 -0
  750. package/src/internal/funding/Subsidy.local.test.ts +152 -0
  751. package/src/internal/funding/Subsidy.test.ts +75 -0
  752. package/src/internal/funding/Subsidy.ts +260 -0
  753. package/src/internal/funding/Token.test.ts +47 -0
  754. package/src/internal/funding/Token.ts +59 -0
  755. package/src/internal/funding/Transfer.test.ts +369 -0
  756. package/src/internal/funding/Transfer.ts +522 -0
  757. package/src/internal/funding/index.test.ts +21 -0
  758. package/src/internal/funding/index.ts +13 -0
  759. package/src/internal/funding/providers/across.ts +149 -0
  760. package/src/{apps/data/FundingQuoteProviders.test.ts → internal/funding/providers/index.test.ts} +178 -157
  761. package/src/internal/funding/providers/relay.test.ts +564 -0
  762. package/src/internal/funding/providers/relay.ts +1060 -0
  763. package/src/internal/funding/providers/rhino.ts +139 -0
  764. package/src/internal/funding/providers/squid.ts +151 -0
  765. package/src/internal/funding/providers/symbiosis.ts +221 -0
  766. package/src/internal/index.test.ts +5 -1
  767. package/src/internal/index.ts +5 -1
  768. package/src/internal/webhookDestinations/url.ts +49 -45
  769. package/src/providers/index.test.ts +14 -0
  770. package/src/providers/index.ts +54 -0
  771. package/dist/apps/data/FundingQuoteProviders.d.ts +0 -174
  772. package/dist/apps/data/FundingQuoteProviders.d.ts.map +0 -1
  773. package/dist/apps/data/FundingQuoteProviders.js +0 -775
  774. package/dist/apps/data/FundingQuoteProviders.js.map +0 -1
  775. package/dist/apps/data/routes/funding.d.ts.map +0 -1
  776. package/dist/apps/data/routes/funding.js +0 -410
  777. package/dist/apps/data/routes/funding.js.map +0 -1
  778. package/dist/internal/FundingQuotes.d.ts +0 -864
  779. package/dist/internal/FundingQuotes.d.ts.map +0 -1
  780. package/dist/internal/FundingQuotes.js +0 -672
  781. package/dist/internal/FundingQuotes.js.map +0 -1
  782. package/src/apps/data/FundingQuoteProviders.ts +0 -1043
  783. package/src/apps/data/routes/funding.test.ts +0 -503
  784. package/src/apps/data/routes/funding.ts +0 -604
  785. package/src/internal/FundingQuotes.test.ts +0 -456
  786. package/src/internal/FundingQuotes.ts +0 -944
@@ -0,0 +1,2188 @@
1
+ import { type Context, Hono } from 'hono'
2
+ import { Value as core_Value } from 'ox'
3
+ import {
4
+ type Chain,
5
+ ContractFunctionExecutionError,
6
+ ContractFunctionRevertedError,
7
+ ContractFunctionZeroDataError,
8
+ zeroAddress,
9
+ } from 'viem'
10
+ import * as z from 'zod/mini'
11
+
12
+ import type * as App from '../../../App.js'
13
+ import * as Db from '../../../db/Db.js'
14
+ import * as core_EarnVaults from '../../../db/tables/earnVaults.js'
15
+ import * as Auth from '../../../internal/Auth.js'
16
+ import * as Cache from '../../../internal/Cache.js'
17
+ import * as Cursor from '../../../internal/Cursor.js'
18
+ import * as EarnRates from '../../../internal/EarnRates.js'
19
+ import * as EarnVaults from '../../../internal/EarnVaults.js'
20
+ import * as OpenApi from '../../../internal/OpenApi.js'
21
+ import * as Response from '../../../internal/Response.js'
22
+ import * as Schema from '../../../internal/Schema.js'
23
+ import * as Store from '../../../internal/Store.js'
24
+ import * as Tidx from '../../../internal/Tidx.js'
25
+ import * as Timing from '../../../internal/Timing.js'
26
+ import * as Ttl from '../../../internal/Ttl.js'
27
+ import * as Value from '../../../internal/Value.js'
28
+ import * as VerifiedTokens from '../../../internal/VerifiedTokens.js'
29
+ import * as FxOracle from '../FxOracle.js'
30
+ import * as Tokens from './tokens.js'
31
+ import * as Valuation from './valuation.js'
32
+
33
+ const deployedSignature =
34
+ 'event EarnStackDeployed(address indexed earnVault,address indexed earnShare,address indexed earnFees,address engine,address asset,address owner,bytes32 deploymentId,address emergencyGuardian,address asyncJanitor,uint8 migrationMode,bytes32 earnShareSalt,bytes32 controlConfigHash,bytes32 feeConfigHash,bytes32 earnFeesSalt)'
35
+
36
+ const depositedSignature =
37
+ 'event Deposited(address indexed caller,address indexed receiver,uint256 assets,uint256 earnShares)'
38
+
39
+ const redeemedSignature =
40
+ 'event Redeemed(address indexed caller,address indexed receiver,uint256 earnShares,uint256 assets)'
41
+
42
+ const withdrewExactSignature =
43
+ 'event WithdrewExact(address indexed caller,address indexed receiver,uint256 assets,uint256 earnSharesBurned)'
44
+
45
+ const redeemFinalizedSignature =
46
+ 'event RedeemFinalized(bytes32 indexed requestId,address indexed receiver,uint256 earnShares,address asset,uint256 assets)'
47
+
48
+ const redeemCancelledSignature =
49
+ 'event RedeemCancelled(bytes32 indexed requestId,address indexed receiver,uint256 earnShares)'
50
+
51
+ const redeemRequestedSignature =
52
+ 'event RedeemRequested(bytes32 indexed requestId,address indexed requester,address indexed receiver,uint256 earnShares)'
53
+
54
+ const venueSharesDepositedSignature =
55
+ 'event VenueSharesDeposited(address indexed caller,address indexed receiver,uint256 requestedVenueShares,uint256 receivedVenueShares,uint256 earnShares)'
56
+
57
+ const maxScannedDeployments = 250
58
+ const scanLimit = 50
59
+
60
+ /** Vault TVL is reported in USD only; the API exposes no denomination parameter. */
61
+ const tvlDenomination = 'USD'
62
+
63
+ /** `Valuation.valuationFor` floors to 6 dp, so 6 is the precision a valued amount carries. */
64
+ const tvlDecimals = 6
65
+
66
+ /** Returns the smallest useful live-discovery batch. */
67
+ export function discoveryBatchLimit(options: discoveryBatchLimit.Options): number {
68
+ const needed = options.limit + 1 - options.matches
69
+ return Math.min(options.filtered ? needed * 2 : needed, options.remaining, scanLimit)
70
+ }
71
+
72
+ export declare namespace discoveryBatchLimit {
73
+ /** Inputs controlling one discovery batch. */
74
+ type Options = {
75
+ /** Whether filters can reject resolved candidates. */
76
+ filtered: boolean
77
+ /** Requested response page size. */
78
+ limit: number
79
+ /** Compatible matches already collected. */
80
+ matches: number
81
+ /** Candidates remaining within the scan bound. */
82
+ remaining: number
83
+ }
84
+ }
85
+
86
+ /** Zod schemas owned by the public Earn-vault resource. */
87
+ export namespace schema {
88
+ const capability = z
89
+ .enum([
90
+ 'asyncRedeem',
91
+ 'boundedRedeem',
92
+ 'deposit',
93
+ 'exactWithdraw',
94
+ 'inKindDeposit',
95
+ 'privateRouting',
96
+ 'redeem',
97
+ 'routerSwaps',
98
+ ])
99
+ .check(z.describe('Vault capability required by the request.'))
100
+
101
+ const engineType = z
102
+ .enum(['erc4626', 'veda'])
103
+ .check(z.describe('Inferred vault engine type.'), z.meta({ examples: ['erc4626'] }))
104
+
105
+ const engine = Schema.describe(
106
+ z.strictObject({
107
+ address: Schema.Address.check(
108
+ z.describe('Current vault engine contract address.'),
109
+ z.meta({ examples: ['0xd85fc943333a6cd7c0e3391acd54fc4e572f0baf'] }),
110
+ ),
111
+ type: z
112
+ .nullable(engineType)
113
+ .check(z.describe('Inferred engine type, or null when it is unknown.')),
114
+ venue: z
115
+ .nullable(
116
+ Schema.Address.check(
117
+ z.describe('Yield venue contract bound to the engine.'),
118
+ z.meta({ examples: ['0xa8b4f0e69cd4b0e56b676343f02acd8c3b355322'] }),
119
+ ),
120
+ )
121
+ .check(z.describe('Yield venue, or null when the engine does not expose one.')),
122
+ }),
123
+ 'Current vault engine.',
124
+ )
125
+
126
+ const include = z
127
+ .enum(['access', 'apy', 'capabilities', 'token.logoUri', 'tvl', 'zone'])
128
+ .check(z.describe('Optional vault fields to embed via `include`.'))
129
+
130
+ const includeQuery = Schema.includeQuery(
131
+ include,
132
+ 'Comma-separated vault fields to include, such as `apy,tvl`.',
133
+ )
134
+
135
+ // `apy` and `tvl` need indexer and FX access, so surfaces lacking either
136
+ // select from this reduced set instead.
137
+ const baseInclude = z
138
+ .enum(['access', 'capabilities', 'token.logoUri', 'zone'])
139
+ .check(z.describe('Optional vault fields to embed via `include`.'))
140
+
141
+ const baseIncludeQuery = Schema.includeQuery(
142
+ baseInclude,
143
+ 'Comma-separated vault fields to include, such as `access,zone`.',
144
+ )
145
+
146
+ const zone = z
147
+ .strictObject({
148
+ chainId: Schema.ChainId.check(
149
+ z.describe('Private Zone chain id.'),
150
+ z.meta({ examples: [421700001] }),
151
+ ),
152
+ inputTokens: z
153
+ .array(Schema.Address)
154
+ .check(
155
+ z.describe('Tokens accepted by private deposits.'),
156
+ z.meta({ examples: [['0x20c000000000000000000000ff04042ee92fd449']] }),
157
+ ),
158
+ name: z.string().check(z.describe('Private Zone name.'), z.meta({ examples: ['zone-e'] })),
159
+ outputTokens: z
160
+ .array(Schema.Address)
161
+ .check(
162
+ z.describe('Tokens supported by private redemptions.'),
163
+ z.meta({ examples: [['0x20c000000000000000000000ff04042ee92fd449']] }),
164
+ ),
165
+ })
166
+ .check(z.describe('Private Zone route configured for a verified vault.'))
167
+
168
+ const value = z.strictObject({
169
+ amount: Schema.DecimalString.check(
170
+ z.describe('Value in base units at `decimals` precision.'),
171
+ z.meta({ examples: ['125288750000'] }),
172
+ ),
173
+ currency: z
174
+ .string()
175
+ .check(z.describe('Display currency of this value.'), z.meta({ examples: ['USD'] })),
176
+ decimals: z
177
+ .number()
178
+ .check(
179
+ z.int(),
180
+ z.nonnegative(),
181
+ z.describe('Decimal places used to convert `amount` into `formatted`.'),
182
+ z.meta({ examples: [6] }),
183
+ ),
184
+ formatted: z
185
+ .string()
186
+ .check(
187
+ z.regex(/^\d+(\.\d+)?$/),
188
+ z.describe('Value rendered in whole units.'),
189
+ z.meta({ examples: ['125288.75'] }),
190
+ ),
191
+ })
192
+
193
+ const vault = z.strictObject({
194
+ access: z
195
+ .optional(EarnVaults.schema.Discovery.shape.access)
196
+ .check(z.describe('Vault share-token access, when requested.')),
197
+ apy: z
198
+ .optional(z.nullable(EarnRates.schema.Apy))
199
+ .check(
200
+ z.describe(
201
+ 'Annualized share-price growth over the selected window, when requested via `include=apy` or by selecting `apy.window`. Null when the rate could not be measured: the vault predates the window, a boundary or historical quote is unavailable, the engine changed inside the window, or the growth is too extreme to annualize as a ratio.',
202
+ ),
203
+ ),
204
+ assetToken: Tokens.schema.Token.check(z.describe('Asset accepted by the vault.')),
205
+ capabilities: z
206
+ .optional(EarnVaults.schema.Discovery.shape.capabilities)
207
+ .check(z.describe('Vault capabilities, when requested.')),
208
+ description: z
209
+ .nullable(z.string())
210
+ .check(
211
+ z.describe('Curated vault description, or null when the vault is not verified.'),
212
+ z.meta({ examples: ['Bridge test PATHUSD deposited into Tempo Earn.'] }),
213
+ ),
214
+ engine,
215
+ id: Schema.Address.check(
216
+ z.describe('Stable vault id, equal to the lowercase vault address.'),
217
+ z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] }),
218
+ ),
219
+ instantLiquidity: EarnVaults.schema.Discovery.shape.instantLiquidity.check(
220
+ z.describe(
221
+ 'Assets the venue would release right now, capped at the vault backing, in asset base units. Null when the engine interface cannot report a reliable number.',
222
+ ),
223
+ ),
224
+ label: z
225
+ .string()
226
+ .check(z.describe('Vault display label.'), z.meta({ examples: ['btPATHUSD Earn'] })),
227
+ sharePrice: z
228
+ .nullable(value)
229
+ .check(
230
+ z.describe(
231
+ 'Assets returned for one whole Earn share, in the asset display currency, or null when the engine cannot quote an exit.',
232
+ ),
233
+ ),
234
+ shareToken: Tokens.schema.Token.check(z.describe('Token representing vault shares.')),
235
+ slug: z
236
+ .nullable(z.string())
237
+ .check(
238
+ z.describe('Curated URL slug, or null when the vault is not verified.'),
239
+ z.meta({ examples: ['btpathusd-earn-h8k2m4n6'] }),
240
+ ),
241
+ state: EarnVaults.schema.Discovery.shape.state,
242
+ tvl: z
243
+ .optional(z.nullable(value))
244
+ .check(
245
+ z.describe(
246
+ 'Total assets valued in USD, when requested via `include=tvl`, or null when the asset has no priced display currency.',
247
+ ),
248
+ ),
249
+ vaultAddress: Schema.Address.check(
250
+ z.describe('Earn vault contract address.'),
251
+ z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] }),
252
+ ),
253
+ verified: z
254
+ .boolean()
255
+ .check(
256
+ z.describe('Whether this vault is in the curated Earn registry.'),
257
+ z.meta({ examples: [true] }),
258
+ ),
259
+ zone: z
260
+ .optional(z.nullable(zone))
261
+ .check(z.describe('Curated private Zone route, when requested, or null when absent.')),
262
+ })
263
+
264
+ /** One compatible Earn vault resolved from current chain state. */
265
+ export const Vault = Schema.describe(vault, 'A compatible Earn vault resolved from chain state.')
266
+
267
+ /** One registry-curated Earn vault. */
268
+ export const VerifiedVault = Schema.describe(
269
+ z.extend(vault, {
270
+ engine: z.extend(engine, { type: engineType }),
271
+ slug: z
272
+ .string()
273
+ .check(
274
+ z.describe('Stable curated vault slug.'),
275
+ z.meta({ examples: ['btpathusd-earn-h8k2m4n6'] }),
276
+ ),
277
+ verified: z
278
+ .literal(true)
279
+ .check(
280
+ z.describe('This vault is in the curated Earn registry.'),
281
+ z.meta({ examples: [true] }),
282
+ ),
283
+ }),
284
+ 'A registry-curated Earn vault resolved from chain state.',
285
+ )
286
+
287
+ const position = z.strictObject({
288
+ account: Schema.Address.check(
289
+ z.describe('Account whose Earn position was read.'),
290
+ z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] }),
291
+ ),
292
+ assetAllowance: Schema.DecimalString.check(
293
+ z.describe('Assets the vault may spend from this account, in asset base units.'),
294
+ z.meta({ examples: ['0'] }),
295
+ ),
296
+ assetBalance: Schema.DecimalString.check(
297
+ z.describe('Assets this account currently holds, in asset base units.'),
298
+ z.meta({ examples: ['250000000'] }),
299
+ ),
300
+ assetToken: Schema.tokenAddress('0x20c000000000000000000000b9537d11c60e8b50').check(
301
+ z.describe('TIP-20 asset token accepted by the vault.'),
302
+ ),
303
+ id: Schema.Address.check(
304
+ z.describe('Stable resource ID for this vault position, equal to the account address.'),
305
+ z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] }),
306
+ ),
307
+ shareAllowance: Schema.DecimalString.check(
308
+ z.describe('Earn shares the vault may spend from this account, in share base units.'),
309
+ z.meta({ examples: ['0'] }),
310
+ ),
311
+ shareBalance: Schema.DecimalString.check(
312
+ z.describe('Earn shares this account currently holds, in share base units.'),
313
+ z.meta({ examples: ['99500000'] }),
314
+ ),
315
+ shareToken: Schema.tokenAddress('0x20c000000000000000000000300e14ab91a10769').check(
316
+ z.describe('TIP-20 token representing Earn shares.'),
317
+ ),
318
+ value: Schema.DecimalString.check(
319
+ z.describe(
320
+ 'Current asset value of this account’s Earn shares, including fees, in asset base units.',
321
+ ),
322
+ z.meta({ examples: ['100000000'] }),
323
+ ),
324
+ })
325
+
326
+ const earningsBase = z.strictObject({
327
+ account: Schema.Address.check(
328
+ z.describe('Account whose vault earnings were calculated.'),
329
+ z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] }),
330
+ ),
331
+ assetToken: Schema.TokenAddress.check(
332
+ z.describe('TIP-20 asset token accepted by the vault.'),
333
+ z.meta({ examples: ['0x20c000000000000000000000ff04042ee92fd449'] }),
334
+ ),
335
+ currentValue: Schema.DecimalString.check(
336
+ z.describe(
337
+ 'Asset value of the account’s active Earn shares at the latest indexed block, in asset base units.',
338
+ ),
339
+ z.meta({ examples: ['600000'] }),
340
+ ),
341
+ id: Schema.Address.check(
342
+ z.describe('Stable resource ID for these vault earnings, equal to the account address.'),
343
+ z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] }),
344
+ ),
345
+ })
346
+
347
+ const earningsPeriod = z
348
+ .enum(['active', 'lifetime'])
349
+ .check(z.describe('Earnings period to calculate.'), z.meta({ examples: ['lifetime'] }))
350
+
351
+ const earnings = z.union([
352
+ z.extend(earningsBase, {
353
+ lifetimeEarnings: z
354
+ .string()
355
+ .check(
356
+ z.regex(/^-?\d+$/),
357
+ z.describe(
358
+ 'Current share value plus completed redemptions, exact withdrawals, and finalized async assets, minus asset deposits, at the latest indexed block.',
359
+ ),
360
+ z.meta({ examples: ['0'] }),
361
+ ),
362
+ period: z
363
+ .literal('lifetime')
364
+ .check(
365
+ z.describe('Returns lifetime net asset earnings.'),
366
+ z.meta({ examples: ['lifetime'] }),
367
+ ),
368
+ status: z
369
+ .literal('complete')
370
+ .check(
371
+ z.describe('Earnings include a complete asset-denominated cost basis.'),
372
+ z.meta({ examples: ['complete'] }),
373
+ ),
374
+ }),
375
+ z.extend(earningsBase, {
376
+ activeEarnings: z
377
+ .string()
378
+ .check(
379
+ z.regex(/^-?\d+$/),
380
+ z.describe(
381
+ 'Current share value minus the weighted-average asset cost basis of shares still held, at the latest indexed block.',
382
+ ),
383
+ z.meta({ examples: ['0'] }),
384
+ ),
385
+ period: z
386
+ .literal('active')
387
+ .check(
388
+ z.describe('Returns earnings on shares still held.'),
389
+ z.meta({ examples: ['active'] }),
390
+ ),
391
+ status: z
392
+ .literal('complete')
393
+ .check(
394
+ z.describe('Earnings include a complete asset-denominated cost basis.'),
395
+ z.meta({ examples: ['complete'] }),
396
+ ),
397
+ }),
398
+ z.extend(earningsBase, {
399
+ period: earningsPeriod,
400
+ status: z
401
+ .literal('incomplete_cost_basis')
402
+ .check(
403
+ z.describe(
404
+ 'Earnings are unavailable when indexed activity cannot reconstruct the remaining asset cost basis.',
405
+ ),
406
+ z.meta({ examples: ['incomplete_cost_basis'] }),
407
+ ),
408
+ }),
409
+ z.extend(earningsBase, {
410
+ period: z
411
+ .literal('lifetime')
412
+ .check(
413
+ z.describe('Returns lifetime net asset earnings.'),
414
+ z.meta({ examples: ['lifetime'] }),
415
+ ),
416
+ status: z
417
+ .literal('pending_redemption')
418
+ .check(
419
+ z.describe('Earnings are unavailable while an async redemption remains open.'),
420
+ z.meta({ examples: ['pending_redemption'] }),
421
+ ),
422
+ }),
423
+ ])
424
+
425
+ const meta = z
426
+ .object({
427
+ valuation: z
428
+ .optional(Valuation.schema.Pricing)
429
+ .check(
430
+ z.describe(
431
+ 'Rate provenance for valued fields. Present when `include=tvl` consulted conversion rates; absent when TVL was not requested, every value was identity-valued, or rates were unavailable.',
432
+ ),
433
+ ),
434
+ })
435
+ .check(z.describe('Rate provenance for valued fields.'))
436
+
437
+ const collectionFilters = z.strictObject({
438
+ asset: z
439
+ .optional(Schema.Address)
440
+ .check(z.describe('Only include vaults accepting this asset address.')),
441
+ capability: Schema.includeQuery(
442
+ capability,
443
+ 'Comma-separated capabilities that every returned vault must support.',
444
+ ),
445
+ chainId: Schema.ChainIdQuery,
446
+ cursor: Schema.Cursor,
447
+ 'engine.type': z
448
+ .optional(engineType)
449
+ .check(z.describe('Only include vaults with this inferred engine type.')),
450
+ include: includeQuery,
451
+ limit: Schema.Limit,
452
+ })
453
+
454
+ const collectionQuery = z
455
+ .extend(collectionFilters, { 'apy.window': EarnRates.schema.Window })
456
+ .check(z.describe('Query parameters for listing Earn vaults.'))
457
+
458
+ const vaultSelector = z.strictObject({
459
+ chainId: Schema.ChainIdQuery,
460
+ include: includeQuery,
461
+ })
462
+
463
+ /** Schemas for the getEarnVaults operation. */
464
+ export namespace getEarnVaults {
465
+ /** Query parameters for all compatible deployed Earn vaults. */
466
+ export const Query = collectionQuery
467
+
468
+ /** Paginated list of compatible deployed Earn vaults. */
469
+ export const Response = z
470
+ .strictObject({
471
+ data: z
472
+ .array(Vault)
473
+ .check(z.describe('Compatible deployed Earn vaults in deployment order.')),
474
+ meta: z.optional(meta),
475
+ nextCursor: Schema.NextCursor,
476
+ })
477
+ .check(z.describe('A page of compatible deployed Earn vaults.'))
478
+ }
479
+
480
+ /** Schemas for the getVerifiedEarnVaults operation. */
481
+ export namespace getVerifiedEarnVaults {
482
+ /** Collection query without rates or valuation, for surfaces that cannot measure them. */
483
+ export const BaseQuery = Schema.describe(
484
+ z.extend(collectionFilters, { include: baseIncludeQuery }),
485
+ 'Query parameters for listing Earn vaults.',
486
+ )
487
+
488
+ /** Query parameters for registry-curated Earn vaults. */
489
+ export const Query = collectionQuery
490
+
491
+ /** Paginated list of registry-curated Earn vaults. */
492
+ export const Response = z
493
+ .strictObject({
494
+ data: z
495
+ .array(VerifiedVault)
496
+ .check(z.describe('Registry-curated Earn vaults ordered by vault address.')),
497
+ meta: z.optional(meta),
498
+ nextCursor: Schema.NextCursor,
499
+ })
500
+ .check(z.describe('A page of registry-curated Earn vaults.'))
501
+ }
502
+
503
+ /** Schemas for the getEarnVault operation. */
504
+ export namespace getEarnVault {
505
+ /** Path parameters identifying one Earn vault. */
506
+ export const Params = z
507
+ .strictObject({
508
+ vaultId: Schema.Address.check(z.describe('Earn vault contract address.')),
509
+ })
510
+ .check(z.describe('Path parameters identifying one Earn vault.'))
511
+
512
+ /** Detail query without rates or valuation, for surfaces that cannot measure them. */
513
+ export const BaseQuery = Schema.describe(
514
+ z.extend(vaultSelector, { include: baseIncludeQuery }),
515
+ 'Query parameters selecting one Earn vault.',
516
+ )
517
+
518
+ /** Query parameters selecting the chain containing the vault. */
519
+ export const Query = z
520
+ .extend(vaultSelector, { 'apy.window': EarnRates.schema.Window })
521
+ .check(z.describe('Query parameters selecting one Earn vault.'))
522
+
523
+ /** One compatible Earn vault resolved directly from current chain state. */
524
+ export const Response = Schema.describe(
525
+ z.extend(vault, { meta: z.optional(meta) }),
526
+ 'A compatible Earn vault resolved from chain state, with rate provenance for its valued fields.',
527
+ )
528
+ }
529
+
530
+ /** Schemas for the getEarnVaultPosition operation. */
531
+ export namespace getEarnVaultPosition {
532
+ /** Path parameters identifying the Earn vault and account. */
533
+ export const Params = z
534
+ .strictObject({
535
+ address: Schema.Address.check(z.describe('Account whose Earn position to read.')),
536
+ vaultId: Schema.address('0xdb48b00fb28d9a8db881fcd671c3fb1bf8c486b6').check(
537
+ z.describe('Earn vault contract address.'),
538
+ ),
539
+ })
540
+ .check(z.describe('Path parameters identifying an Earn vault position.'))
541
+
542
+ /** Query parameters selecting the chain containing the vault. */
543
+ export const Query = z
544
+ .strictObject({ chainId: Schema.ChainIdQuery })
545
+ .check(z.describe('Query parameters selecting an Earn vault position.'))
546
+
547
+ /** Current asset and Earn-share state for one account in one vault. */
548
+ export const Response = Schema.describe(
549
+ position,
550
+ 'Current asset and Earn-share balances, allowances, and asset value for an account in one Earn vault.',
551
+ )
552
+ }
553
+
554
+ /** Schemas for the getEarnVaultEarnings operation. */
555
+ export namespace getEarnVaultEarnings {
556
+ /** Path parameters identifying the Earn vault and account. */
557
+ export const Params = z
558
+ .strictObject({
559
+ address: Schema.Address.check(z.describe('Account whose vault earnings to read.')),
560
+ vaultId: Schema.address('0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a').check(
561
+ z.describe('Earn vault contract address.'),
562
+ ),
563
+ })
564
+ .check(z.describe('Path parameters identifying an Earn vault account.'))
565
+
566
+ /** Query parameters selecting the chain containing the vault. */
567
+ export const Query = z
568
+ .strictObject({
569
+ chainId: Schema.ChainIdQuery,
570
+ period: z._default(earningsPeriod, 'lifetime'),
571
+ })
572
+ .check(z.describe('Query parameters selecting an Earn vault account.'))
573
+
574
+ /** Earnings for one account and period in one Earn vault. */
575
+ export const Response = Schema.describe(
576
+ earnings,
577
+ 'Indexed asset value and cost-basis-aware earnings for an account in one Earn vault.',
578
+ )
579
+ }
580
+ }
581
+
582
+ /** Creates the public Earn-vault handlers. */
583
+ export function earn(options: earn.Options = {}) {
584
+ const oracle = options.fx?.oracle ?? FxOracle.ecb()
585
+ return new Hono<App.Environment>()
586
+ .get(
587
+ '/v1/earn/vaults',
588
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
589
+ OpenApi.validate('query', schema.getEarnVaults.Query, {
590
+ code: 'query_invalid',
591
+ message: 'Invalid query parameters',
592
+ }),
593
+ OpenApi.describeRoute({
594
+ description:
595
+ 'Lists compatible Earn vaults discovered from deployment events and resolved from current chain state.',
596
+ operationId: 'getEarnVaults',
597
+ responses: OpenApi.responses({
598
+ errors: { 502: 'Could not read Earn deployment or vault data.' },
599
+ success: {
600
+ description: 'A page of compatible deployed Earn vaults.',
601
+ schema: schema.getEarnVaults.Response,
602
+ },
603
+ }),
604
+ summary: 'List vaults',
605
+ tags: ['Earn'],
606
+ }),
607
+ // Live state and rate enrichment are embedded, so every vault read caches
608
+ // as current-state rather than slow-changing metadata.
609
+ Cache.response<App.Environment>({
610
+ cacheControl: Cache.policies.state,
611
+ key: (c) => Cache.urlKey(c, schema.getEarnVaults.Query),
612
+ name: 'tempo-api:earn-vaults:v2',
613
+ }),
614
+ async (c) => {
615
+ if (Auth.narrowAccess) return Auth.paidAccessError(c)
616
+ if (OpenApi.narrowValidation)
617
+ return OpenApi.validationError(c, {
618
+ code: 'query_invalid',
619
+ message: 'Invalid query parameters',
620
+ })
621
+
622
+ const query = c.req.valid('query')
623
+ try {
624
+ const chainId = query.chainId ?? c.get('chainId')
625
+ const data = await listVaults(c, { ...query, chainId, oracle })
626
+ return c.json(Response.validated(schema.getEarnVaults.Response, data), 200)
627
+ } catch (cause) {
628
+ return Response.upstream(c, cause)
629
+ }
630
+ },
631
+ )
632
+ .get(
633
+ '/v1/earn/vaults/verified',
634
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
635
+ OpenApi.validate('query', schema.getVerifiedEarnVaults.Query, {
636
+ code: 'query_invalid',
637
+ message: 'Invalid query parameters',
638
+ }),
639
+ OpenApi.describeRoute({
640
+ description:
641
+ 'Lists registry-curated Earn vaults after resolving their current onchain configuration.',
642
+ operationId: 'getVerifiedEarnVaults',
643
+ responses: OpenApi.responses({
644
+ errors: { 502: 'Could not read curated Earn registry or vault data.' },
645
+ success: {
646
+ description: 'A page of registry-curated Earn vaults.',
647
+ schema: schema.getVerifiedEarnVaults.Response,
648
+ },
649
+ }),
650
+ summary: 'List verified vaults',
651
+ tags: ['Earn'],
652
+ }),
653
+ Cache.response<App.Environment>({
654
+ cacheControl: Cache.policies.state,
655
+ key: (c) => Cache.urlKey(c, schema.getVerifiedEarnVaults.Query),
656
+ name: 'tempo-api:earn-vaults:v2',
657
+ }),
658
+ async (c) => {
659
+ if (Auth.narrowAccess) return Auth.paidAccessError(c)
660
+ if (OpenApi.narrowValidation)
661
+ return OpenApi.validationError(c, {
662
+ code: 'query_invalid',
663
+ message: 'Invalid query parameters',
664
+ })
665
+
666
+ const query = c.req.valid('query')
667
+ try {
668
+ const chainId = query.chainId ?? c.get('chainId')
669
+ const data = await listVerifiedVaults(c, { ...query, chainId, oracle })
670
+ return c.json(Response.validated(schema.getVerifiedEarnVaults.Response, data), 200)
671
+ } catch (cause) {
672
+ return Response.upstream(c, cause)
673
+ }
674
+ },
675
+ )
676
+ .get(
677
+ '/v1/earn/vaults/:vaultId/positions/:address',
678
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
679
+ OpenApi.validate('param', schema.getEarnVaultPosition.Params, {
680
+ code: 'position_invalid',
681
+ message: 'Invalid vault position',
682
+ }),
683
+ OpenApi.validate('query', schema.getEarnVaultPosition.Query, {
684
+ code: 'query_invalid',
685
+ message: 'Invalid query parameters',
686
+ }),
687
+ OpenApi.describeRoute({
688
+ description:
689
+ 'Gets an account’s asset and Earn-share balances, allowances, and current asset value for one Earn vault.',
690
+ operationId: 'getEarnVaultPosition',
691
+ responses: OpenApi.responses({
692
+ errors: {
693
+ 400: {
694
+ codes: [
695
+ 'chain_id_invalid',
696
+ 'chain_id_unsupported',
697
+ 'position_invalid',
698
+ 'query_invalid',
699
+ ],
700
+ },
701
+ 404: {
702
+ codes: ['earn_vault_not_found'],
703
+ description: 'No compatible Earn vault was found at the supplied vault address.',
704
+ },
705
+ 502: 'Could not read the Earn vault position.',
706
+ },
707
+ success: {
708
+ description: 'Current Earn position for one account in one vault.',
709
+ schema: schema.getEarnVaultPosition.Response,
710
+ },
711
+ }),
712
+ summary: 'Get account position',
713
+ tags: ['Earn'],
714
+ }),
715
+ Cache.response<App.Environment>({
716
+ cacheControl: Cache.policies.state,
717
+ key: (c) => Cache.urlKey(c, schema.getEarnVaultPosition.Query),
718
+ name: 'tempo-api:earn-vault-positions:v1',
719
+ }),
720
+ async (c) => {
721
+ if (Auth.narrowAccess) return Auth.paidAccessError(c)
722
+ if (OpenApi.narrowValidation)
723
+ return Response.error(c, {
724
+ code: 'chain_id_invalid',
725
+ message: 'Invalid chain id',
726
+ status: 400,
727
+ })
728
+ if (OpenApi.narrowValidation)
729
+ return Response.error(c, {
730
+ code: 'chain_id_unsupported',
731
+ message: 'Unsupported chain id',
732
+ status: 400,
733
+ })
734
+ if (OpenApi.narrowValidation)
735
+ return OpenApi.validationError(c, {
736
+ code: 'position_invalid',
737
+ message: 'Invalid request parameters',
738
+ })
739
+ if (OpenApi.narrowValidation)
740
+ return OpenApi.validationError(c, {
741
+ code: 'query_invalid',
742
+ message: 'Invalid query parameters',
743
+ })
744
+
745
+ const { address, vaultId } = c.req.valid('param')
746
+ const query = c.req.valid('query')
747
+ const chainId = query.chainId ?? c.get('chainId')
748
+ try {
749
+ const position = await c
750
+ .get('getClient')(chainId)
751
+ .earn.getPosition({ account: address, vault: vaultId })
752
+ return c.json(
753
+ Response.validated(schema.getEarnVaultPosition.Response, {
754
+ account: address,
755
+ assetAllowance: position.assetAllowance.toString(),
756
+ assetBalance: position.assetBalance.toString(),
757
+ assetToken: position.assetToken,
758
+ id: address,
759
+ shareAllowance: position.shareAllowance.toString(),
760
+ shareBalance: position.shareBalance.toString(),
761
+ shareToken: position.shareToken,
762
+ value: position.value.toString(),
763
+ }),
764
+ 200,
765
+ )
766
+ } catch (cause) {
767
+ try {
768
+ await resolveVaultDiscovery(c, { chainId, include: [], vaultAddress: vaultId })
769
+ } catch (vaultCause) {
770
+ if (isIncompatible(vaultCause))
771
+ return Response.error(c, {
772
+ code: 'earn_vault_not_found',
773
+ message: 'Earn vault not found',
774
+ status: 404,
775
+ })
776
+ }
777
+ return Response.upstream(c, cause)
778
+ }
779
+ },
780
+ )
781
+ .get(
782
+ '/v1/earn/vaults/:vaultId/earnings/:address',
783
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
784
+ OpenApi.validate('param', schema.getEarnVaultEarnings.Params, {
785
+ code: 'earnings_invalid',
786
+ message: 'Invalid vault earnings',
787
+ }),
788
+ OpenApi.validate('query', schema.getEarnVaultEarnings.Query, {
789
+ code: 'query_invalid',
790
+ message: 'Invalid query parameters',
791
+ }),
792
+ OpenApi.describeRoute({
793
+ description:
794
+ 'Gets an account’s indexed asset value and earnings for lifetime history or shares still held.',
795
+ operationId: 'getEarnVaultEarnings',
796
+ responses: OpenApi.responses({
797
+ errors: {
798
+ 400: {
799
+ codes: [
800
+ 'chain_id_invalid',
801
+ 'chain_id_unsupported',
802
+ 'earnings_invalid',
803
+ 'query_invalid',
804
+ ],
805
+ },
806
+ 404: {
807
+ codes: ['earn_vault_not_found'],
808
+ description: 'No compatible Earn vault was found at this address.',
809
+ },
810
+ 502: 'Could not read the Earn vault earnings.',
811
+ },
812
+ success: {
813
+ description: 'Cost-basis-aware earnings for one account in one vault.',
814
+ schema: schema.getEarnVaultEarnings.Response,
815
+ },
816
+ }),
817
+ summary: 'Get vault earnings',
818
+ tags: ['Earn'],
819
+ }),
820
+ Cache.response<App.Environment>({
821
+ cacheControl: Cache.policies.state,
822
+ key: (c) => Cache.urlKey(c, schema.getEarnVaultEarnings.Query),
823
+ name: 'tempo-api:earn-vault-earnings:v2',
824
+ }),
825
+ async (c) => {
826
+ if (Auth.narrowAccess) return Auth.paidAccessError(c)
827
+ if (OpenApi.narrowValidation)
828
+ return Response.error(c, {
829
+ code: 'chain_id_invalid',
830
+ message: 'Invalid chain id',
831
+ status: 400,
832
+ })
833
+ if (OpenApi.narrowValidation)
834
+ return Response.error(c, {
835
+ code: 'chain_id_unsupported',
836
+ message: 'Unsupported chain id',
837
+ status: 400,
838
+ })
839
+ if (OpenApi.narrowValidation)
840
+ return OpenApi.validationError(c, {
841
+ code: 'earnings_invalid',
842
+ message: 'Invalid request parameters',
843
+ })
844
+ if (OpenApi.narrowValidation)
845
+ return OpenApi.validationError(c, {
846
+ code: 'query_invalid',
847
+ message: 'Invalid query parameters',
848
+ })
849
+
850
+ const { address, vaultId } = c.req.valid('param')
851
+ const query = c.req.valid('query')
852
+ const chainId = query.chainId ?? c.get('chainId')
853
+ try {
854
+ const data = await getVaultEarnings(c, {
855
+ account: address,
856
+ chainId,
857
+ period: query.period,
858
+ vault: vaultId,
859
+ })
860
+ return c.json(Response.validated(schema.getEarnVaultEarnings.Response, data), 200)
861
+ } catch (cause) {
862
+ try {
863
+ await resolveVaultDiscovery(c, { chainId, include: [], vaultAddress: vaultId })
864
+ } catch (vaultCause) {
865
+ if (isIncompatible(vaultCause))
866
+ return Response.error(c, {
867
+ code: 'earn_vault_not_found',
868
+ message: 'Earn vault not found',
869
+ status: 404,
870
+ })
871
+ }
872
+ return Response.upstream(c, cause)
873
+ }
874
+ },
875
+ )
876
+ .get(
877
+ '/v1/earn/vaults/:vaultId',
878
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
879
+ OpenApi.validate('param', schema.getEarnVault.Params, {
880
+ code: 'vault_id_invalid',
881
+ message: 'Invalid vault id',
882
+ }),
883
+ OpenApi.validate('query', schema.getEarnVault.Query, {
884
+ code: 'query_invalid',
885
+ message: 'Invalid query parameters',
886
+ }),
887
+ OpenApi.describeRoute({
888
+ description:
889
+ 'Resolves any compatible Earn vault directly from current chain state and enriches it when curated.',
890
+ operationId: 'getEarnVault',
891
+ responses: OpenApi.responses({
892
+ errors: {
893
+ 404: {
894
+ codes: ['earn_vault_not_found'],
895
+ description: 'No compatible Earn vault was found at this address.',
896
+ },
897
+ 502: 'Could not read Earn vault data.',
898
+ },
899
+ success: {
900
+ description: 'One compatible Earn vault.',
901
+ schema: schema.getEarnVault.Response,
902
+ },
903
+ }),
904
+ summary: 'Get vault',
905
+ tags: ['Earn'],
906
+ }),
907
+ Cache.response<App.Environment>({
908
+ cacheControl: Cache.policies.state,
909
+ key: (c) => Cache.urlKey(c, schema.getEarnVault.Query),
910
+ name: 'tempo-api:earn-vaults:v2',
911
+ }),
912
+ async (c) => {
913
+ if (Auth.narrowAccess) return Auth.paidAccessError(c)
914
+ if (OpenApi.narrowValidation)
915
+ return OpenApi.validationError(c, {
916
+ code: 'vault_id_invalid',
917
+ message: 'Invalid request parameters',
918
+ })
919
+ if (OpenApi.narrowValidation)
920
+ return OpenApi.validationError(c, {
921
+ code: 'query_invalid',
922
+ message: 'Invalid request parameters',
923
+ })
924
+
925
+ const { vaultId } = c.req.valid('param')
926
+ const query = c.req.valid('query')
927
+ try {
928
+ const chainId = query.chainId ?? c.get('chainId')
929
+ const data = await getVault(c, {
930
+ 'apy.window': query['apy.window'],
931
+ chainId,
932
+ include: query.include,
933
+ oracle,
934
+ vaultAddress: vaultId,
935
+ })
936
+ return c.json(Response.validated(schema.getEarnVault.Response, data), 200)
937
+ } catch (cause) {
938
+ if (cause instanceof NotFoundError)
939
+ return Response.error(c, {
940
+ code: 'earn_vault_not_found',
941
+ message: 'Earn vault not found',
942
+ status: 404,
943
+ })
944
+ return Response.upstream(c, cause)
945
+ }
946
+ },
947
+ )
948
+ }
949
+
950
+ export declare namespace earn {
951
+ /** Options for the public Earn-vault handlers. */
952
+ type Options = {
953
+ /** FX configuration backing TVL valuation. */
954
+ fx?: Valuation.addresses.Fx | undefined
955
+ }
956
+ }
957
+
958
+ /** Reads one account's indexed value and returns earnings when the history is complete. */
959
+ export async function getVaultEarnings(
960
+ c: Context<App.Environment>,
961
+ options: getVaultEarnings.Options,
962
+ ) {
963
+ const client = c.get('getClient')(options.chainId)
964
+ const tidx = c.get('getTidx')(options.chainId)
965
+ const blockNumber = await getLatestIndexedBlockNumber(tidx)
966
+ const position = client.earn.getPosition({
967
+ account: options.account,
968
+ blockNumber: BigInt(blockNumber),
969
+ vault: options.vault,
970
+ })
971
+ if (options.period === 'active') {
972
+ const current = await position
973
+ const base = {
974
+ account: options.account,
975
+ assetToken: current.assetToken,
976
+ currentValue: current.value.toString(),
977
+ id: options.account,
978
+ period: options.period,
979
+ }
980
+ const activeCostBasis = await getActiveCostBasis(tidx, {
981
+ account: options.account,
982
+ blockNumber,
983
+ shareBalance: current.shareBalance,
984
+ shareToken: current.shareToken,
985
+ vault: options.vault,
986
+ })
987
+ if (activeCostBasis === undefined) return { ...base, status: 'incomplete_cost_basis' as const }
988
+ return {
989
+ ...base,
990
+ activeEarnings: (current.value - activeCostBasis).toString(),
991
+ status: 'complete' as const,
992
+ }
993
+ }
994
+ const [history, current] = await Promise.all([
995
+ getVaultEarningsHistory(tidx, {
996
+ account: options.account,
997
+ blockNumber,
998
+ vault: options.vault,
999
+ }),
1000
+ position,
1001
+ ])
1002
+ const externalShareTransfer = await hasExternalShareTransfer(tidx, {
1003
+ account: options.account,
1004
+ blockNumber,
1005
+ shareToken: current.shareToken,
1006
+ vault: options.vault,
1007
+ })
1008
+ const base = {
1009
+ account: options.account,
1010
+ assetToken: current.assetToken,
1011
+ currentValue: current.value.toString(),
1012
+ id: options.account,
1013
+ period: options.period,
1014
+ }
1015
+ if (history.incompleteCostBasis || externalShareTransfer)
1016
+ return { ...base, status: 'incomplete_cost_basis' as const }
1017
+ if (history.pendingRedemption) return { ...base, status: 'pending_redemption' as const }
1018
+ const lifetimeEarnings =
1019
+ current.value + history.redeemed + history.withdrewExact + history.finalized - history.deposited
1020
+ return { ...base, lifetimeEarnings: lifetimeEarnings.toString(), status: 'complete' as const }
1021
+ }
1022
+
1023
+ export declare namespace getVaultEarnings {
1024
+ /** Inputs identifying the account and vault to calculate. */
1025
+ type Options = {
1026
+ account: z.output<typeof Schema.Address>
1027
+ chainId: z.output<typeof Schema.ChainId>
1028
+ period: z.output<typeof schema.getEarnVaultEarnings.Query>['period']
1029
+ vault: z.output<typeof Schema.Address>
1030
+ }
1031
+ }
1032
+
1033
+ /** Reads the latest block whose indexed logs back the earnings calculation. */
1034
+ async function getLatestIndexedBlockNumber(tidx: Tidx.Client) {
1035
+ const result = await tidx.fetch({
1036
+ engine: 'clickhouse',
1037
+ query: 'SELECT num FROM blocks ORDER BY num DESC LIMIT 1' as string,
1038
+ })
1039
+ const blockNumber = Value.toNumber(result.rows[0]?.['num'])
1040
+ if (blockNumber === undefined) throw new Error('TIDX returned no indexed block.')
1041
+ return blockNumber
1042
+ }
1043
+
1044
+ /** Reads the indexed cash flows and exceptional states behind one earnings calculation. */
1045
+ async function getVaultEarningsHistory(
1046
+ tidx: Tidx.Client,
1047
+ options: getVaultEarningsHistory.Options,
1048
+ ) {
1049
+ const result = await tidx.fetch({
1050
+ engine: 'clickhouse',
1051
+ query: `
1052
+ SELECT
1053
+ (
1054
+ SELECT toString(sum(toUInt256(assets)))
1055
+ FROM Deposited
1056
+ WHERE address = '${options.vault}'
1057
+ AND receiver = '${options.account}'
1058
+ AND block_num <= ${options.blockNumber}
1059
+ ) AS deposited_assets,
1060
+ (
1061
+ SELECT toString(sum(toUInt256(finalized.assets)))
1062
+ FROM RedeemFinalized AS finalized
1063
+ INNER JOIN RedeemRequested AS requested
1064
+ ON requested.address = finalized.address
1065
+ AND requested."requestId" = finalized."requestId"
1066
+ WHERE finalized.address = '${options.vault}'
1067
+ AND requested.requester = '${options.account}'
1068
+ AND finalized.block_num <= ${options.blockNumber}
1069
+ AND requested.block_num <= ${options.blockNumber}
1070
+ ) AS finalized_assets,
1071
+ (
1072
+ SELECT count()
1073
+ FROM RedeemRequested AS requested
1074
+ LEFT ANTI JOIN RedeemFinalized AS finalized
1075
+ ON finalized.address = requested.address
1076
+ AND finalized."requestId" = requested."requestId"
1077
+ AND finalized.block_num <= ${options.blockNumber}
1078
+ LEFT ANTI JOIN RedeemCancelled AS cancelled
1079
+ ON cancelled.address = requested.address
1080
+ AND cancelled."requestId" = requested."requestId"
1081
+ AND cancelled.block_num <= ${options.blockNumber}
1082
+ WHERE requested.address = '${options.vault}'
1083
+ AND requested.requester = '${options.account}'
1084
+ AND requested.block_num <= ${options.blockNumber}
1085
+ ) AS pending_redemptions,
1086
+ (
1087
+ SELECT toString(sum(toUInt256(assets)))
1088
+ FROM Redeemed
1089
+ WHERE address = '${options.vault}'
1090
+ AND caller = '${options.account}'
1091
+ AND block_num <= ${options.blockNumber}
1092
+ ) AS redeemed_assets,
1093
+ (
1094
+ SELECT count()
1095
+ FROM RedeemCancelled AS cancelled
1096
+ INNER JOIN RedeemRequested AS requested
1097
+ ON requested.address = cancelled.address
1098
+ AND requested."requestId" = cancelled."requestId"
1099
+ WHERE cancelled.address = '${options.vault}'
1100
+ AND cancelled.block_num <= ${options.blockNumber}
1101
+ AND requested.block_num <= ${options.blockNumber}
1102
+ AND requested.requester != cancelled.receiver
1103
+ AND (
1104
+ requested.requester = '${options.account}'
1105
+ OR cancelled.receiver = '${options.account}'
1106
+ )
1107
+ ) AS transferred_cancellations,
1108
+ (
1109
+ SELECT count()
1110
+ FROM VenueSharesDeposited
1111
+ WHERE address = '${options.vault}'
1112
+ AND receiver = '${options.account}'
1113
+ AND block_num <= ${options.blockNumber}
1114
+ ) AS venue_deposits,
1115
+ (
1116
+ SELECT toString(sum(toUInt256(assets)))
1117
+ FROM WithdrewExact
1118
+ WHERE address = '${options.vault}'
1119
+ AND caller = '${options.account}'
1120
+ AND block_num <= ${options.blockNumber}
1121
+ ) AS withdrew_exact
1122
+ ` as string,
1123
+ signatures: [
1124
+ depositedSignature,
1125
+ redeemCancelledSignature,
1126
+ redeemFinalizedSignature,
1127
+ redeemRequestedSignature,
1128
+ redeemedSignature,
1129
+ venueSharesDepositedSignature,
1130
+ withdrewExactSignature,
1131
+ ],
1132
+ })
1133
+ const row = result.rows[0]
1134
+ const deposited = Value.toIntegerString(row?.['deposited_assets'])
1135
+ const finalized = Value.toIntegerString(row?.['finalized_assets'])
1136
+ const pendingRedemptions = Value.toNumber(row?.['pending_redemptions'])
1137
+ const redeemed = Value.toIntegerString(row?.['redeemed_assets'])
1138
+ const transferredCancellations = Value.toNumber(row?.['transferred_cancellations'])
1139
+ const venueDeposits = Value.toNumber(row?.['venue_deposits'])
1140
+ const withdrewExact = Value.toIntegerString(row?.['withdrew_exact'])
1141
+ if (
1142
+ deposited === undefined ||
1143
+ finalized === undefined ||
1144
+ pendingRedemptions === undefined ||
1145
+ redeemed === undefined ||
1146
+ transferredCancellations === undefined ||
1147
+ venueDeposits === undefined ||
1148
+ withdrewExact === undefined
1149
+ )
1150
+ throw new Error('TIDX returned invalid Earn earnings history.')
1151
+ return {
1152
+ deposited: BigInt(deposited),
1153
+ finalized: BigInt(finalized),
1154
+ incompleteCostBasis: transferredCancellations > 0 || venueDeposits > 0,
1155
+ pendingRedemption: pendingRedemptions > 0,
1156
+ redeemed: BigInt(redeemed),
1157
+ withdrewExact: BigInt(withdrewExact),
1158
+ }
1159
+ }
1160
+
1161
+ declare namespace getVaultEarningsHistory {
1162
+ type Options = {
1163
+ account: z.output<typeof Schema.Address>
1164
+ blockNumber: number
1165
+ vault: z.output<typeof Schema.Address>
1166
+ }
1167
+ }
1168
+
1169
+ /** Returns whether an account sent or received Earn shares outside known vault flows. */
1170
+ async function hasExternalShareTransfer(
1171
+ tidx: Tidx.Client,
1172
+ options: hasExternalShareTransfer.Options,
1173
+ ) {
1174
+ // Vault exits pull shares into the vault before burning. Reconcile counts by transaction and amount so unmatched direct sends remain external.
1175
+ const result = await tidx.fetch({
1176
+ engine: 'clickhouse',
1177
+ query: `
1178
+ SELECT
1179
+ (
1180
+ SELECT count()
1181
+ FROM token_transfers
1182
+ WHERE token = '${options.shareToken.toLowerCase()}'
1183
+ AND block_num <= ${options.blockNumber}
1184
+ AND (
1185
+ ("from" = '${options.account}' AND "to" != '${options.vault}')
1186
+ OR ("to" = '${options.account}' AND "from" != '${zeroAddress}')
1187
+ )
1188
+ ) + (
1189
+ SELECT count()
1190
+ FROM (
1191
+ SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
1192
+ FROM token_transfers
1193
+ WHERE token = '${options.shareToken.toLowerCase()}'
1194
+ AND "from" = '${options.account}'
1195
+ AND "to" = '${options.vault}'
1196
+ AND block_num <= ${options.blockNumber}
1197
+ GROUP BY tx_hash, amount
1198
+ ) AS candidate
1199
+ LEFT JOIN (
1200
+ SELECT tx_hash, amount, count() AS exit_count
1201
+ FROM (
1202
+ SELECT tx_hash, toUInt256(earnShares) AS amount
1203
+ FROM Redeemed
1204
+ WHERE address = '${options.vault}'
1205
+ AND caller = '${options.account}'
1206
+ AND block_num <= ${options.blockNumber}
1207
+ UNION ALL
1208
+ SELECT tx_hash, toUInt256(earnShares) AS amount
1209
+ FROM RedeemRequested
1210
+ WHERE address = '${options.vault}'
1211
+ AND requester = '${options.account}'
1212
+ AND block_num <= ${options.blockNumber}
1213
+ UNION ALL
1214
+ SELECT tx_hash, toUInt256(earnSharesBurned) AS amount
1215
+ FROM WithdrewExact
1216
+ WHERE address = '${options.vault}'
1217
+ AND caller = '${options.account}'
1218
+ AND block_num <= ${options.blockNumber}
1219
+ ) AS protocol_exit
1220
+ GROUP BY tx_hash, amount
1221
+ ) AS matched
1222
+ ON matched.tx_hash = candidate.tx_hash AND matched.amount = candidate.amount
1223
+ WHERE candidate.transfer_count > coalesce(matched.exit_count, 0)
1224
+ ) AS count
1225
+ ` as string,
1226
+ signatures: [redeemedSignature, redeemRequestedSignature, withdrewExactSignature],
1227
+ })
1228
+ const count = Value.toNumber(result.rows[0]?.['count'])
1229
+ if (count === undefined) throw new Error('TIDX returned an invalid Earn-share transfer count.')
1230
+ return count > 0
1231
+ }
1232
+
1233
+ declare namespace hasExternalShareTransfer {
1234
+ type Options = {
1235
+ account: z.output<typeof Schema.Address>
1236
+ blockNumber: number
1237
+ shareToken: z.output<typeof Schema.Address>
1238
+ vault: z.output<typeof Schema.Address>
1239
+ }
1240
+ }
1241
+
1242
+ /** Calculates the asset cost basis attached to an account's remaining Earn shares. */
1243
+ export function calculateActiveCostBasis(options: calculateActiveCostBasis.Options) {
1244
+ const requests = new Map<string, { costBasis: bigint | undefined; shares: bigint }>()
1245
+ let costBasis: bigint | undefined = 0n
1246
+ let shares = 0n
1247
+
1248
+ const remove = (burned: bigint) => {
1249
+ if (burned > shares || shares === 0n) return undefined
1250
+ const removed = (() => {
1251
+ if (costBasis === undefined) return undefined
1252
+ return burned === shares ? costBasis : (costBasis * burned) / shares
1253
+ })()
1254
+ if (burned === shares) costBasis = 0n
1255
+ else if (costBasis !== undefined && removed !== undefined) costBasis -= removed
1256
+ shares -= burned
1257
+ return { costBasis: removed }
1258
+ }
1259
+
1260
+ for (const action of options.actions) {
1261
+ if (action.kind === 'deposit') {
1262
+ if (costBasis !== undefined) costBasis += action.assets
1263
+ shares += action.shares
1264
+ continue
1265
+ }
1266
+ if (action.kind === 'unknown') {
1267
+ costBasis = undefined
1268
+ shares += action.shares
1269
+ continue
1270
+ }
1271
+ if (action.kind === 'cancel') {
1272
+ const restored = requests.get(action.requestId)
1273
+ if (restored === undefined || restored.shares !== action.shares) return undefined
1274
+ if (costBasis === undefined || restored.costBasis === undefined) costBasis = undefined
1275
+ else costBasis += restored.costBasis
1276
+ shares += restored.shares
1277
+ requests.delete(action.requestId)
1278
+ continue
1279
+ }
1280
+ const removed = remove(action.shares)
1281
+ if (removed === undefined) return undefined
1282
+ if (action.kind === 'request')
1283
+ requests.set(action.requestId, { costBasis: removed.costBasis, shares: action.shares })
1284
+ }
1285
+
1286
+ return shares === options.shareBalance && costBasis !== undefined ? costBasis : undefined
1287
+ }
1288
+
1289
+ export declare namespace calculateActiveCostBasis {
1290
+ /** One indexed share or cost-basis mutation. */
1291
+ export type Action =
1292
+ | {
1293
+ /** Action discriminator. */
1294
+ kind: 'cancel'
1295
+ /** Async redemption request being cancelled. */
1296
+ requestId: string
1297
+ /** Shares restored to the account. */
1298
+ shares: bigint
1299
+ }
1300
+ | {
1301
+ /** Assets added to the account's cost basis. */
1302
+ assets: bigint
1303
+ /** Action discriminator. */
1304
+ kind: 'deposit'
1305
+ /** Shares issued for the deposit. */
1306
+ shares: bigint
1307
+ }
1308
+ | {
1309
+ /** Action discriminator. */
1310
+ kind: 'redeem'
1311
+ /** Shares removed from the account. */
1312
+ shares: bigint
1313
+ }
1314
+ | {
1315
+ /** Action discriminator. */
1316
+ kind: 'request'
1317
+ /** Async redemption request holding the removed basis. */
1318
+ requestId: string
1319
+ /** Shares removed from the account. */
1320
+ shares: bigint
1321
+ }
1322
+ | {
1323
+ /** Action discriminator. */
1324
+ kind: 'unknown'
1325
+ /** Shares received without an asset-denominated cost basis. */
1326
+ shares: bigint
1327
+ }
1328
+
1329
+ /** Inputs for calculating the remaining cost basis. */
1330
+ export type Options = {
1331
+ /** Indexed mutations in chain order. */
1332
+ actions: readonly Action[]
1333
+ /** Share balance read at the same indexed block. */
1334
+ shareBalance: bigint
1335
+ }
1336
+ }
1337
+
1338
+ /** Reads the active asset cost basis for shares held at one indexed block. */
1339
+ async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis.Options) {
1340
+ const result = await tidx.fetch({
1341
+ engine: 'clickhouse',
1342
+ query: `
1343
+ SELECT kind, request_id, shares, assets
1344
+ FROM (
1345
+ SELECT
1346
+ 'deposit' AS kind,
1347
+ block_num,
1348
+ log_idx,
1349
+ '' AS request_id,
1350
+ toString(toUInt256(earnShares)) AS shares,
1351
+ toString(toUInt256(assets)) AS assets
1352
+ FROM Deposited
1353
+ WHERE address = '${options.vault}'
1354
+ AND receiver = '${options.account}'
1355
+ AND block_num <= ${options.blockNumber}
1356
+ UNION ALL
1357
+ SELECT
1358
+ 'redeem' AS kind,
1359
+ block_num,
1360
+ log_idx,
1361
+ '' AS request_id,
1362
+ toString(toUInt256(earnShares)) AS shares,
1363
+ '0' AS assets
1364
+ FROM Redeemed
1365
+ WHERE address = '${options.vault}'
1366
+ AND caller = '${options.account}'
1367
+ AND block_num <= ${options.blockNumber}
1368
+ UNION ALL
1369
+ SELECT
1370
+ 'redeem' AS kind,
1371
+ block_num,
1372
+ log_idx,
1373
+ '' AS request_id,
1374
+ toString(toUInt256(earnSharesBurned)) AS shares,
1375
+ '0' AS assets
1376
+ FROM WithdrewExact
1377
+ WHERE address = '${options.vault}'
1378
+ AND caller = '${options.account}'
1379
+ AND block_num <= ${options.blockNumber}
1380
+ UNION ALL
1381
+ SELECT
1382
+ 'request' AS kind,
1383
+ block_num,
1384
+ log_idx,
1385
+ "requestId" AS request_id,
1386
+ toString(toUInt256(earnShares)) AS shares,
1387
+ '0' AS assets
1388
+ FROM RedeemRequested
1389
+ WHERE address = '${options.vault}'
1390
+ AND requester = '${options.account}'
1391
+ AND block_num <= ${options.blockNumber}
1392
+ UNION ALL
1393
+ SELECT
1394
+ 'cancel' AS kind,
1395
+ cancelled.block_num AS block_num,
1396
+ cancelled.log_idx AS log_idx,
1397
+ cancelled."requestId" AS request_id,
1398
+ toString(toUInt256(cancelled.earnShares)) AS shares,
1399
+ '0' AS assets
1400
+ FROM RedeemCancelled AS cancelled
1401
+ INNER JOIN RedeemRequested AS requested
1402
+ ON requested.address = cancelled.address
1403
+ AND requested."requestId" = cancelled."requestId"
1404
+ WHERE cancelled.address = '${options.vault}'
1405
+ AND requested.requester = '${options.account}'
1406
+ AND cancelled.receiver = '${options.account}'
1407
+ AND cancelled.block_num <= ${options.blockNumber}
1408
+ AND requested.block_num <= ${options.blockNumber}
1409
+ UNION ALL
1410
+ SELECT
1411
+ 'unknown' AS kind,
1412
+ block_num,
1413
+ log_idx,
1414
+ '' AS request_id,
1415
+ toString(toUInt256(earnShares)) AS shares,
1416
+ '0' AS assets
1417
+ FROM VenueSharesDeposited
1418
+ WHERE address = '${options.vault}'
1419
+ AND receiver = '${options.account}'
1420
+ AND block_num <= ${options.blockNumber}
1421
+ UNION ALL
1422
+ SELECT
1423
+ 'unknown' AS kind,
1424
+ block_num,
1425
+ log_idx,
1426
+ '' AS request_id,
1427
+ toString(toUInt256(amount)) AS shares,
1428
+ '0' AS assets
1429
+ FROM token_transfers
1430
+ WHERE token = '${options.shareToken.toLowerCase()}'
1431
+ AND "to" = '${options.account}'
1432
+ AND "from" != '${zeroAddress}'
1433
+ AND "from" != '${options.vault}'
1434
+ AND block_num <= ${options.blockNumber}
1435
+ UNION ALL
1436
+ SELECT
1437
+ 'redeem' AS kind,
1438
+ block_num,
1439
+ log_idx,
1440
+ '' AS request_id,
1441
+ toString(toUInt256(amount)) AS shares,
1442
+ '0' AS assets
1443
+ FROM token_transfers
1444
+ WHERE token = '${options.shareToken.toLowerCase()}'
1445
+ AND "from" = '${options.account}'
1446
+ AND "to" != '${options.vault}'
1447
+ AND block_num <= ${options.blockNumber}
1448
+ ) AS actions
1449
+ ORDER BY block_num, log_idx
1450
+ ` as string,
1451
+ signatures: [
1452
+ depositedSignature,
1453
+ redeemCancelledSignature,
1454
+ redeemRequestedSignature,
1455
+ redeemedSignature,
1456
+ venueSharesDepositedSignature,
1457
+ withdrewExactSignature,
1458
+ ],
1459
+ })
1460
+ const actions: calculateActiveCostBasis.Action[] = []
1461
+ for (const row of result.rows) {
1462
+ const kind = Value.toText(row['kind'])
1463
+ if (kind === 'deposit') {
1464
+ const assets = Value.toIntegerString(row['assets'])
1465
+ const shares = Value.toIntegerString(row['shares'])
1466
+ if (assets === undefined || shares === undefined) return undefined
1467
+ actions.push({ assets: BigInt(assets), kind, shares: BigInt(shares) })
1468
+ continue
1469
+ }
1470
+ if (kind === 'redeem') {
1471
+ const shares = Value.toIntegerString(row['shares'])
1472
+ if (shares === undefined) return undefined
1473
+ actions.push({ kind, shares: BigInt(shares) })
1474
+ continue
1475
+ }
1476
+ if (kind === 'cancel') {
1477
+ const requestId = Value.toText(row['request_id'])
1478
+ const shares = Value.toIntegerString(row['shares'])
1479
+ if (requestId === undefined || shares === undefined) return undefined
1480
+ actions.push({ kind, requestId, shares: BigInt(shares) })
1481
+ continue
1482
+ }
1483
+ if (kind === 'request') {
1484
+ const requestId = Value.toText(row['request_id'])
1485
+ const shares = Value.toIntegerString(row['shares'])
1486
+ if (requestId === undefined || shares === undefined) return undefined
1487
+ actions.push({ kind, requestId, shares: BigInt(shares) })
1488
+ continue
1489
+ }
1490
+ if (kind === 'unknown') {
1491
+ const shares = Value.toIntegerString(row['shares'])
1492
+ if (shares === undefined) return undefined
1493
+ actions.push({ kind, shares: BigInt(shares) })
1494
+ continue
1495
+ }
1496
+ return undefined
1497
+ }
1498
+ return calculateActiveCostBasis({ actions, shareBalance: options.shareBalance })
1499
+ }
1500
+
1501
+ declare namespace getActiveCostBasis {
1502
+ type Options = {
1503
+ account: z.output<typeof Schema.Address>
1504
+ blockNumber: number
1505
+ shareBalance: bigint
1506
+ shareToken: z.output<typeof Schema.Address>
1507
+ vault: z.output<typeof Schema.Address>
1508
+ }
1509
+ }
1510
+
1511
+ /** Lists compatible deployed Earn vaults. */
1512
+ export async function listVaults(c: Context<App.Environment>, options: listVaults.Options) {
1513
+ const db = Db.get(c.get('dbCached'))
1514
+ const [records, snapshot] = await Promise.all([
1515
+ core_EarnVaults.list(db, { chainId: options.chainId }),
1516
+ VerifiedTokens.snapshot(c, options.chainId),
1517
+ ])
1518
+ const registry = new Map(records.map((record) => [record.vaultAddress.toLowerCase(), record]))
1519
+ const decoded = options.cursor ? Cursor.decode(options.cursor, ['int', 'int']) : undefined
1520
+ let position = decoded
1521
+ ? ([Number(decoded[0]), Number(decoded[1])] as listVaults.Position)
1522
+ : undefined
1523
+ let hasMore = false
1524
+ let scanned = 0
1525
+ const matches: listVaults.Match[] = []
1526
+
1527
+ while (matches.length <= options.limit && scanned < maxScannedDeployments) {
1528
+ const page = await queryDeployments(c, {
1529
+ asset: options.asset,
1530
+ chainId: options.chainId,
1531
+ cursor: position,
1532
+ limit: discoveryBatchLimit({
1533
+ filtered: options.capability.length > 0 || options['engine.type'] !== undefined,
1534
+ limit: options.limit,
1535
+ matches: matches.length,
1536
+ remaining: maxScannedDeployments - scanned,
1537
+ }),
1538
+ })
1539
+ hasMore = page.hasMore
1540
+ if (page.items.length === 0) {
1541
+ hasMore = false
1542
+ break
1543
+ }
1544
+ scanned += page.items.length
1545
+
1546
+ const discovered = await Promise.all(
1547
+ page.items.map(async (item) => {
1548
+ const record = registry.get(item.vaultAddress.toLowerCase())
1549
+ try {
1550
+ const discovery = await resolveVaultDiscovery(c, {
1551
+ chainId: options.chainId,
1552
+ include: discoveryInclude(options),
1553
+ vaultAddress: item.vaultAddress,
1554
+ })
1555
+ return { discovery, position: item.position, record }
1556
+ } catch (cause) {
1557
+ if (!record && isIncompatible(cause)) return undefined
1558
+ throw cause
1559
+ }
1560
+ }),
1561
+ )
1562
+ for (const match of discovered) {
1563
+ if (!match || !matchesVaultFilters(match.discovery, options)) continue
1564
+ matches.push(match)
1565
+ }
1566
+
1567
+ position = page.items.at(-1)!.position
1568
+ if (!hasMore) break
1569
+ }
1570
+
1571
+ const page = matches.slice(0, options.limit)
1572
+ const discoveries = page.map(({ discovery }) => discovery)
1573
+ const include = vaultInclude(options)
1574
+ const [apy, rates, tokens] = await Promise.all([
1575
+ resolvePageApy(c, {
1576
+ chainId: options.chainId,
1577
+ discoveries,
1578
+ include,
1579
+ window: options['apy.window'],
1580
+ }),
1581
+ resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
1582
+ resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
1583
+ ])
1584
+ const data = page.map((match) =>
1585
+ resolveVault(c, { ...match, apy, include, rates, snapshot, tokens }),
1586
+ )
1587
+ const last = matches[options.limit - 1]
1588
+ const nextPosition =
1589
+ matches.length > options.limit ? last?.position : hasMore ? position : undefined
1590
+ return {
1591
+ data,
1592
+ ...(rates ? { meta: { valuation: Valuation.pricing(rates, options.oracle) } } : {}),
1593
+ nextCursor: nextPosition ? Cursor.encode(nextPosition) : null,
1594
+ }
1595
+ }
1596
+
1597
+ export declare namespace listVaults {
1598
+ /** One compatible deployment found while scanning TIDX. */
1599
+ type Match = {
1600
+ discovery: EarnVaults.Discovery
1601
+ position: Position
1602
+ record?: RegistryRecord | undefined
1603
+ }
1604
+
1605
+ /** Options for listing compatible Earn vaults. */
1606
+ type Options = z.output<typeof schema.getEarnVaults.Query> & {
1607
+ chainId: z.output<typeof Schema.ChainId>
1608
+ /** FX rate oracle backing TVL valuation. */
1609
+ oracle: FxOracle.Oracle
1610
+ }
1611
+
1612
+ /** TIDX deployment cursor position. */
1613
+ type Position = [number, number]
1614
+ }
1615
+
1616
+ /** Lists registry-curated Earn vaults. */
1617
+ export async function listVerifiedVaults(
1618
+ c: Context<App.Environment>,
1619
+ options: listVerifiedVaults.Options,
1620
+ ) {
1621
+ const db = Db.get(c.get('dbCached'))
1622
+ const [records, snapshot] = await Promise.all([
1623
+ core_EarnVaults.list(db, { chainId: options.chainId }),
1624
+ VerifiedTokens.snapshot(c, options.chainId),
1625
+ ])
1626
+ const decoded = options.cursor ? Cursor.decode(options.cursor, ['address']) : undefined
1627
+ const cursor = decoded?.[0]?.toString().toLowerCase()
1628
+ const candidates = records.filter(
1629
+ (record) => !cursor || record.vaultAddress.toLowerCase() > cursor,
1630
+ )
1631
+ const matches: listVerifiedVaults.Match[] = []
1632
+
1633
+ let offset = 0
1634
+ while (offset < candidates.length && matches.length <= options.limit) {
1635
+ const batchSize = discoveryBatchLimit({
1636
+ filtered: options.capability.length > 0 || options['engine.type'] !== undefined,
1637
+ limit: options.limit,
1638
+ matches: matches.length,
1639
+ remaining: candidates.length - offset,
1640
+ })
1641
+ const batch = candidates.slice(offset, offset + batchSize)
1642
+ offset += batch.length
1643
+ const discovered = await Promise.all(
1644
+ batch.map(async (record) => ({
1645
+ discovery: await resolveVaultDiscovery(c, {
1646
+ chainId: options.chainId,
1647
+ include: discoveryInclude(options),
1648
+ vaultAddress: Schema.Address.parse(record.vaultAddress.toLowerCase()),
1649
+ }),
1650
+ record,
1651
+ })),
1652
+ )
1653
+ for (const match of discovered) {
1654
+ if (!matchesVaultFilters(match.discovery, options)) continue
1655
+ matches.push(match)
1656
+ }
1657
+ }
1658
+
1659
+ const page = matches.slice(0, options.limit)
1660
+ const discoveries = page.map(({ discovery }) => discovery)
1661
+ const include = vaultInclude(options)
1662
+ const [apy, rates, tokens] = await Promise.all([
1663
+ resolvePageApy(c, {
1664
+ chainId: options.chainId,
1665
+ discoveries,
1666
+ include,
1667
+ window: options['apy.window'],
1668
+ }),
1669
+ resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
1670
+ resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
1671
+ ])
1672
+ const data = page.map((match) =>
1673
+ Response.validated(
1674
+ schema.VerifiedVault,
1675
+ resolveVault(c, { ...match, apy, include, rates, snapshot, tokens }),
1676
+ ),
1677
+ )
1678
+ const last = matches[options.limit - 1]
1679
+ return {
1680
+ data,
1681
+ ...(rates ? { meta: { valuation: Valuation.pricing(rates, options.oracle) } } : {}),
1682
+ nextCursor:
1683
+ matches.length > options.limit && last ? Cursor.encode([last.record.vaultAddress]) : null,
1684
+ }
1685
+ }
1686
+
1687
+ export declare namespace listVerifiedVaults {
1688
+ /** One verified vault and its current onchain state. */
1689
+ type Match = {
1690
+ discovery: EarnVaults.Discovery
1691
+ record: RegistryRecord
1692
+ }
1693
+
1694
+ /** Options for listing verified Earn vaults. */
1695
+ type Options = z.output<typeof schema.getVerifiedEarnVaults.Query> & {
1696
+ chainId: z.output<typeof Schema.ChainId>
1697
+ /** FX rate oracle backing TVL valuation. */
1698
+ oracle: FxOracle.Oracle
1699
+ }
1700
+ }
1701
+
1702
+ /** Gets one compatible Earn vault. */
1703
+ export async function getVault(c: Context<App.Environment>, options: getVault.Options) {
1704
+ const db = Db.get(c.get('dbCached'))
1705
+ const [record, snapshot] = await Promise.all([
1706
+ core_EarnVaults.get(db, {
1707
+ chainId: options.chainId,
1708
+ vaultAddress: options.vaultAddress,
1709
+ }),
1710
+ VerifiedTokens.snapshot(c, options.chainId),
1711
+ ])
1712
+ try {
1713
+ const discovery = await resolveVaultDiscovery(c, {
1714
+ chainId: options.chainId,
1715
+ include: discoveryInclude({ capability: [], include: options.include }),
1716
+ vaultAddress: options.vaultAddress,
1717
+ })
1718
+ const discoveries = [discovery]
1719
+ const include = vaultInclude(options)
1720
+ const [apy, rates, tokens] = await Promise.all([
1721
+ resolvePageApy(c, {
1722
+ chainId: options.chainId,
1723
+ discoveries,
1724
+ include,
1725
+ window: options['apy.window'],
1726
+ }),
1727
+ resolvePageRates(c, { discoveries, include, oracle: options.oracle, snapshot }),
1728
+ resolvePageTokens(c, { chainId: options.chainId, discoveries, include, snapshot }),
1729
+ ])
1730
+ return {
1731
+ ...resolveVault(c, {
1732
+ apy,
1733
+ discovery,
1734
+ include,
1735
+ rates,
1736
+ record,
1737
+ snapshot,
1738
+ tokens,
1739
+ }),
1740
+ ...(rates ? { meta: { valuation: Valuation.pricing(rates, options.oracle) } } : {}),
1741
+ }
1742
+ } catch (cause) {
1743
+ if (cause instanceof EarnVaults.NotFoundError || (!record && isIncompatible(cause)))
1744
+ throw new NotFoundError()
1745
+ throw cause
1746
+ }
1747
+ }
1748
+
1749
+ export declare namespace getVault {
1750
+ /** Options for getting one Earn vault. */
1751
+ type Options = {
1752
+ /** Canonical rate window the vault's APY covers. */
1753
+ 'apy.window': z.output<typeof schema.getEarnVault.Query>['apy.window']
1754
+ chainId: z.output<typeof Schema.ChainId>
1755
+ include: z.output<typeof schema.getEarnVault.Query>['include']
1756
+ /** FX rate oracle backing TVL valuation. */
1757
+ oracle: FxOracle.Oracle
1758
+ vaultAddress: z.output<typeof Schema.Address>
1759
+ }
1760
+ }
1761
+
1762
+ // Selecting a rate window is a request for the rate it measures, mirroring how
1763
+ // a capability filter promotes `capabilities`.
1764
+ function vaultInclude(options: vaultInclude.Options) {
1765
+ const { include } = options
1766
+ if (options['apy.window'] === undefined || include.includes('apy')) return include
1767
+ return [...include, 'apy' as const]
1768
+ }
1769
+
1770
+ declare namespace vaultInclude {
1771
+ type Options = Pick<z.output<typeof schema.getEarnVault.Query>, 'apy.window' | 'include'>
1772
+ }
1773
+
1774
+ function discoveryInclude(
1775
+ options: discoveryInclude.Options,
1776
+ ): readonly EarnVaults.resolve.Include[] {
1777
+ const includeCapabilities =
1778
+ options.include.includes('capabilities') ||
1779
+ options.include.includes('zone') ||
1780
+ options.capability.length > 0
1781
+ return [
1782
+ ...(options.include.includes('access') ? (['access'] as const) : []),
1783
+ ...(includeCapabilities ? (['capabilities'] as const) : []),
1784
+ ]
1785
+ }
1786
+
1787
+ declare namespace discoveryInclude {
1788
+ type Options = Pick<z.output<typeof schema.getEarnVaults.Query>, 'capability' | 'include'>
1789
+ }
1790
+
1791
+ async function resolveVaultDiscovery(
1792
+ c: Context<App.Environment>,
1793
+ options: resolveVaultDiscovery.Options,
1794
+ ) {
1795
+ return Store.memoize(
1796
+ () =>
1797
+ Timing.time(c, 'earn_vault_discovery', () =>
1798
+ EarnVaults.resolve({
1799
+ getClient: c.get('getClient'),
1800
+ include: options.include,
1801
+ input: {
1802
+ chainId: options.chainId,
1803
+ vaultAddress: options.vaultAddress,
1804
+ },
1805
+ zones: [...c.get('zones').values()],
1806
+ }),
1807
+ ),
1808
+ {
1809
+ key: `earn-vault:v3:${options.chainId}:${options.vaultAddress.toLowerCase()}:include:${options.include.join(',') || 'base'}`,
1810
+ store: c.get('store'),
1811
+ // Matches the routes' `state` policy `max-age` so the two cache layers do
1812
+ // not compound staleness on live vault state.
1813
+ ttl: Ttl.seconds(30),
1814
+ },
1815
+ )
1816
+ }
1817
+
1818
+ declare namespace resolveVaultDiscovery {
1819
+ type Options = {
1820
+ chainId: z.output<typeof Schema.ChainId>
1821
+ include: readonly EarnVaults.resolve.Include[]
1822
+ vaultAddress: z.output<typeof Schema.Address>
1823
+ }
1824
+ }
1825
+
1826
+ // Measuring a rate costs indexer boundary queries plus historical quotes against
1827
+ // an archive node, so an unrequested rate reaches neither.
1828
+ function resolvePageApy(c: Context<App.Environment>, options: resolvePageApy.Options) {
1829
+ if (!options.include.includes('apy')) return undefined
1830
+ return Timing.time(c, 'earn_apy', () =>
1831
+ EarnRates.resolve({
1832
+ chainId: options.chainId,
1833
+ getClient: c.get('getClient'),
1834
+ getTidx: c.get('getTidx'),
1835
+ store: c.get('store'),
1836
+ vaults: options.discoveries.map(({ vaultAddress }) => vaultAddress),
1837
+ window: options.window,
1838
+ }),
1839
+ )
1840
+ }
1841
+
1842
+ declare namespace resolvePageApy {
1843
+ type Options = {
1844
+ chainId: z.output<typeof Schema.ChainId>
1845
+ discoveries: readonly EarnVaults.Discovery[]
1846
+ include: z.output<typeof schema.getEarnVault.Query>['include']
1847
+ window: z.output<typeof schema.getEarnVault.Query>['apy.window']
1848
+ }
1849
+ }
1850
+
1851
+ // The TVL denomination is a fixed constant, not caller input, so a rate set
1852
+ // lacking it leaves `tvl` unpriced instead of failing the whole vault read.
1853
+ function resolvePageRates(c: Context<App.Environment>, options: resolvePageRates.Options) {
1854
+ // Valuing total assets is the only reason this read consults the oracle.
1855
+ if (!options.include.includes('tvl')) return undefined
1856
+ return Valuation.ratesFor(c, {
1857
+ currencies: options.discoveries.flatMap((discovery) => {
1858
+ const currency = options.snapshot.byAddress.get(discovery.assetToken.address)?.currency
1859
+ return currency === undefined ? [] : [currency]
1860
+ }),
1861
+ denomination: tvlDenomination,
1862
+ oracle: options.oracle,
1863
+ }).catch((cause) => {
1864
+ if (cause instanceof Valuation.UnsupportedDenominationError) return undefined
1865
+ throw cause
1866
+ })
1867
+ }
1868
+
1869
+ declare namespace resolvePageRates {
1870
+ type Options = {
1871
+ discoveries: readonly EarnVaults.Discovery[]
1872
+ include: z.output<typeof schema.getEarnVault.Query>['include']
1873
+ oracle: FxOracle.Oracle
1874
+ snapshot: VerifiedTokens.Snapshot
1875
+ }
1876
+ }
1877
+
1878
+ async function resolvePageTokens(c: Context<App.Environment>, options: resolvePageTokens.Options) {
1879
+ const metadata = new Map(
1880
+ options.discoveries.flatMap(({ assetToken, shareToken }) => [
1881
+ [Schema.Address.parse(assetToken.address.toLowerCase()), assetToken] as const,
1882
+ [Schema.Address.parse(shareToken.address.toLowerCase()), shareToken] as const,
1883
+ ]),
1884
+ )
1885
+ return Tokens.resolveTokens(c, {
1886
+ addresses: [...metadata.keys()],
1887
+ chainId: options.chainId,
1888
+ include: [
1889
+ 'token.verified',
1890
+ ...(options.include.includes('token.logoUri') ? (['token.logoUri'] as const) : []),
1891
+ ],
1892
+ metadata,
1893
+ snapshot: options.snapshot,
1894
+ })
1895
+ }
1896
+
1897
+ declare namespace resolvePageTokens {
1898
+ type Options = {
1899
+ chainId: z.output<typeof Schema.ChainId>
1900
+ discoveries: readonly EarnVaults.Discovery[]
1901
+ include: z.output<typeof schema.getEarnVault.Query>['include']
1902
+ snapshot: VerifiedTokens.Snapshot
1903
+ }
1904
+ }
1905
+
1906
+ function resolveVault(c: Context<App.Environment>, options: resolveVault.Options) {
1907
+ const { discovery, record } = options
1908
+ const valued = options.include.includes('tvl')
1909
+ ? Valuation.valuationFor({
1910
+ amount: BigInt(discovery.state.totalAssets),
1911
+ denomination: tvlDenomination,
1912
+ rates: options.rates,
1913
+ token: options.snapshot.byAddress.get(discovery.assetToken.address),
1914
+ })
1915
+ : undefined
1916
+ return serializeVault({
1917
+ apy: options.apy?.get(discovery.vaultAddress) ?? null,
1918
+ assetToken: getToken(options.tokens, discovery.assetToken.address),
1919
+ discovery,
1920
+ include: options.include,
1921
+ record,
1922
+ shareToken: getToken(options.tokens, discovery.shareToken.address),
1923
+ tvl: valued
1924
+ ? {
1925
+ amount: core_Value.from(valued.amount, tvlDecimals).toString(),
1926
+ currency: valued.currency,
1927
+ decimals: tvlDecimals,
1928
+ formatted: valued.amount,
1929
+ }
1930
+ : null,
1931
+ zones: [...c.get('zones').values()],
1932
+ })
1933
+ }
1934
+
1935
+ declare namespace resolveVault {
1936
+ type Options = {
1937
+ apy: ReadonlyMap<string, EarnRates.Apy | null> | undefined
1938
+ discovery: EarnVaults.Discovery
1939
+ include: z.output<typeof schema.getEarnVault.Query>['include']
1940
+ rates: Awaited<ReturnType<typeof Valuation.ratesFor>>
1941
+ record?: RegistryRecord | undefined
1942
+ snapshot: VerifiedTokens.Snapshot
1943
+ tokens: Tokens.resolveTokens.ReturnType
1944
+ }
1945
+ }
1946
+
1947
+ /** Serializes resolved onchain and curated fields into the shared vault contract. */
1948
+ export function serializeVault(options: serializeVault.Options): z.output<typeof schema.Vault> {
1949
+ const { discovery, record } = options
1950
+ if (options.include.includes('access') && !discovery.access)
1951
+ throw new Error('Earn vault access was not resolved.')
1952
+ if (options.include.includes('capabilities') && !discovery.capabilities)
1953
+ throw new Error('Earn vault capabilities were not resolved.')
1954
+ const vaultAddress = Schema.Address.parse(discovery.vaultAddress.toLowerCase())
1955
+ return Response.validated(schema.Vault, {
1956
+ ...(options.include.includes('access') ? { access: discovery.access } : {}),
1957
+ ...(options.include.includes('apy') ? { apy: options.apy } : {}),
1958
+ assetToken: options.assetToken,
1959
+ ...(options.include.includes('capabilities') ? { capabilities: discovery.capabilities } : {}),
1960
+ description: record?.description ?? null,
1961
+ engine: {
1962
+ address: discovery.engine.address.toLowerCase(),
1963
+ type: discovery.engine.type,
1964
+ venue: discovery.engine.venue?.toLowerCase() ?? null,
1965
+ },
1966
+ id: vaultAddress,
1967
+ instantLiquidity: discovery.instantLiquidity,
1968
+ label: record?.label ?? options.shareToken.name,
1969
+ sharePrice:
1970
+ discovery.sharePrice === null
1971
+ ? null
1972
+ : {
1973
+ amount: discovery.sharePrice,
1974
+ currency: options.assetToken.currency,
1975
+ decimals: options.assetToken.decimals,
1976
+ formatted: core_Value.format(BigInt(discovery.sharePrice), options.assetToken.decimals),
1977
+ },
1978
+ shareToken: options.shareToken,
1979
+ slug: record?.slug ?? null,
1980
+ state: discovery.state,
1981
+ ...(options.include.includes('tvl') ? { tvl: options.tvl } : {}),
1982
+ vaultAddress,
1983
+ verified: record !== undefined,
1984
+ ...(options.include.includes('zone')
1985
+ ? { zone: record ? resolveZone({ discovery, record, zones: options.zones }) : null }
1986
+ : {}),
1987
+ })
1988
+ }
1989
+
1990
+ export declare namespace serializeVault {
1991
+ /** Values used to serialize one resolved Earn vault. */
1992
+ type Options = {
1993
+ /** Measured rate, or null when unavailable; omitted unless `include` carries `apy`. */
1994
+ apy: EarnRates.Apy | null
1995
+ /** Resolved asset token. */
1996
+ assetToken: z.output<typeof Tokens.schema.Token>
1997
+ /** Live onchain vault state. */
1998
+ discovery: EarnVaults.Discovery
1999
+ /** Optional vault fields selected by the request. */
2000
+ include: z.output<typeof schema.getEarnVault.Query>['include']
2001
+ /** Curated registry row, when verified. */
2002
+ record?: core_EarnVaults.Record | undefined
2003
+ /** Resolved share token. */
2004
+ shareToken: z.output<typeof Tokens.schema.Token>
2005
+ /** USD value of the assets, or null when unpriced; omitted unless `include` carries `tvl`. */
2006
+ tvl: z.output<typeof schema.Vault>['tvl']
2007
+ /** Private Zones available to the vault. */
2008
+ zones: readonly Chain[]
2009
+ }
2010
+ }
2011
+
2012
+ function resolveZone(options: resolveZone.Options): z.output<typeof schema.Vault.shape.zone> {
2013
+ if (!options.discovery.capabilities?.privateRouting) return null
2014
+ const hasInputs = options.record.privateInputTokens.length > 0
2015
+ const hasOutputs = options.record.privateOutputTokens.length > 0
2016
+ if (!hasInputs && !hasOutputs) return null
2017
+ if (hasInputs !== hasOutputs)
2018
+ throw new Error('Private input and output tokens must both be configured.')
2019
+
2020
+ const configured = options.zones.filter((zone) => zone.sourceId === options.record.chainId)
2021
+ if (configured.length !== 1)
2022
+ throw new Error(`Expected one Earn Zone for chain ${options.record.chainId}.`)
2023
+ const zone = configured[0]!
2024
+ return {
2025
+ chainId: zone.id,
2026
+ inputTokens: options.record.privateInputTokens.map((token) =>
2027
+ Schema.Address.parse(token.toLowerCase()),
2028
+ ),
2029
+ name: zone.name,
2030
+ outputTokens: options.record.privateOutputTokens.map((token) =>
2031
+ Schema.Address.parse(token.toLowerCase()),
2032
+ ),
2033
+ }
2034
+ }
2035
+
2036
+ declare namespace resolveZone {
2037
+ type Options = {
2038
+ discovery: EarnVaults.Discovery
2039
+ record: RegistryRecord
2040
+ zones: readonly Chain[]
2041
+ }
2042
+ }
2043
+
2044
+ function getToken(
2045
+ tokens: Tokens.resolveTokens.ReturnType,
2046
+ address: z.output<typeof Schema.Address>,
2047
+ ): z.output<typeof Tokens.schema.Token> {
2048
+ const token = tokens.get(Schema.Address.parse(address.toLowerCase()))
2049
+ if (!token) throw new Error(`Token metadata was not resolved for ${address}.`)
2050
+ return Response.validated(Tokens.schema.Token, token)
2051
+ }
2052
+
2053
+ async function queryDeployments(
2054
+ c: Context<App.Environment>,
2055
+ options: queryDeployments.Options,
2056
+ ): Promise<queryDeployments.Result> {
2057
+ const filters = ['deployment_rank = 1']
2058
+ if (options.asset) filters.push(`lower(asset) = '${options.asset.toLowerCase()}'`)
2059
+ if (options.cursor)
2060
+ filters.push(
2061
+ Cursor.keyset([
2062
+ {
2063
+ literal: Cursor.literal(options.cursor[0], 'int'),
2064
+ name: 'block_num',
2065
+ order: 'desc',
2066
+ },
2067
+ {
2068
+ literal: Cursor.literal(options.cursor[1], 'int'),
2069
+ name: 'log_idx',
2070
+ order: 'desc',
2071
+ },
2072
+ ]),
2073
+ )
2074
+
2075
+ // ClickHouse supports ordering and windowing over decoded event CTEs.
2076
+ const result = await c
2077
+ .get('getTidx')(options.chainId)
2078
+ .fetch({
2079
+ chainId: options.chainId,
2080
+ engine: 'clickhouse',
2081
+ query: `
2082
+ SELECT "earnVault", asset, block_num, log_idx
2083
+ FROM (
2084
+ SELECT
2085
+ "earnVault",
2086
+ asset,
2087
+ block_num,
2088
+ log_idx,
2089
+ row_number() OVER (
2090
+ PARTITION BY lower("earnVault")
2091
+ ORDER BY block_num DESC, log_idx DESC
2092
+ ) AS deployment_rank
2093
+ FROM EarnStackDeployed
2094
+ )
2095
+ WHERE ${filters.join(' AND ')}
2096
+ ORDER BY block_num DESC, log_idx DESC
2097
+ LIMIT ${options.limit + 1}
2098
+ ` as string,
2099
+ signatures: [deployedSignature],
2100
+ })
2101
+
2102
+ const rows: readonly Record<string, unknown>[] = result.rows
2103
+ const items = rows.slice(0, options.limit).map((row) => {
2104
+ const vaultAddress = Schema.Address.safeParse(Value.toText(row['earnVault']))
2105
+ const asset = Schema.Address.safeParse(Value.toText(row['asset']))
2106
+ const blockNumber = Value.toNumber(row['block_num'])
2107
+ const logIndex = Value.toNumber(row['log_idx'])
2108
+ if (
2109
+ !vaultAddress.success ||
2110
+ !asset.success ||
2111
+ blockNumber === undefined ||
2112
+ logIndex === undefined
2113
+ )
2114
+ throw new Error('TIDX returned an invalid Earn deployment row.')
2115
+ return {
2116
+ asset: asset.data,
2117
+ position: [blockNumber, logIndex] as queryDeployments.Position,
2118
+ vaultAddress: vaultAddress.data,
2119
+ }
2120
+ })
2121
+ return { hasMore: rows.length > options.limit, items }
2122
+ }
2123
+
2124
+ declare namespace queryDeployments {
2125
+ type Item = {
2126
+ asset: z.output<typeof Schema.Address>
2127
+ position: Position
2128
+ vaultAddress: z.output<typeof Schema.Address>
2129
+ }
2130
+
2131
+ type Options = {
2132
+ asset?: z.output<typeof Schema.Address> | undefined
2133
+ chainId: z.output<typeof Schema.ChainId>
2134
+ cursor?: Position | undefined
2135
+ limit: number
2136
+ }
2137
+
2138
+ type Position = [number, number]
2139
+
2140
+ type Result = {
2141
+ hasMore: boolean
2142
+ items: readonly Item[]
2143
+ }
2144
+ }
2145
+
2146
+ /** Tests live vault state against the shared collection filters. */
2147
+ export function matchesVaultFilters(
2148
+ discovery: EarnVaults.Discovery,
2149
+ options: matchesVaultFilters.Options,
2150
+ ): boolean {
2151
+ if (options.asset && discovery.assetToken.address.toLowerCase() !== options.asset.toLowerCase())
2152
+ return false
2153
+ const capabilities = discovery.capabilities
2154
+ if (options.capability.length > 0 && !capabilities)
2155
+ throw new Error('Earn vault capabilities were not resolved.')
2156
+ if (capabilities && !options.capability.every((name) => capabilities[name])) return false
2157
+ if (options['engine.type'] && discovery.engine.type !== options['engine.type']) return false
2158
+ return true
2159
+ }
2160
+
2161
+ export declare namespace matchesVaultFilters {
2162
+ /** Filters shared by the all-vault and verified-vault collections. */
2163
+ type Options = Pick<
2164
+ z.output<typeof schema.getEarnVaults.Query>,
2165
+ 'asset' | 'capability' | 'engine.type'
2166
+ >
2167
+ }
2168
+
2169
+ /** Returns whether a failed read proves the address is not a compatible Earn vault. */
2170
+ export function isIncompatible(cause: unknown) {
2171
+ if (cause instanceof EarnVaults.NotFoundError) return true
2172
+ if (cause instanceof EarnVaults.VerificationError) return cause.result.status === 'mismatch'
2173
+ if (!(cause instanceof ContractFunctionExecutionError)) return false
2174
+ return Boolean(
2175
+ cause.walk(
2176
+ (error) =>
2177
+ error instanceof ContractFunctionRevertedError ||
2178
+ error instanceof ContractFunctionZeroDataError,
2179
+ ),
2180
+ )
2181
+ }
2182
+
2183
+ type RegistryRecord = Awaited<ReturnType<typeof core_EarnVaults.list>>[number]
2184
+
2185
+ /** Thrown when no compatible Earn vault exists at the requested address. */
2186
+ export class NotFoundError extends Error {
2187
+ override name = 'Earn.NotFoundError'
2188
+ }