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
@@ -1,6 +1,6 @@
1
1
  import { Hono } from 'hono';
2
2
  import { Value as core_Value } from 'ox';
3
- import { ContractFunctionExecutionError, ContractFunctionRevertedError, ContractFunctionZeroDataError, } from 'viem';
3
+ import { ContractFunctionExecutionError, ContractFunctionRevertedError, ContractFunctionZeroDataError, zeroAddress, } from 'viem';
4
4
  import * as z from 'zod/mini';
5
5
  import * as Db from '../../../db/Db.js';
6
6
  import * as core_EarnVaults from '../../../db/tables/earnVaults.js';
@@ -13,6 +13,7 @@ import * as OpenApi from '../../../internal/OpenApi.js';
13
13
  import * as Response from '../../../internal/Response.js';
14
14
  import * as Schema from '../../../internal/Schema.js';
15
15
  import * as Store from '../../../internal/Store.js';
16
+ import * as Tidx from '../../../internal/Tidx.js';
16
17
  import * as Timing from '../../../internal/Timing.js';
17
18
  import * as Ttl from '../../../internal/Ttl.js';
18
19
  import * as Value from '../../../internal/Value.js';
@@ -21,8 +22,17 @@ import * as FxOracle from '../FxOracle.js';
21
22
  import * as Tokens from './tokens.js';
22
23
  import * as Valuation from './valuation.js';
23
24
  const deployedSignature = '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)';
25
+ const depositedSignature = 'event Deposited(address indexed caller,address indexed receiver,uint256 assets,uint256 earnShares)';
26
+ const redeemedSignature = 'event Redeemed(address indexed caller,address indexed receiver,uint256 earnShares,uint256 assets)';
27
+ const withdrewExactSignature = 'event WithdrewExact(address indexed caller,address indexed receiver,uint256 assets,uint256 earnSharesBurned)';
28
+ const redeemFinalizedSignature = 'event RedeemFinalized(bytes32 indexed requestId,address indexed receiver,uint256 earnShares,address asset,uint256 assets)';
29
+ const redeemCancelledSignature = 'event RedeemCancelled(bytes32 indexed requestId,address indexed receiver,uint256 earnShares)';
30
+ const redeemRequestedSignature = 'event RedeemRequested(bytes32 indexed requestId,address indexed requester,address indexed receiver,uint256 earnShares)';
31
+ const venueSharesDepositedSignature = 'event VenueSharesDeposited(address indexed caller,address indexed receiver,uint256 requestedVenueShares,uint256 receivedVenueShares,uint256 earnShares)';
24
32
  const maxScannedDeployments = 250;
25
33
  const scanLimit = 50;
34
+ /** Block boundary that includes all indexed history. */
35
+ const beforeFirstBlockNumber = -1;
26
36
  /** Vault TVL is reported in USD only; the API exposes no denomination parameter. */
27
37
  const tvlDenomination = 'USD';
28
38
  /** `Valuation.valuationFor` floors to 6 dp, so 6 is the precision a valued amount carries. */
@@ -155,6 +165,64 @@ export var schema;
155
165
  shareToken: Schema.tokenAddress('0x20c000000000000000000000300e14ab91a10769').check(z.describe('TIP-20 token representing Earn shares.')),
156
166
  value: Schema.DecimalString.check(z.describe('Current asset value of this account’s Earn shares, including fees, in asset base units.'), z.meta({ examples: ['100000000'] })),
157
167
  });
168
+ const earningsBase = z.strictObject({
169
+ account: Schema.Address.check(z.describe('Account whose vault earnings were calculated.'), z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] })),
170
+ assetToken: Schema.TokenAddress.check(z.describe('TIP-20 asset token accepted by the vault.'), z.meta({ examples: ['0x20c000000000000000000000ff04042ee92fd449'] })),
171
+ currentValue: Schema.DecimalString.check(z.describe('Asset value of the account’s active Earn shares at the latest indexed block, in asset base units.'), z.meta({ examples: ['600000'] })),
172
+ id: Schema.Address.check(z.describe('Stable resource ID for these vault earnings, equal to the account address.'), z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] })),
173
+ });
174
+ const earningsPeriod = z
175
+ .enum(['30d', 'active', 'lifetime'])
176
+ .check(z.describe('Earnings period to calculate: trailing 30 days, shares still held, or lifetime history.'), z.meta({ examples: ['lifetime'] }));
177
+ const earnings = z.union([
178
+ z.extend(earningsBase, {
179
+ lifetimeEarnings: z
180
+ .string()
181
+ .check(z.regex(/^-?\d+$/), z.describe('Current share value plus completed redemptions, exact withdrawals, and finalized async assets, minus asset deposits, at the latest indexed block.'), z.meta({ examples: ['0'] })),
182
+ period: z
183
+ .literal('lifetime')
184
+ .check(z.describe('Returns lifetime net asset earnings.'), z.meta({ examples: ['lifetime'] })),
185
+ status: z
186
+ .literal('complete')
187
+ .check(z.describe('Earnings include a complete asset-denominated cost basis.'), z.meta({ examples: ['complete'] })),
188
+ }),
189
+ z.extend(earningsBase, {
190
+ activeEarnings: z
191
+ .string()
192
+ .check(z.regex(/^-?\d+$/), z.describe('Current share value minus the weighted-average asset cost basis of shares still held, at the latest indexed block.'), z.meta({ examples: ['0'] })),
193
+ period: z
194
+ .literal('active')
195
+ .check(z.describe('Returns earnings on shares still held.'), z.meta({ examples: ['active'] })),
196
+ status: z
197
+ .literal('complete')
198
+ .check(z.describe('Earnings include a complete asset-denominated cost basis.'), z.meta({ examples: ['complete'] })),
199
+ }),
200
+ z.extend(earningsBase, {
201
+ period: z
202
+ .literal('30d')
203
+ .check(z.describe('Returns cash-flow-adjusted earnings over the trailing 30 days.'), z.meta({ examples: ['30d'] })),
204
+ status: z
205
+ .literal('complete')
206
+ .check(z.describe('Earnings include a complete asset-denominated cost basis.'), z.meta({ examples: ['complete'] })),
207
+ windowEarnings: z
208
+ .string()
209
+ .check(z.regex(/^-?\d+$/), z.describe('Ending share value plus realized assets, minus deposits and opening share value, over the trailing 30 days.'), z.meta({ examples: ['0'] })),
210
+ }),
211
+ z.extend(earningsBase, {
212
+ period: earningsPeriod,
213
+ status: z
214
+ .literal('incomplete_cost_basis')
215
+ .check(z.describe('Earnings are unavailable when indexed activity cannot reconstruct the asset cost basis for the requested period.'), z.meta({ examples: ['incomplete_cost_basis'] })),
216
+ }),
217
+ z.extend(earningsBase, {
218
+ period: z
219
+ .enum(['30d', 'lifetime'])
220
+ .check(z.describe('Earnings period with an unresolved async redemption.'), z.meta({ examples: ['lifetime'] })),
221
+ status: z
222
+ .literal('pending_redemption')
223
+ .check(z.describe('Earnings are unavailable while an async redemption remains open.'), z.meta({ examples: ['pending_redemption'] })),
224
+ }),
225
+ ]);
158
226
  const meta = z
159
227
  .object({
160
228
  valuation: z
@@ -251,6 +319,26 @@ export var schema;
251
319
  /** Current asset and Earn-share state for one account in one vault. */
252
320
  getEarnVaultPosition.Response = Schema.describe(position, 'Current asset and Earn-share balances, allowances, and asset value for an account in one Earn vault.');
253
321
  })(getEarnVaultPosition = schema.getEarnVaultPosition || (schema.getEarnVaultPosition = {}));
322
+ /** Schemas for the getEarnVaultEarnings operation. */
323
+ let getEarnVaultEarnings;
324
+ (function (getEarnVaultEarnings) {
325
+ /** Path parameters identifying the Earn vault and account. */
326
+ getEarnVaultEarnings.Params = z
327
+ .strictObject({
328
+ address: Schema.Address.check(z.describe('Account whose vault earnings to read.')),
329
+ vaultId: Schema.address('0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a').check(z.describe('Earn vault contract address.')),
330
+ })
331
+ .check(z.describe('Path parameters identifying an Earn vault account.'));
332
+ /** Query parameters selecting the chain containing the vault. */
333
+ getEarnVaultEarnings.Query = z
334
+ .strictObject({
335
+ chainId: Schema.ChainIdQuery,
336
+ period: z._default(earningsPeriod, 'lifetime'),
337
+ })
338
+ .check(z.describe('Query parameters selecting an Earn vault account.'));
339
+ /** Earnings for one account and period in one Earn vault. */
340
+ getEarnVaultEarnings.Response = Schema.describe(earnings, 'Indexed asset value and cost-basis-aware earnings for an account in one Earn vault.');
341
+ })(getEarnVaultEarnings = schema.getEarnVaultEarnings || (schema.getEarnVaultEarnings = {}));
254
342
  })(schema || (schema = {}));
255
343
  /** Creates the public Earn-vault handlers. */
256
344
  export function earn(options = {}) {
@@ -269,7 +357,7 @@ export function earn(options = {}) {
269
357
  schema: schema.getEarnVaults.Response,
270
358
  },
271
359
  }),
272
- summary: 'List Earn vaults',
360
+ summary: 'List vaults',
273
361
  tags: ['Earn'],
274
362
  }),
275
363
  // Live state and rate enrichment are embedded, so every vault read caches
@@ -363,7 +451,7 @@ export function earn(options = {}) {
363
451
  schema: schema.getEarnVaultPosition.Response,
364
452
  },
365
453
  }),
366
- summary: 'Get vault position',
454
+ summary: 'Get account position',
367
455
  tags: ['Earn'],
368
456
  }), Cache.response({
369
457
  cacheControl: Cache.policies.state,
@@ -427,6 +515,94 @@ export function earn(options = {}) {
427
515
  }
428
516
  return Response.upstream(c, cause);
429
517
  }
518
+ })
519
+ .get('/v1/earn/vaults/:vaultId/earnings/:address', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('param', schema.getEarnVaultEarnings.Params, {
520
+ code: 'earnings_invalid',
521
+ message: 'Invalid vault earnings',
522
+ }), OpenApi.validate('query', schema.getEarnVaultEarnings.Query, {
523
+ code: 'query_invalid',
524
+ message: 'Invalid query parameters',
525
+ }), OpenApi.describeRoute({
526
+ description: 'Gets an account’s indexed asset value and earnings for lifetime history, the trailing 30 days, or shares still held.',
527
+ operationId: 'getEarnVaultEarnings',
528
+ responses: OpenApi.responses({
529
+ errors: {
530
+ 400: {
531
+ codes: [
532
+ 'chain_id_invalid',
533
+ 'chain_id_unsupported',
534
+ 'earnings_invalid',
535
+ 'query_invalid',
536
+ ],
537
+ },
538
+ 404: {
539
+ codes: ['earn_vault_not_found'],
540
+ description: 'No compatible Earn vault was found at this address.',
541
+ },
542
+ 502: 'Could not read the Earn vault earnings.',
543
+ },
544
+ success: {
545
+ description: 'Cost-basis-aware earnings for one account in one vault.',
546
+ schema: schema.getEarnVaultEarnings.Response,
547
+ },
548
+ }),
549
+ summary: 'Get vault earnings',
550
+ tags: ['Earn'],
551
+ }), Cache.response({
552
+ cacheControl: Cache.policies.state,
553
+ key: (c) => Cache.urlKey(c, schema.getEarnVaultEarnings.Query),
554
+ name: 'tempo-api:earn-vault-earnings:v3',
555
+ }), async (c) => {
556
+ if (Auth.narrowAccess)
557
+ return Auth.paidAccessError(c);
558
+ if (OpenApi.narrowValidation)
559
+ return Response.error(c, {
560
+ code: 'chain_id_invalid',
561
+ message: 'Invalid chain id',
562
+ status: 400,
563
+ });
564
+ if (OpenApi.narrowValidation)
565
+ return Response.error(c, {
566
+ code: 'chain_id_unsupported',
567
+ message: 'Unsupported chain id',
568
+ status: 400,
569
+ });
570
+ if (OpenApi.narrowValidation)
571
+ return OpenApi.validationError(c, {
572
+ code: 'earnings_invalid',
573
+ message: 'Invalid request parameters',
574
+ });
575
+ if (OpenApi.narrowValidation)
576
+ return OpenApi.validationError(c, {
577
+ code: 'query_invalid',
578
+ message: 'Invalid query parameters',
579
+ });
580
+ const { address, vaultId } = c.req.valid('param');
581
+ const query = c.req.valid('query');
582
+ const chainId = query.chainId ?? c.get('chainId');
583
+ try {
584
+ const data = await getVaultEarnings(c, {
585
+ account: address,
586
+ chainId,
587
+ period: query.period,
588
+ vault: vaultId,
589
+ });
590
+ return c.json(Response.validated(schema.getEarnVaultEarnings.Response, data), 200);
591
+ }
592
+ catch (cause) {
593
+ try {
594
+ await resolveVaultDiscovery(c, { chainId, include: [], vaultAddress: vaultId });
595
+ }
596
+ catch (vaultCause) {
597
+ if (isIncompatible(vaultCause))
598
+ return Response.error(c, {
599
+ code: 'earn_vault_not_found',
600
+ message: 'Earn vault not found',
601
+ status: 404,
602
+ });
603
+ }
604
+ return Response.upstream(c, cause);
605
+ }
430
606
  })
431
607
  .get('/v1/earn/vaults/:vaultId', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('param', schema.getEarnVault.Params, {
432
608
  code: 'vault_id_invalid',
@@ -450,7 +626,7 @@ export function earn(options = {}) {
450
626
  schema: schema.getEarnVault.Response,
451
627
  },
452
628
  }),
453
- summary: 'Get Earn vault',
629
+ summary: 'Get vault',
454
630
  tags: ['Earn'],
455
631
  }), Cache.response({
456
632
  cacheControl: Cache.policies.state,
@@ -493,6 +669,702 @@ export function earn(options = {}) {
493
669
  }
494
670
  });
495
671
  }
672
+ /** Reads one account's indexed value and returns earnings when the history is complete. */
673
+ export async function getVaultEarnings(c, options) {
674
+ const client = c.get('getClient')(options.chainId);
675
+ const tidx = c.get('getTidx')(options.chainId);
676
+ const block = await getLatestIndexedBlock(tidx);
677
+ const blockNumber = block.number;
678
+ if (options.period === '30d')
679
+ return getWindowVaultEarnings(c, {
680
+ account: options.account,
681
+ blockNumber,
682
+ chainId: options.chainId,
683
+ period: options.period,
684
+ timestamp: block.timestamp,
685
+ vault: options.vault,
686
+ });
687
+ const position = client.earn.getPosition({
688
+ account: options.account,
689
+ blockNumber: BigInt(blockNumber),
690
+ vault: options.vault,
691
+ });
692
+ if (options.period === 'active') {
693
+ const current = await position;
694
+ const base = {
695
+ account: options.account,
696
+ assetToken: current.assetToken,
697
+ currentValue: current.value.toString(),
698
+ id: options.account,
699
+ period: options.period,
700
+ };
701
+ const activeCostBasis = await getActiveCostBasis(tidx, {
702
+ account: options.account,
703
+ blockNumber,
704
+ shareBalance: current.shareBalance,
705
+ shareToken: current.shareToken,
706
+ vault: options.vault,
707
+ });
708
+ if (activeCostBasis === undefined)
709
+ return { ...base, status: 'incomplete_cost_basis' };
710
+ return {
711
+ ...base,
712
+ activeEarnings: (current.value - activeCostBasis).toString(),
713
+ status: 'complete',
714
+ };
715
+ }
716
+ const [history, current] = await Promise.all([
717
+ getVaultEarningsHistory(tidx, {
718
+ account: options.account,
719
+ blockNumber,
720
+ startBlockNumber: beforeFirstBlockNumber,
721
+ vault: options.vault,
722
+ }),
723
+ position,
724
+ ]);
725
+ const externalShareTransfer = await hasExternalShareTransfer(tidx, {
726
+ account: options.account,
727
+ blockNumber,
728
+ shareToken: current.shareToken,
729
+ startBlockNumber: beforeFirstBlockNumber,
730
+ vault: options.vault,
731
+ });
732
+ const base = {
733
+ account: options.account,
734
+ assetToken: current.assetToken,
735
+ currentValue: current.value.toString(),
736
+ id: options.account,
737
+ period: options.period,
738
+ };
739
+ if (history.incompleteCostBasis || externalShareTransfer)
740
+ return { ...base, status: 'incomplete_cost_basis' };
741
+ if (history.pendingRedemption)
742
+ return { ...base, status: 'pending_redemption' };
743
+ const lifetimeEarnings = current.value + history.redeemed + history.withdrewExact + history.finalized - history.deposited;
744
+ return { ...base, lifetimeEarnings: lifetimeEarnings.toString(), status: 'complete' };
745
+ }
746
+ /** Reads cash-flow-adjusted earnings over a trailing indexed window. */
747
+ async function getWindowVaultEarnings(c, options) {
748
+ const client = c.get('getClient')(options.chainId);
749
+ const tidx = c.get('getTidx')(options.chainId);
750
+ const [current, startBlockNumber] = await Promise.all([
751
+ client.earn.getPosition({
752
+ account: options.account,
753
+ blockNumber: BigInt(options.blockNumber),
754
+ vault: options.vault,
755
+ }),
756
+ getWindowStartBlockNumber(tidx, { days: 30, timestamp: options.timestamp }),
757
+ ]);
758
+ const [externalShareTransfer, history, opening] = await Promise.all([
759
+ hasExternalShareTransfer(tidx, {
760
+ account: options.account,
761
+ blockNumber: options.blockNumber,
762
+ shareToken: current.shareToken,
763
+ startBlockNumber,
764
+ vault: options.vault,
765
+ }),
766
+ getVaultEarningsHistory(tidx, {
767
+ account: options.account,
768
+ blockNumber: options.blockNumber,
769
+ startBlockNumber,
770
+ vault: options.vault,
771
+ }),
772
+ getWindowShareState(tidx, {
773
+ account: options.account,
774
+ blockNumber: options.blockNumber,
775
+ shareToken: current.shareToken,
776
+ startBlockNumber,
777
+ vault: options.vault,
778
+ }),
779
+ ]);
780
+ const base = {
781
+ account: options.account,
782
+ assetToken: current.assetToken,
783
+ currentValue: current.value.toString(),
784
+ id: options.account,
785
+ period: options.period,
786
+ };
787
+ if (opening === undefined ||
788
+ externalShareTransfer ||
789
+ history.incompleteCostBasis ||
790
+ opening.crossBoundaryRedemption ||
791
+ opening.endShares !== current.shareBalance)
792
+ return { ...base, status: 'incomplete_cost_basis' };
793
+ if (history.pendingRedemption)
794
+ return { ...base, status: 'pending_redemption' };
795
+ const openingValue = opening.startShares === 0n
796
+ ? 0n
797
+ : await client.earn.getRedeemQuote({
798
+ blockNumber: BigInt(startBlockNumber),
799
+ shareAmount: opening.startShares,
800
+ vault: options.vault,
801
+ });
802
+ const windowEarnings = calculateWindowEarnings({
803
+ deposited: history.deposited,
804
+ endingValue: current.value,
805
+ finalized: history.finalized,
806
+ openingValue,
807
+ redeemed: history.redeemed,
808
+ withdrewExact: history.withdrewExact,
809
+ });
810
+ return { ...base, status: 'complete', windowEarnings: windowEarnings.toString() };
811
+ }
812
+ /** Calculates cash-flow-adjusted earnings between two position snapshots. */
813
+ export function calculateWindowEarnings(options) {
814
+ return (options.endingValue +
815
+ options.redeemed +
816
+ options.withdrewExact +
817
+ options.finalized -
818
+ options.deposited -
819
+ options.openingValue);
820
+ }
821
+ /** Reads the latest block whose indexed logs back the earnings calculation. */
822
+ async function getLatestIndexedBlock(tidx) {
823
+ const result = await tidx.fetch({
824
+ engine: 'clickhouse',
825
+ query: 'SELECT num, timestamp FROM blocks ORDER BY num DESC LIMIT 1',
826
+ });
827
+ const number = Value.toNumber(result.rows[0]?.['num']);
828
+ const timestamp = Value.toIsoDateTime(result.rows[0]?.['timestamp']);
829
+ if (number === undefined || timestamp === undefined)
830
+ throw new Error('TIDX returned no indexed block.');
831
+ return { number, timestamp };
832
+ }
833
+ /** Resolves the last indexed block at or before a trailing-day boundary. */
834
+ async function getWindowStartBlockNumber(tidx, options) {
835
+ const cutoff = new Date(Date.parse(options.timestamp) - Ttl.days(options.days)).toISOString();
836
+ const result = await tidx.fetch({
837
+ query: `SELECT num FROM blocks WHERE timestamp <= '${cutoff}' ORDER BY num DESC LIMIT 1`,
838
+ });
839
+ const row = result.rows[0];
840
+ if (!row)
841
+ return beforeFirstBlockNumber;
842
+ const blockNumber = Value.toNumber(row['num']);
843
+ if (blockNumber === undefined)
844
+ throw new Error('TIDX returned no Earn earnings boundary.');
845
+ return blockNumber;
846
+ }
847
+ /** Reads the indexed cash flows and exceptional states behind one earnings calculation. */
848
+ async function getVaultEarningsHistory(tidx, options) {
849
+ const result = await tidx.fetch({
850
+ engine: 'clickhouse',
851
+ query: `
852
+ SELECT
853
+ (
854
+ SELECT toString(sum(toUInt256(assets)))
855
+ FROM Deposited
856
+ WHERE address = '${options.vault}'
857
+ AND receiver = '${options.account}'
858
+ AND block_num > ${options.startBlockNumber}
859
+ AND block_num <= ${options.blockNumber}
860
+ ) AS deposited_assets,
861
+ (
862
+ SELECT toString(sum(toUInt256(finalized.assets)))
863
+ FROM RedeemFinalized AS finalized
864
+ INNER JOIN RedeemRequested AS requested
865
+ ON requested.address = finalized.address
866
+ AND requested."requestId" = finalized."requestId"
867
+ WHERE finalized.address = '${options.vault}'
868
+ AND requested.requester = '${options.account}'
869
+ AND finalized.block_num > ${options.startBlockNumber}
870
+ AND finalized.block_num <= ${options.blockNumber}
871
+ AND requested.block_num <= ${options.blockNumber}
872
+ ) AS finalized_assets,
873
+ (
874
+ SELECT count()
875
+ FROM RedeemRequested AS requested
876
+ LEFT ANTI JOIN RedeemFinalized AS finalized
877
+ ON finalized.address = requested.address
878
+ AND finalized."requestId" = requested."requestId"
879
+ AND finalized.block_num <= ${options.blockNumber}
880
+ LEFT ANTI JOIN RedeemCancelled AS cancelled
881
+ ON cancelled.address = requested.address
882
+ AND cancelled."requestId" = requested."requestId"
883
+ AND cancelled.block_num <= ${options.blockNumber}
884
+ WHERE requested.address = '${options.vault}'
885
+ AND requested.requester = '${options.account}'
886
+ AND requested.block_num <= ${options.blockNumber}
887
+ ) AS pending_redemptions,
888
+ (
889
+ SELECT toString(sum(toUInt256(assets)))
890
+ FROM Redeemed
891
+ WHERE address = '${options.vault}'
892
+ AND caller = '${options.account}'
893
+ AND block_num > ${options.startBlockNumber}
894
+ AND block_num <= ${options.blockNumber}
895
+ ) AS redeemed_assets,
896
+ (
897
+ SELECT count()
898
+ FROM RedeemCancelled AS cancelled
899
+ INNER JOIN RedeemRequested AS requested
900
+ ON requested.address = cancelled.address
901
+ AND requested."requestId" = cancelled."requestId"
902
+ WHERE cancelled.address = '${options.vault}'
903
+ AND cancelled.block_num > ${options.startBlockNumber}
904
+ AND cancelled.block_num <= ${options.blockNumber}
905
+ AND requested.block_num <= ${options.blockNumber}
906
+ AND requested.requester != cancelled.receiver
907
+ AND (
908
+ requested.requester = '${options.account}'
909
+ OR cancelled.receiver = '${options.account}'
910
+ )
911
+ ) AS transferred_cancellations,
912
+ (
913
+ SELECT count()
914
+ FROM VenueSharesDeposited
915
+ WHERE address = '${options.vault}'
916
+ AND receiver = '${options.account}'
917
+ AND block_num > ${options.startBlockNumber}
918
+ AND block_num <= ${options.blockNumber}
919
+ ) AS venue_deposits,
920
+ (
921
+ SELECT toString(sum(toUInt256(assets)))
922
+ FROM WithdrewExact
923
+ WHERE address = '${options.vault}'
924
+ AND caller = '${options.account}'
925
+ AND block_num > ${options.startBlockNumber}
926
+ AND block_num <= ${options.blockNumber}
927
+ ) AS withdrew_exact
928
+ `,
929
+ signatures: [
930
+ depositedSignature,
931
+ redeemCancelledSignature,
932
+ redeemFinalizedSignature,
933
+ redeemRequestedSignature,
934
+ redeemedSignature,
935
+ venueSharesDepositedSignature,
936
+ withdrewExactSignature,
937
+ ],
938
+ });
939
+ const row = result.rows[0];
940
+ const deposited = Value.toIntegerString(row?.['deposited_assets']);
941
+ const finalized = Value.toIntegerString(row?.['finalized_assets']);
942
+ const pendingRedemptions = Value.toNumber(row?.['pending_redemptions']);
943
+ const redeemed = Value.toIntegerString(row?.['redeemed_assets']);
944
+ const transferredCancellations = Value.toNumber(row?.['transferred_cancellations']);
945
+ const venueDeposits = Value.toNumber(row?.['venue_deposits']);
946
+ const withdrewExact = Value.toIntegerString(row?.['withdrew_exact']);
947
+ if (deposited === undefined ||
948
+ finalized === undefined ||
949
+ pendingRedemptions === undefined ||
950
+ redeemed === undefined ||
951
+ transferredCancellations === undefined ||
952
+ venueDeposits === undefined ||
953
+ withdrewExact === undefined)
954
+ throw new Error('TIDX returned invalid Earn earnings history.');
955
+ return {
956
+ deposited: BigInt(deposited),
957
+ finalized: BigInt(finalized),
958
+ incompleteCostBasis: transferredCancellations > 0 || venueDeposits > 0,
959
+ pendingRedemption: pendingRedemptions > 0,
960
+ redeemed: BigInt(redeemed),
961
+ withdrewExact: BigInt(withdrewExact),
962
+ };
963
+ }
964
+ /** Reads share balances and cross-boundary redemptions for a trailing window. */
965
+ async function getWindowShareState(tidx, options) {
966
+ const result = await tidx.fetch({
967
+ engine: 'clickhouse',
968
+ query: `
969
+ SELECT
970
+ (
971
+ SELECT toString(sum(toUInt256(amount)))
972
+ FROM token_transfers
973
+ WHERE token = '${options.shareToken.toLowerCase()}'
974
+ AND "to" = '${options.account}'
975
+ AND block_num <= ${options.startBlockNumber}
976
+ ) AS received_start,
977
+ (
978
+ SELECT toString(sum(toUInt256(amount)))
979
+ FROM token_transfers
980
+ WHERE token = '${options.shareToken.toLowerCase()}'
981
+ AND "from" = '${options.account}'
982
+ AND block_num <= ${options.startBlockNumber}
983
+ ) AS sent_start,
984
+ (
985
+ SELECT toString(sum(toUInt256(amount)))
986
+ FROM token_transfers
987
+ WHERE token = '${options.shareToken.toLowerCase()}'
988
+ AND "to" = '${options.account}'
989
+ AND block_num <= ${options.blockNumber}
990
+ ) AS received_end,
991
+ (
992
+ SELECT toString(sum(toUInt256(amount)))
993
+ FROM token_transfers
994
+ WHERE token = '${options.shareToken.toLowerCase()}'
995
+ AND "from" = '${options.account}'
996
+ AND block_num <= ${options.blockNumber}
997
+ ) AS sent_end,
998
+ (
999
+ SELECT count()
1000
+ FROM (
1001
+ SELECT finalized."requestId"
1002
+ FROM RedeemFinalized AS finalized
1003
+ INNER JOIN RedeemRequested AS requested
1004
+ ON requested.address = finalized.address
1005
+ AND requested."requestId" = finalized."requestId"
1006
+ WHERE finalized.address = '${options.vault}'
1007
+ AND requested.requester = '${options.account}'
1008
+ AND requested.block_num <= ${options.startBlockNumber}
1009
+ AND finalized.block_num > ${options.startBlockNumber}
1010
+ AND finalized.block_num <= ${options.blockNumber}
1011
+ UNION ALL
1012
+ SELECT cancelled."requestId"
1013
+ FROM RedeemCancelled AS cancelled
1014
+ INNER JOIN RedeemRequested AS requested
1015
+ ON requested.address = cancelled.address
1016
+ AND requested."requestId" = cancelled."requestId"
1017
+ WHERE cancelled.address = '${options.vault}'
1018
+ AND requested.requester = '${options.account}'
1019
+ AND requested.block_num <= ${options.startBlockNumber}
1020
+ AND cancelled.block_num > ${options.startBlockNumber}
1021
+ AND cancelled.block_num <= ${options.blockNumber}
1022
+ ) AS cross_boundary
1023
+ ) AS cross_boundary_redemptions
1024
+ `,
1025
+ signatures: [redeemCancelledSignature, redeemFinalizedSignature, redeemRequestedSignature],
1026
+ });
1027
+ const row = result.rows[0];
1028
+ const crossBoundaryRedemptions = Value.toNumber(row?.['cross_boundary_redemptions']);
1029
+ const receivedEnd = Value.toIntegerString(row?.['received_end']);
1030
+ const receivedStart = Value.toIntegerString(row?.['received_start']);
1031
+ const sentEnd = Value.toIntegerString(row?.['sent_end']);
1032
+ const sentStart = Value.toIntegerString(row?.['sent_start']);
1033
+ if (crossBoundaryRedemptions === undefined ||
1034
+ receivedEnd === undefined ||
1035
+ receivedStart === undefined ||
1036
+ sentEnd === undefined ||
1037
+ sentStart === undefined)
1038
+ return undefined;
1039
+ const receivedEndValue = BigInt(receivedEnd);
1040
+ const receivedStartValue = BigInt(receivedStart);
1041
+ const sentEndValue = BigInt(sentEnd);
1042
+ const sentStartValue = BigInt(sentStart);
1043
+ if (sentEndValue > receivedEndValue || sentStartValue > receivedStartValue)
1044
+ return undefined;
1045
+ return {
1046
+ crossBoundaryRedemption: crossBoundaryRedemptions > 0,
1047
+ endShares: receivedEndValue - sentEndValue,
1048
+ startShares: receivedStartValue - sentStartValue,
1049
+ };
1050
+ }
1051
+ /** Returns whether an account sent or received Earn shares outside known vault flows. */
1052
+ async function hasExternalShareTransfer(tidx, options) {
1053
+ // Vault exits pull shares into the vault before burning. Reconcile counts by transaction and amount so unmatched direct sends remain external.
1054
+ const result = await tidx.fetch({
1055
+ engine: 'clickhouse',
1056
+ query: `
1057
+ SELECT
1058
+ (
1059
+ SELECT count()
1060
+ FROM token_transfers
1061
+ WHERE token = '${options.shareToken.toLowerCase()}'
1062
+ AND block_num > ${options.startBlockNumber}
1063
+ AND block_num <= ${options.blockNumber}
1064
+ AND (
1065
+ ("from" = '${options.account}' AND "to" != '${options.vault}')
1066
+ OR (
1067
+ "to" = '${options.account}'
1068
+ AND "from" != '${zeroAddress}'
1069
+ AND "from" != '${options.vault}'
1070
+ )
1071
+ )
1072
+ ) + (
1073
+ SELECT count()
1074
+ FROM (
1075
+ SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
1076
+ FROM token_transfers
1077
+ WHERE token = '${options.shareToken.toLowerCase()}'
1078
+ AND "from" = '${options.account}'
1079
+ AND "to" = '${options.vault}'
1080
+ AND block_num > ${options.startBlockNumber}
1081
+ AND block_num <= ${options.blockNumber}
1082
+ GROUP BY tx_hash, amount
1083
+ ) AS candidate
1084
+ LEFT JOIN (
1085
+ SELECT tx_hash, amount, count() AS exit_count
1086
+ FROM (
1087
+ SELECT tx_hash, toUInt256(earnShares) AS amount
1088
+ FROM Redeemed
1089
+ WHERE address = '${options.vault}'
1090
+ AND caller = '${options.account}'
1091
+ AND block_num > ${options.startBlockNumber}
1092
+ AND block_num <= ${options.blockNumber}
1093
+ UNION ALL
1094
+ SELECT tx_hash, toUInt256(earnShares) AS amount
1095
+ FROM RedeemRequested
1096
+ WHERE address = '${options.vault}'
1097
+ AND requester = '${options.account}'
1098
+ AND block_num > ${options.startBlockNumber}
1099
+ AND block_num <= ${options.blockNumber}
1100
+ UNION ALL
1101
+ SELECT tx_hash, toUInt256(earnSharesBurned) AS amount
1102
+ FROM WithdrewExact
1103
+ WHERE address = '${options.vault}'
1104
+ AND caller = '${options.account}'
1105
+ AND block_num > ${options.startBlockNumber}
1106
+ AND block_num <= ${options.blockNumber}
1107
+ ) AS protocol_exit
1108
+ GROUP BY tx_hash, amount
1109
+ ) AS matched
1110
+ ON matched.tx_hash = candidate.tx_hash AND matched.amount = candidate.amount
1111
+ WHERE candidate.transfer_count > coalesce(matched.exit_count, 0)
1112
+ ) + (
1113
+ SELECT count()
1114
+ FROM (
1115
+ SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
1116
+ FROM token_transfers
1117
+ WHERE token = '${options.shareToken.toLowerCase()}'
1118
+ AND "from" = '${options.vault}'
1119
+ AND "to" = '${options.account}'
1120
+ AND block_num > ${options.startBlockNumber}
1121
+ AND block_num <= ${options.blockNumber}
1122
+ GROUP BY tx_hash, amount
1123
+ ) AS candidate
1124
+ LEFT JOIN (
1125
+ SELECT tx_hash, toUInt256(earnShares) AS amount, count() AS cancel_count
1126
+ FROM RedeemCancelled
1127
+ WHERE address = '${options.vault}'
1128
+ AND receiver = '${options.account}'
1129
+ AND block_num > ${options.startBlockNumber}
1130
+ AND block_num <= ${options.blockNumber}
1131
+ GROUP BY tx_hash, amount
1132
+ ) AS matched
1133
+ ON matched.tx_hash = candidate.tx_hash AND matched.amount = candidate.amount
1134
+ WHERE candidate.transfer_count > coalesce(matched.cancel_count, 0)
1135
+ ) AS count
1136
+ `,
1137
+ signatures: [
1138
+ redeemCancelledSignature,
1139
+ redeemedSignature,
1140
+ redeemRequestedSignature,
1141
+ withdrewExactSignature,
1142
+ ],
1143
+ });
1144
+ const count = Value.toNumber(result.rows[0]?.['count']);
1145
+ if (count === undefined)
1146
+ throw new Error('TIDX returned an invalid Earn-share transfer count.');
1147
+ return count > 0;
1148
+ }
1149
+ /** Calculates the asset cost basis attached to an account's remaining Earn shares. */
1150
+ export function calculateActiveCostBasis(options) {
1151
+ const requests = new Map();
1152
+ let costBasis = 0n;
1153
+ let shares = 0n;
1154
+ const remove = (burned) => {
1155
+ if (burned > shares || shares === 0n)
1156
+ return undefined;
1157
+ const removed = (() => {
1158
+ if (costBasis === undefined)
1159
+ return undefined;
1160
+ return burned === shares ? costBasis : (costBasis * burned) / shares;
1161
+ })();
1162
+ if (burned === shares)
1163
+ costBasis = 0n;
1164
+ else if (costBasis !== undefined && removed !== undefined)
1165
+ costBasis -= removed;
1166
+ shares -= burned;
1167
+ return { costBasis: removed };
1168
+ };
1169
+ for (const action of options.actions) {
1170
+ if (action.kind === 'deposit') {
1171
+ if (costBasis !== undefined)
1172
+ costBasis += action.assets;
1173
+ shares += action.shares;
1174
+ continue;
1175
+ }
1176
+ if (action.kind === 'unknown') {
1177
+ costBasis = undefined;
1178
+ shares += action.shares;
1179
+ continue;
1180
+ }
1181
+ if (action.kind === 'cancel') {
1182
+ const restored = requests.get(action.requestId);
1183
+ if (restored === undefined || restored.shares !== action.shares)
1184
+ return undefined;
1185
+ if (costBasis === undefined || restored.costBasis === undefined)
1186
+ costBasis = undefined;
1187
+ else
1188
+ costBasis += restored.costBasis;
1189
+ shares += restored.shares;
1190
+ requests.delete(action.requestId);
1191
+ continue;
1192
+ }
1193
+ const removed = remove(action.shares);
1194
+ if (removed === undefined)
1195
+ return undefined;
1196
+ if (action.kind === 'request')
1197
+ requests.set(action.requestId, { costBasis: removed.costBasis, shares: action.shares });
1198
+ }
1199
+ return shares === options.shareBalance && costBasis !== undefined ? costBasis : undefined;
1200
+ }
1201
+ /** Reads the active asset cost basis for shares held at one indexed block. */
1202
+ async function getActiveCostBasis(tidx, options) {
1203
+ const result = await tidx.fetch({
1204
+ engine: 'clickhouse',
1205
+ query: `
1206
+ SELECT kind, request_id, shares, assets
1207
+ FROM (
1208
+ SELECT
1209
+ 'deposit' AS kind,
1210
+ block_num,
1211
+ log_idx,
1212
+ '' AS request_id,
1213
+ toString(toUInt256(earnShares)) AS shares,
1214
+ toString(toUInt256(assets)) AS assets
1215
+ FROM Deposited
1216
+ WHERE address = '${options.vault}'
1217
+ AND receiver = '${options.account}'
1218
+ AND block_num <= ${options.blockNumber}
1219
+ UNION ALL
1220
+ SELECT
1221
+ 'redeem' AS kind,
1222
+ block_num,
1223
+ log_idx,
1224
+ '' AS request_id,
1225
+ toString(toUInt256(earnShares)) AS shares,
1226
+ '0' AS assets
1227
+ FROM Redeemed
1228
+ WHERE address = '${options.vault}'
1229
+ AND caller = '${options.account}'
1230
+ AND block_num <= ${options.blockNumber}
1231
+ UNION ALL
1232
+ SELECT
1233
+ 'redeem' AS kind,
1234
+ block_num,
1235
+ log_idx,
1236
+ '' AS request_id,
1237
+ toString(toUInt256(earnSharesBurned)) AS shares,
1238
+ '0' AS assets
1239
+ FROM WithdrewExact
1240
+ WHERE address = '${options.vault}'
1241
+ AND caller = '${options.account}'
1242
+ AND block_num <= ${options.blockNumber}
1243
+ UNION ALL
1244
+ SELECT
1245
+ 'request' AS kind,
1246
+ block_num,
1247
+ log_idx,
1248
+ "requestId" AS request_id,
1249
+ toString(toUInt256(earnShares)) AS shares,
1250
+ '0' AS assets
1251
+ FROM RedeemRequested
1252
+ WHERE address = '${options.vault}'
1253
+ AND requester = '${options.account}'
1254
+ AND block_num <= ${options.blockNumber}
1255
+ UNION ALL
1256
+ SELECT
1257
+ 'cancel' AS kind,
1258
+ cancelled.block_num AS block_num,
1259
+ cancelled.log_idx AS log_idx,
1260
+ cancelled."requestId" AS request_id,
1261
+ toString(toUInt256(cancelled.earnShares)) AS shares,
1262
+ '0' AS assets
1263
+ FROM RedeemCancelled AS cancelled
1264
+ INNER JOIN RedeemRequested AS requested
1265
+ ON requested.address = cancelled.address
1266
+ AND requested."requestId" = cancelled."requestId"
1267
+ WHERE cancelled.address = '${options.vault}'
1268
+ AND requested.requester = '${options.account}'
1269
+ AND cancelled.receiver = '${options.account}'
1270
+ AND cancelled.block_num <= ${options.blockNumber}
1271
+ AND requested.block_num <= ${options.blockNumber}
1272
+ UNION ALL
1273
+ SELECT
1274
+ 'unknown' AS kind,
1275
+ block_num,
1276
+ log_idx,
1277
+ '' AS request_id,
1278
+ toString(toUInt256(earnShares)) AS shares,
1279
+ '0' AS assets
1280
+ FROM VenueSharesDeposited
1281
+ WHERE address = '${options.vault}'
1282
+ AND receiver = '${options.account}'
1283
+ AND block_num <= ${options.blockNumber}
1284
+ UNION ALL
1285
+ SELECT
1286
+ 'unknown' AS kind,
1287
+ block_num,
1288
+ log_idx,
1289
+ '' AS request_id,
1290
+ toString(toUInt256(amount)) AS shares,
1291
+ '0' AS assets
1292
+ FROM token_transfers
1293
+ WHERE token = '${options.shareToken.toLowerCase()}'
1294
+ AND "to" = '${options.account}'
1295
+ AND "from" != '${zeroAddress}'
1296
+ AND "from" != '${options.vault}'
1297
+ AND block_num <= ${options.blockNumber}
1298
+ UNION ALL
1299
+ SELECT
1300
+ 'redeem' AS kind,
1301
+ block_num,
1302
+ log_idx,
1303
+ '' AS request_id,
1304
+ toString(toUInt256(amount)) AS shares,
1305
+ '0' AS assets
1306
+ FROM token_transfers
1307
+ WHERE token = '${options.shareToken.toLowerCase()}'
1308
+ AND "from" = '${options.account}'
1309
+ AND "to" != '${options.vault}'
1310
+ AND block_num <= ${options.blockNumber}
1311
+ ) AS actions
1312
+ ORDER BY block_num, log_idx
1313
+ `,
1314
+ signatures: [
1315
+ depositedSignature,
1316
+ redeemCancelledSignature,
1317
+ redeemRequestedSignature,
1318
+ redeemedSignature,
1319
+ venueSharesDepositedSignature,
1320
+ withdrewExactSignature,
1321
+ ],
1322
+ });
1323
+ const actions = [];
1324
+ for (const row of result.rows) {
1325
+ const kind = Value.toText(row['kind']);
1326
+ if (kind === 'deposit') {
1327
+ const assets = Value.toIntegerString(row['assets']);
1328
+ const shares = Value.toIntegerString(row['shares']);
1329
+ if (assets === undefined || shares === undefined)
1330
+ return undefined;
1331
+ actions.push({ assets: BigInt(assets), kind, shares: BigInt(shares) });
1332
+ continue;
1333
+ }
1334
+ if (kind === 'redeem') {
1335
+ const shares = Value.toIntegerString(row['shares']);
1336
+ if (shares === undefined)
1337
+ return undefined;
1338
+ actions.push({ kind, shares: BigInt(shares) });
1339
+ continue;
1340
+ }
1341
+ if (kind === 'cancel') {
1342
+ const requestId = Value.toText(row['request_id']);
1343
+ const shares = Value.toIntegerString(row['shares']);
1344
+ if (requestId === undefined || shares === undefined)
1345
+ return undefined;
1346
+ actions.push({ kind, requestId, shares: BigInt(shares) });
1347
+ continue;
1348
+ }
1349
+ if (kind === 'request') {
1350
+ const requestId = Value.toText(row['request_id']);
1351
+ const shares = Value.toIntegerString(row['shares']);
1352
+ if (requestId === undefined || shares === undefined)
1353
+ return undefined;
1354
+ actions.push({ kind, requestId, shares: BigInt(shares) });
1355
+ continue;
1356
+ }
1357
+ if (kind === 'unknown') {
1358
+ const shares = Value.toIntegerString(row['shares']);
1359
+ if (shares === undefined)
1360
+ return undefined;
1361
+ actions.push({ kind, shares: BigInt(shares) });
1362
+ continue;
1363
+ }
1364
+ return undefined;
1365
+ }
1366
+ return calculateActiveCostBasis({ actions, shareBalance: options.shareBalance });
1367
+ }
496
1368
  /** Lists compatible deployed Earn vaults. */
497
1369
  export async function listVaults(c, options) {
498
1370
  const db = Db.get(c.get('dbCached'));