tapimo 0.7.2 → 0.9.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 (300) 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 +7 -3
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +9 -2
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +20117 -19183
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +61 -4
  13. package/dist/App.js.map +1 -1
  14. package/dist/admin/App.d.ts +84 -74
  15. package/dist/admin/App.d.ts.map +1 -1
  16. package/dist/admin/apps/api-keys.d.ts +14 -1
  17. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  18. package/dist/admin/apps/api-keys.js +6 -2
  19. package/dist/admin/apps/api-keys.js.map +1 -1
  20. package/dist/admin/apps/organizations.d.ts +2 -0
  21. package/dist/admin/apps/organizations.d.ts.map +1 -1
  22. package/dist/admin/apps/verified-tokens.d.ts +11 -11
  23. package/dist/admin/ui.generated.d.ts +1 -1
  24. package/dist/admin/ui.generated.js +67 -67
  25. package/dist/admin/ui.generated.js.map +1 -1
  26. package/dist/analytics/Analytics.d.ts +2 -0
  27. package/dist/analytics/Analytics.d.ts.map +1 -1
  28. package/dist/analytics/Analytics.js +3 -0
  29. package/dist/analytics/Analytics.js.map +1 -1
  30. package/dist/apps/data/App.d.ts +11696 -13937
  31. package/dist/apps/data/App.d.ts.map +1 -1
  32. package/dist/apps/data/App.js +4 -8
  33. package/dist/apps/data/App.js.map +1 -1
  34. package/dist/apps/data/routes/activities.d.ts +271 -271
  35. package/dist/apps/data/routes/addresses.d.ts +398 -167
  36. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  37. package/dist/apps/data/routes/balances.d.ts +278 -8
  38. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  39. package/dist/apps/data/routes/balances.js +114 -2
  40. package/dist/apps/data/routes/balances.js.map +1 -1
  41. package/dist/apps/data/routes/blocks.d.ts +123 -123
  42. package/dist/apps/data/routes/coingecko.d.ts +170 -170
  43. package/dist/apps/data/routes/earn.d.ts +498 -6
  44. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  45. package/dist/apps/data/routes/earn.js +876 -4
  46. package/dist/apps/data/routes/earn.js.map +1 -1
  47. package/dist/apps/data/routes/exchanges.d.ts +10 -10
  48. package/dist/apps/data/routes/fee-amm.d.ts +2 -2
  49. package/dist/apps/data/routes/indexer.d.ts +6 -6
  50. package/dist/apps/data/routes/indexer.js +4 -1
  51. package/dist/apps/data/routes/indexer.js.map +1 -1
  52. package/dist/apps/data/routes/receipts.d.ts +235 -235
  53. package/dist/apps/data/routes/rpc.d.ts +31 -31
  54. package/dist/apps/data/routes/rpc.js +4 -1
  55. package/dist/apps/data/routes/rpc.js.map +1 -1
  56. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  57. package/dist/apps/data/routes/tokens.d.ts +449 -449
  58. package/dist/apps/data/routes/transactions.d.ts +2 -2
  59. package/dist/apps/data/routes/transfers.d.ts +81 -81
  60. package/dist/apps/data/routes/valuation.d.ts +1 -1
  61. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  62. package/dist/apps/data/routes/webhooks.d.ts +9 -9
  63. package/dist/apps/data/routes/zones.d.ts +14 -14
  64. package/dist/apps/funding/Access.d.ts +16 -0
  65. package/dist/apps/funding/Access.d.ts.map +1 -0
  66. package/dist/apps/funding/Access.js +22 -0
  67. package/dist/apps/funding/Access.js.map +1 -0
  68. package/dist/apps/{data/routes/funding.d.ts → funding/App.d.ts} +1640 -3217
  69. package/dist/apps/funding/App.d.ts.map +1 -0
  70. package/dist/apps/funding/App.js +61 -0
  71. package/dist/apps/funding/App.js.map +1 -0
  72. package/dist/apps/funding/Inventory.d.ts +5 -0
  73. package/dist/apps/funding/Inventory.d.ts.map +1 -0
  74. package/dist/apps/funding/Inventory.js +25 -0
  75. package/dist/apps/funding/Inventory.js.map +1 -0
  76. package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
  77. package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
  78. package/dist/apps/funding/ProviderAttempts.js +24 -0
  79. package/dist/apps/funding/ProviderAttempts.js.map +1 -0
  80. package/dist/apps/funding/index.d.ts +2 -0
  81. package/dist/apps/funding/index.d.ts.map +1 -0
  82. package/dist/apps/funding/index.js +2 -0
  83. package/dist/apps/funding/index.js.map +1 -0
  84. package/dist/apps/funding/routes/chains.d.ts +169 -0
  85. package/dist/apps/funding/routes/chains.d.ts.map +1 -0
  86. package/dist/apps/funding/routes/chains.js +80 -0
  87. package/dist/apps/funding/routes/chains.js.map +1 -0
  88. package/dist/apps/funding/routes/deposit-addresses.d.ts +1997 -0
  89. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
  90. package/dist/apps/funding/routes/deposit-addresses.js +778 -0
  91. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
  92. package/dist/apps/funding/routes/providers.d.ts +91 -0
  93. package/dist/apps/funding/routes/providers.d.ts.map +1 -0
  94. package/dist/apps/funding/routes/providers.js +56 -0
  95. package/dist/apps/funding/routes/providers.js.map +1 -0
  96. package/dist/apps/funding/routes/quotes.d.ts +426 -0
  97. package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
  98. package/dist/apps/funding/routes/quotes.js +188 -0
  99. package/dist/apps/funding/routes/quotes.js.map +1 -0
  100. package/dist/apps/funding/routes/transfers.d.ts +1999 -0
  101. package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
  102. package/dist/apps/funding/routes/transfers.js +614 -0
  103. package/dist/apps/funding/routes/transfers.js.map +1 -0
  104. package/dist/apps/index.d.ts +1 -0
  105. package/dist/apps/index.d.ts.map +1 -1
  106. package/dist/apps/index.js +1 -0
  107. package/dist/apps/index.js.map +1 -1
  108. package/dist/apps/management/App.d.ts +430 -165
  109. package/dist/apps/management/App.d.ts.map +1 -1
  110. package/dist/apps/management/routes/api-keys.d.ts +298 -21
  111. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  112. package/dist/apps/management/routes/api-keys.js +75 -1
  113. package/dist/apps/management/routes/api-keys.js.map +1 -1
  114. package/dist/apps/management/routes/billing.d.ts +6 -6
  115. package/dist/apps/management/routes/faucet.d.ts +35 -35
  116. package/dist/apps/management/routes/invitations.d.ts +122 -122
  117. package/dist/apps/management/routes/invite-links.d.ts +38 -38
  118. package/dist/apps/management/routes/members.d.ts +69 -69
  119. package/dist/apps/management/routes/orgs.d.ts +99 -99
  120. package/dist/apps/management/routes/projects.d.ts +25 -25
  121. package/dist/apps/management/routes/scopes.d.ts +1 -1
  122. package/dist/apps/management/routes/usage.d.ts +72 -72
  123. package/dist/apps/management/routes/verified-token-requests.d.ts +77 -77
  124. package/dist/apps/management/routes/webhooks.d.ts +128 -128
  125. package/dist/apps/mcp.d.ts +2 -3
  126. package/dist/apps/mcp.d.ts.map +1 -1
  127. package/dist/apps/mcp.js +16 -14
  128. package/dist/apps/mcp.js.map +1 -1
  129. package/dist/apps/metadata.d.ts +4 -0
  130. package/dist/apps/metadata.d.ts.map +1 -1
  131. package/dist/apps/metadata.js +10 -0
  132. package/dist/apps/metadata.js.map +1 -1
  133. package/dist/apps/mpp/App.d.ts +44 -44
  134. package/dist/cloudflare.d.ts +50 -0
  135. package/dist/cloudflare.d.ts.map +1 -1
  136. package/dist/cloudflare.js +178 -0
  137. package/dist/cloudflare.js.map +1 -1
  138. package/dist/db/Db.d.ts +2 -0
  139. package/dist/db/Db.d.ts.map +1 -1
  140. package/dist/db/Db.js +45 -0
  141. package/dist/db/Db.js.map +1 -1
  142. package/dist/db/Schema.d.ts +12 -0
  143. package/dist/db/Schema.d.ts.map +1 -1
  144. package/dist/db/Schema.js +20 -0
  145. package/dist/db/Schema.js.map +1 -1
  146. package/dist/db/tables/fundingDepositAddresses.d.ts +13 -0
  147. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
  148. package/dist/db/tables/fundingDepositAddresses.js +12 -0
  149. package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
  150. package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
  151. package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
  152. package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
  153. package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
  154. package/dist/db/tables/fundingDeposits.d.ts +74 -1
  155. package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
  156. package/dist/db/tables/fundingDeposits.js +126 -0
  157. package/dist/db/tables/fundingDeposits.js.map +1 -1
  158. package/dist/internal/Auth.d.ts +15 -11
  159. package/dist/internal/Auth.d.ts.map +1 -1
  160. package/dist/internal/Auth.js +52 -17
  161. package/dist/internal/Auth.js.map +1 -1
  162. package/dist/internal/Cache.js +1 -1
  163. package/dist/internal/Cache.js.map +1 -1
  164. package/dist/internal/EdgeCache.d.ts +2 -2
  165. package/dist/internal/EdgeCache.d.ts.map +1 -1
  166. package/dist/internal/EdgeCache.js +10 -4
  167. package/dist/internal/EdgeCache.js.map +1 -1
  168. package/dist/internal/IpAllowlist.d.ts +15 -0
  169. package/dist/internal/IpAllowlist.d.ts.map +1 -0
  170. package/dist/internal/IpAllowlist.js +27 -0
  171. package/dist/internal/IpAllowlist.js.map +1 -0
  172. package/dist/internal/OpenApi.d.ts +1 -1
  173. package/dist/internal/OpenApi.js +1 -1
  174. package/dist/internal/OpenApi.js.map +1 -1
  175. package/dist/internal/Tidx.d.ts +6 -0
  176. package/dist/internal/Tidx.d.ts.map +1 -1
  177. package/dist/internal/Tidx.js +13 -3
  178. package/dist/internal/Tidx.js.map +1 -1
  179. package/dist/internal/Viem.d.ts +8 -0
  180. package/dist/internal/Viem.d.ts.map +1 -1
  181. package/dist/internal/Viem.js +12 -5
  182. package/dist/internal/Viem.js.map +1 -1
  183. package/dist/internal/funding/Deposit.d.ts +42 -0
  184. package/dist/internal/funding/Deposit.d.ts.map +1 -1
  185. package/dist/internal/funding/Deposit.js +37 -5
  186. package/dist/internal/funding/Deposit.js.map +1 -1
  187. package/dist/internal/funding/Evidence.d.ts +1 -5
  188. package/dist/internal/funding/Evidence.d.ts.map +1 -1
  189. package/dist/internal/funding/Evidence.js +4 -3
  190. package/dist/internal/funding/Evidence.js.map +1 -1
  191. package/dist/internal/funding/Provider.d.ts +102 -0
  192. package/dist/internal/funding/Provider.d.ts.map +1 -1
  193. package/dist/internal/funding/Provider.js +5 -0
  194. package/dist/internal/funding/Provider.js.map +1 -1
  195. package/dist/internal/funding/Reconciliation.d.ts +16 -2
  196. package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
  197. package/dist/internal/funding/Reconciliation.js +348 -54
  198. package/dist/internal/funding/Reconciliation.js.map +1 -1
  199. package/dist/internal/funding/SourceObservation.d.ts +108 -0
  200. package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
  201. package/dist/internal/funding/SourceObservation.js +143 -0
  202. package/dist/internal/funding/SourceObservation.js.map +1 -0
  203. package/dist/internal/funding/Subsidy.d.ts +2 -2
  204. package/dist/internal/funding/Subsidy.d.ts.map +1 -1
  205. package/dist/internal/funding/Subsidy.js +1 -1
  206. package/dist/internal/funding/Subsidy.js.map +1 -1
  207. package/dist/internal/funding/index.d.ts +1 -0
  208. package/dist/internal/funding/index.d.ts.map +1 -1
  209. package/dist/internal/funding/index.js +1 -0
  210. package/dist/internal/funding/index.js.map +1 -1
  211. package/dist/internal/funding/providers/relay.d.ts +43 -1
  212. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  213. package/dist/internal/funding/providers/relay.js +438 -27
  214. package/dist/internal/funding/providers/relay.js.map +1 -1
  215. package/package.json +3 -3
  216. package/src/ApiKey.test.ts +35 -0
  217. package/src/ApiKey.ts +27 -0
  218. package/src/ApiKeys.test.ts +29 -0
  219. package/src/ApiKeys.ts +11 -3
  220. package/src/App.test.ts +55 -5
  221. package/src/App.ts +79 -5
  222. package/src/Client.test-d.ts +203 -28
  223. package/src/admin/apps/api-keys.test.ts +25 -1
  224. package/src/admin/apps/api-keys.ts +6 -2
  225. package/src/admin/ui.generated.ts +67 -67
  226. package/src/analytics/Analytics.ts +4 -0
  227. package/src/apps/composition.test.ts +130 -3
  228. package/src/apps/data/App.test.ts +0 -12
  229. package/src/apps/data/App.ts +4 -8
  230. package/src/apps/data/routes/balances.test.ts +42 -0
  231. package/src/apps/data/routes/balances.ts +218 -85
  232. package/src/apps/data/routes/earn.test.ts +363 -0
  233. package/src/apps/data/routes/earn.ts +1141 -3
  234. package/src/apps/data/routes/indexer.ts +2 -1
  235. package/src/apps/data/routes/rpc.ts +2 -0
  236. package/src/apps/data/routes/tokens.test.ts +6 -8
  237. package/src/apps/funding/Access.ts +26 -0
  238. package/src/apps/funding/App.test.ts +78 -0
  239. package/src/apps/funding/App.ts +78 -0
  240. package/src/apps/funding/Inventory.ts +30 -0
  241. package/src/apps/funding/ProviderAttempts.ts +26 -0
  242. package/src/apps/funding/index.ts +1 -0
  243. package/src/apps/funding/routes/chains.test.ts +78 -0
  244. package/src/apps/funding/routes/chains.ts +101 -0
  245. package/src/apps/funding/routes/deposit-addresses.test.ts +1649 -0
  246. package/src/apps/funding/routes/deposit-addresses.ts +932 -0
  247. package/src/apps/funding/routes/providers.test.ts +165 -0
  248. package/src/apps/funding/routes/providers.ts +73 -0
  249. package/src/apps/funding/routes/quotes.test.ts +511 -0
  250. package/src/apps/funding/routes/quotes.ts +205 -0
  251. package/src/apps/funding/routes/transfers.test.ts +1236 -0
  252. package/src/apps/funding/routes/transfers.ts +704 -0
  253. package/src/apps/index.ts +1 -0
  254. package/src/apps/management/routes/api-keys.test.ts +98 -0
  255. package/src/apps/management/routes/api-keys.ts +96 -1
  256. package/src/apps/mcp.test.ts +17 -2
  257. package/src/apps/mcp.ts +18 -13
  258. package/src/apps/metadata.ts +12 -0
  259. package/src/apps/mpp/internal/Policy.test.ts +7 -1
  260. package/src/cloudflare.ts +279 -0
  261. package/src/db/Db.ts +71 -0
  262. package/src/db/Schema.ts +24 -0
  263. package/src/db/tables/fundingDepositAddresses.ts +28 -0
  264. package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
  265. package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
  266. package/src/db/tables/fundingDeposits.test.ts +239 -0
  267. package/src/db/tables/fundingDeposits.ts +214 -1
  268. package/src/internal/Auth.test.ts +205 -5
  269. package/src/internal/Auth.ts +63 -19
  270. package/src/internal/Cache.test.ts +37 -3
  271. package/src/internal/Cache.ts +1 -1
  272. package/src/internal/EdgeCache.test.ts +7 -7
  273. package/src/internal/EdgeCache.ts +13 -4
  274. package/src/internal/IpAllowlist.test.ts +52 -0
  275. package/src/internal/IpAllowlist.ts +39 -0
  276. package/src/internal/Log.test.ts +7 -1
  277. package/src/internal/OpenApi.ts +1 -1
  278. package/src/internal/Tidx.test.ts +28 -0
  279. package/src/internal/Tidx.ts +19 -3
  280. package/src/internal/Viem.test.ts +28 -0
  281. package/src/internal/Viem.ts +21 -5
  282. package/src/internal/funding/Deposit.ts +74 -5
  283. package/src/internal/funding/Evidence.test.ts +2 -2
  284. package/src/internal/funding/Evidence.ts +4 -9
  285. package/src/internal/funding/Provider.ts +115 -0
  286. package/src/internal/funding/Reconciliation.local.test.ts +7 -0
  287. package/src/internal/funding/Reconciliation.test.ts +1364 -109
  288. package/src/internal/funding/Reconciliation.ts +467 -88
  289. package/src/internal/funding/SourceObservation.test.ts +301 -0
  290. package/src/internal/funding/SourceObservation.ts +267 -0
  291. package/src/internal/funding/Subsidy.ts +2 -2
  292. package/src/internal/funding/index.test.ts +1 -0
  293. package/src/internal/funding/index.ts +1 -0
  294. package/src/internal/funding/providers/relay.test.ts +94 -4
  295. package/src/internal/funding/providers/relay.ts +533 -27
  296. package/dist/apps/data/routes/funding.d.ts.map +0 -1
  297. package/dist/apps/data/routes/funding.js +0 -1557
  298. package/dist/apps/data/routes/funding.js.map +0 -1
  299. package/src/apps/data/routes/funding.test.ts +0 -2994
  300. package/src/apps/data/routes/funding.ts +0 -1821
@@ -28,6 +28,7 @@ type OpenApiDocument = {
28
28
  string,
29
29
  {
30
30
  get?: {
31
+ description?: string
31
32
  parameters?: readonly {
32
33
  description?: string
33
34
  name?: string
@@ -87,6 +88,214 @@ describe('schema.getEarnVaults.Query', () => {
87
88
  })
88
89
  })
89
90
 
91
+ describe('schema.getEarnVaultEarnings.Response', () => {
92
+ test('enforces status-specific earnings fields', () => {
93
+ const response = {
94
+ account: TestEarn.earningsAddress,
95
+ assetToken: TestEarn.assetTokenAddress,
96
+ currentValue: '1000000',
97
+ id: TestEarn.earningsAddress,
98
+ }
99
+
100
+ expect({
101
+ active: Earn.schema.getEarnVaultEarnings.Response.safeParse({
102
+ ...response,
103
+ activeEarnings: '0',
104
+ period: 'active',
105
+ status: 'complete',
106
+ }).success,
107
+ activeWithLifetimeEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
108
+ ...response,
109
+ lifetimeEarnings: '0',
110
+ period: 'active',
111
+ status: 'complete',
112
+ }).success,
113
+ completeWithoutEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
114
+ ...response,
115
+ period: 'lifetime',
116
+ status: 'complete',
117
+ }).success,
118
+ incomplete: Earn.schema.getEarnVaultEarnings.Response.safeParse({
119
+ ...response,
120
+ period: 'active',
121
+ status: 'incomplete_cost_basis',
122
+ }).success,
123
+ incompleteWindow: Earn.schema.getEarnVaultEarnings.Response.safeParse({
124
+ ...response,
125
+ period: '30d',
126
+ status: 'incomplete_cost_basis',
127
+ }).success,
128
+ incompleteWithEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
129
+ ...response,
130
+ lifetimeEarnings: '0',
131
+ period: 'lifetime',
132
+ status: 'incomplete_cost_basis',
133
+ }).success,
134
+ lifetime: Earn.schema.getEarnVaultEarnings.Response.safeParse({
135
+ ...response,
136
+ lifetimeEarnings: '0',
137
+ period: 'lifetime',
138
+ status: 'complete',
139
+ }).success,
140
+ pending: Earn.schema.getEarnVaultEarnings.Response.safeParse({
141
+ ...response,
142
+ period: 'lifetime',
143
+ status: 'pending_redemption',
144
+ }).success,
145
+ pendingActive: Earn.schema.getEarnVaultEarnings.Response.safeParse({
146
+ ...response,
147
+ period: 'active',
148
+ status: 'pending_redemption',
149
+ }).success,
150
+ pendingWindow: Earn.schema.getEarnVaultEarnings.Response.safeParse({
151
+ ...response,
152
+ period: '30d',
153
+ status: 'pending_redemption',
154
+ }).success,
155
+ pendingWithEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
156
+ ...response,
157
+ lifetimeEarnings: '0',
158
+ period: 'lifetime',
159
+ status: 'pending_redemption',
160
+ }).success,
161
+ window: Earn.schema.getEarnVaultEarnings.Response.safeParse({
162
+ ...response,
163
+ period: '30d',
164
+ status: 'complete',
165
+ windowEarnings: '0',
166
+ }).success,
167
+ windowWithActiveEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
168
+ ...response,
169
+ activeEarnings: '0',
170
+ period: '30d',
171
+ status: 'complete',
172
+ }).success,
173
+ windowWithoutEarnings: Earn.schema.getEarnVaultEarnings.Response.safeParse({
174
+ ...response,
175
+ period: '30d',
176
+ status: 'complete',
177
+ }).success,
178
+ }).toMatchInlineSnapshot(`
179
+ {
180
+ "active": true,
181
+ "activeWithLifetimeEarnings": false,
182
+ "completeWithoutEarnings": false,
183
+ "incomplete": true,
184
+ "incompleteWindow": true,
185
+ "incompleteWithEarnings": false,
186
+ "lifetime": true,
187
+ "pending": true,
188
+ "pendingActive": false,
189
+ "pendingWindow": true,
190
+ "pendingWithEarnings": false,
191
+ "window": true,
192
+ "windowWithActiveEarnings": false,
193
+ "windowWithoutEarnings": false,
194
+ }
195
+ `)
196
+ })
197
+ })
198
+
199
+ describe('schema.getEarnVaultEarnings.Query', () => {
200
+ test('defaults to lifetime and accepts active and trailing earnings', () => {
201
+ expect([
202
+ Earn.schema.getEarnVaultEarnings.Query.parse({}).period,
203
+ Earn.schema.getEarnVaultEarnings.Query.parse({ period: '30d' }).period,
204
+ Earn.schema.getEarnVaultEarnings.Query.parse({ period: 'active' }).period,
205
+ ]).toStrictEqual(['lifetime', '30d', 'active'])
206
+ })
207
+ })
208
+
209
+ describe('calculateWindowEarnings', () => {
210
+ test('adjusts snapshot growth for deposits and realized assets', () => {
211
+ expect(
212
+ Earn.calculateWindowEarnings({
213
+ deposited: 50n,
214
+ endingValue: 130n,
215
+ finalized: 10n,
216
+ openingValue: 100n,
217
+ redeemed: 15n,
218
+ withdrewExact: 5n,
219
+ }),
220
+ ).toBe(10n)
221
+ })
222
+ })
223
+
224
+ describe('calculateActiveCostBasis', () => {
225
+ test('allocates weighted-average basis across partial exits', () => {
226
+ expect(
227
+ Earn.calculateActiveCostBasis({
228
+ actions: [
229
+ { assets: 100n, kind: 'deposit', shares: 100n },
230
+ { assets: 300n, kind: 'deposit', shares: 100n },
231
+ { kind: 'redeem', shares: 100n },
232
+ ],
233
+ shareBalance: 100n,
234
+ }),
235
+ ).toBe(200n)
236
+ })
237
+
238
+ test('calculates remaining basis while an async redemption is pending', () => {
239
+ expect(
240
+ Earn.calculateActiveCostBasis({
241
+ actions: [
242
+ { assets: 100n, kind: 'deposit', shares: 100n },
243
+ { kind: 'request', requestId: 'request-1', shares: 40n },
244
+ ],
245
+ shareBalance: 60n,
246
+ }),
247
+ ).toBe(60n)
248
+ })
249
+
250
+ test('restores removed basis when an async redemption is cancelled', () => {
251
+ expect(
252
+ Earn.calculateActiveCostBasis({
253
+ actions: [
254
+ { assets: 100n, kind: 'deposit', shares: 100n },
255
+ { kind: 'request', requestId: 'request-1', shares: 40n },
256
+ { kind: 'cancel', requestId: 'request-1', shares: 40n },
257
+ ],
258
+ shareBalance: 100n,
259
+ }),
260
+ ).toBe(100n)
261
+ })
262
+
263
+ test('recovers after unknown-basis shares fully exit', () => {
264
+ expect(
265
+ Earn.calculateActiveCostBasis({
266
+ actions: [
267
+ { kind: 'unknown', shares: 100n },
268
+ { kind: 'redeem', shares: 100n },
269
+ { assets: 200n, kind: 'deposit', shares: 100n },
270
+ ],
271
+ shareBalance: 100n,
272
+ }),
273
+ ).toBe(200n)
274
+ })
275
+
276
+ test('rejects remaining shares with unknown cost basis', () => {
277
+ expect(
278
+ Earn.calculateActiveCostBasis({
279
+ actions: [
280
+ { assets: 100n, kind: 'deposit', shares: 100n },
281
+ { kind: 'unknown', shares: 100n },
282
+ { kind: 'redeem', shares: 100n },
283
+ ],
284
+ shareBalance: 100n,
285
+ }),
286
+ ).toBeUndefined()
287
+ })
288
+
289
+ test('rejects history that does not reconcile to the indexed share balance', () => {
290
+ expect(
291
+ Earn.calculateActiveCostBasis({
292
+ actions: [{ assets: 100n, kind: 'deposit', shares: 100n }],
293
+ shareBalance: 99n,
294
+ }),
295
+ ).toBeUndefined()
296
+ })
297
+ })
298
+
90
299
  describe('discoveryBatchLimit', () => {
91
300
  test('fetches one extra candidate without live filters', () => {
92
301
  expect(
@@ -864,6 +1073,136 @@ describe('GET /v1/earn/vaults/:vaultId/positions/:address', () => {
864
1073
  )
865
1074
  })
866
1075
 
1076
+ describe('GET /v1/earn/vaults/:vaultId/earnings/:address', () => {
1077
+ test('returns chain selection errors', async () => {
1078
+ const app = TestApp.create({ auth: false, cache })
1079
+ const invalid = await app.request(
1080
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/earnings/${TestEarn.earningsAddress}?chainId=abc`,
1081
+ )
1082
+ const unsupported = await app.request(
1083
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/earnings/${TestEarn.earningsAddress}?chainId=999999`,
1084
+ )
1085
+
1086
+ expect([
1087
+ (await TestApp.json(invalid, Schema.ErrorResponse)).error.code,
1088
+ (await TestApp.json(unsupported, Schema.ErrorResponse)).error.code,
1089
+ ]).toStrictEqual(['chain_id_invalid', 'chain_id_unsupported'])
1090
+ })
1091
+
1092
+ test('rejects malformed vault earnings parameters', async () => {
1093
+ const response = await TestApp.create({ auth: false, cache }).request(
1094
+ '/v1/earn/vaults/not-an-address/earnings/not-an-address',
1095
+ )
1096
+
1097
+ expect(response.status).toBe(400)
1098
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
1099
+ error: { code: 'earnings_invalid' },
1100
+ })
1101
+ })
1102
+
1103
+ test.runIf(Runtime.get().mode === 'testnet')(
1104
+ 'returns lifetime return through indexed vault events',
1105
+ async () => {
1106
+ const app = TestApp.create({ auth: false, cache })
1107
+ const response = await app.request(
1108
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/earnings/${TestEarn.earningsAddress}?chainId=${TestEarn.chain.id}`,
1109
+ )
1110
+
1111
+ expect(response.status).toBe(200)
1112
+ const earnings = await TestApp.json(response, Earn.schema.getEarnVaultEarnings.Response)
1113
+ if (earnings.status !== 'complete') throw new Error('Expected complete earnings history.')
1114
+ expect(earnings).toMatchObject({
1115
+ account: TestEarn.earningsAddress,
1116
+ assetToken: TestEarn.assetTokenAddress,
1117
+ currentValue: expect.stringMatching(/^\d+$/),
1118
+ id: TestEarn.earningsAddress,
1119
+ lifetimeEarnings: expect.stringMatching(/^-?\d+$/),
1120
+ period: 'lifetime',
1121
+ status: 'complete',
1122
+ })
1123
+ },
1124
+ 60_000,
1125
+ )
1126
+
1127
+ test.runIf(Runtime.get().mode === 'testnet')(
1128
+ 'returns earnings on shares still held',
1129
+ async () => {
1130
+ const app = TestApp.create({ auth: false, cache })
1131
+ const response = await app.request(
1132
+ `/v1/earn/vaults/${TestEarn.activeEarningsVaultAddress}/earnings/${TestEarn.activeEarningsAddress}?chainId=${TestEarn.chain.id}&period=active`,
1133
+ )
1134
+
1135
+ expect(response.status).toBe(200)
1136
+ const earnings = await TestApp.json(response, Earn.schema.getEarnVaultEarnings.Response)
1137
+ if (earnings.status !== 'complete' || earnings.period !== 'active')
1138
+ throw new Error('Expected complete active earnings history.')
1139
+ expect(earnings).toMatchObject({
1140
+ account: TestEarn.activeEarningsAddress,
1141
+ activeEarnings: expect.stringMatching(/^-?\d+$/),
1142
+ period: 'active',
1143
+ status: 'complete',
1144
+ })
1145
+ expect(earnings).not.toHaveProperty('lifetimeEarnings')
1146
+ },
1147
+ 60_000,
1148
+ )
1149
+
1150
+ test.runIf(Runtime.get().mode === 'testnet')(
1151
+ 'returns cash-flow-adjusted trailing earnings',
1152
+ async () => {
1153
+ const app = TestApp.create({ auth: false, cache })
1154
+ const response = await app.request(
1155
+ `/v1/earn/vaults/${TestEarn.activeEarningsVaultAddress}/earnings/${TestEarn.activeEarningsAddress}?chainId=${TestEarn.chain.id}&period=30d`,
1156
+ )
1157
+
1158
+ expect(response.status).toBe(200)
1159
+ const earnings = await TestApp.json(response, Earn.schema.getEarnVaultEarnings.Response)
1160
+ if (earnings.status !== 'complete' || earnings.period !== '30d')
1161
+ throw new Error('Expected complete trailing earnings history.')
1162
+ expect(earnings).toMatchObject({
1163
+ account: TestEarn.activeEarningsAddress,
1164
+ period: '30d',
1165
+ status: 'complete',
1166
+ windowEarnings: expect.stringMatching(/^-?\d+$/),
1167
+ })
1168
+ expect(earnings).not.toHaveProperty('activeEarnings')
1169
+ expect(earnings).not.toHaveProperty('lifetimeEarnings')
1170
+ },
1171
+ 60_000,
1172
+ )
1173
+
1174
+ test.runIf(Runtime.get().mode === 'testnet')(
1175
+ 'omits lifetime earnings when venue shares make the cost basis incomplete',
1176
+ async () => {
1177
+ const app = TestApp.create({ auth: false, cache })
1178
+ const response = await app.request(
1179
+ `/v1/earn/vaults/${TestEarn.vaultAddress}/earnings/${TestEarn.incompleteEarningsAddress}?chainId=${TestEarn.chain.id}`,
1180
+ )
1181
+
1182
+ expect(response.status).toBe(200)
1183
+ const earnings = await TestApp.json(response, Earn.schema.getEarnVaultEarnings.Response)
1184
+ expect(earnings.status).toBe('incomplete_cost_basis')
1185
+ expect(earnings).not.toHaveProperty('lifetimeEarnings')
1186
+ },
1187
+ 60_000,
1188
+ )
1189
+
1190
+ test.runIf(Runtime.get().mode === 'testnet')(
1191
+ 'returns not found for an address without an Earn vault',
1192
+ async () => {
1193
+ const response = await TestApp.create({ auth: false, cache }).request(
1194
+ `/v1/earn/vaults/0x1111111111111111111111111111111111111111/earnings/${TestEarn.earningsAddress}?chainId=${TestEarn.chain.id}`,
1195
+ )
1196
+
1197
+ expect(response.status).toBe(404)
1198
+ expect(await TestApp.json(response, Schema.ErrorResponse)).toMatchObject({
1199
+ error: { code: 'earn_vault_not_found' },
1200
+ })
1201
+ },
1202
+ 60_000,
1203
+ )
1204
+ })
1205
+
867
1206
  describe('GET /v1/earn/vaults', () => {
868
1207
  test.runIf(Runtime.get().mode === 'testnet')(
869
1208
  'extracts an indexed deployment and applies onchain filters',
@@ -1039,6 +1378,7 @@ describe('OpenAPI', () => {
1039
1378
  '/v1/earn/vaults',
1040
1379
  '/v1/earn/vaults/verified',
1041
1380
  '/v1/earn/vaults/{vaultId}',
1381
+ '/v1/earn/vaults/{vaultId}/earnings/{address}',
1042
1382
  '/v1/earn/vaults/{vaultId}/positions/{address}',
1043
1383
  ]),
1044
1384
  )
@@ -1105,4 +1445,27 @@ describe('OpenAPI', () => {
1105
1445
  expect(address?.schema?.examples).toStrictEqual([TestEarn.positionAddress])
1106
1446
  expect(vaultId?.schema?.examples).toStrictEqual([TestEarn.mainnetVaultAddress])
1107
1447
  })
1448
+
1449
+ test('documents vault-earnings variants', async () => {
1450
+ const app = TestApp.create({ auth: false, cache })
1451
+ const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
1452
+ const operation = spec.paths['/v1/earn/vaults/{vaultId}/earnings/{address}']?.get
1453
+ const period = operation?.parameters?.find((parameter) => parameter.name === 'period')
1454
+ const response = JSON.stringify(operation?.responses)
1455
+
1456
+ expect(operation?.description).toContain('the trailing 30 days')
1457
+ expect(period?.description).toContain('trailing 30 days')
1458
+ expect(period?.schema?.enum).toStrictEqual(['30d', 'active', 'lifetime'])
1459
+ expect(period?.schema?.examples).toStrictEqual(['lifetime'])
1460
+ expect(response).toContain('"earn_vault_not_found"')
1461
+ expect(response).toContain('Stable resource ID for these vault earnings')
1462
+ expect(response).toContain('Current share value plus completed redemptions')
1463
+ expect(response).toContain('weighted-average asset cost basis')
1464
+ expect(response).toContain('Ending share value plus realized assets')
1465
+ expect(response).toContain('cannot reconstruct the asset cost basis for the requested period')
1466
+ expect(response).toContain('"active"')
1467
+ expect(response).toContain('"30d"')
1468
+ expect(response).toContain('"incomplete_cost_basis"')
1469
+ expect(response).toContain('"pending_redemption"')
1470
+ })
1108
1471
  })