tapimo 0.7.0 → 0.7.2

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 (689) hide show
  1. package/dist/ApiKeys.d.ts +24 -7
  2. package/dist/ApiKeys.d.ts.map +1 -1
  3. package/dist/ApiKeys.js +17 -5
  4. package/dist/ApiKeys.js.map +1 -1
  5. package/dist/App.d.ts +12104 -7861
  6. package/dist/App.d.ts.map +1 -1
  7. package/dist/App.js +191 -128
  8. package/dist/App.js.map +1 -1
  9. package/dist/Assets.d.ts +10 -0
  10. package/dist/Assets.d.ts.map +1 -1
  11. package/dist/Assets.js +17 -4
  12. package/dist/Assets.js.map +1 -1
  13. package/dist/Scope.d.ts +19 -8
  14. package/dist/Scope.d.ts.map +1 -1
  15. package/dist/Scope.js +25 -9
  16. package/dist/Scope.js.map +1 -1
  17. package/dist/Webhooks.d.ts +1 -1
  18. package/dist/admin/App.d.ts +601 -20
  19. package/dist/admin/App.d.ts.map +1 -1
  20. package/dist/admin/App.js +12 -2
  21. package/dist/admin/App.js.map +1 -1
  22. package/dist/admin/apps/api-keys.d.ts +94 -11
  23. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.js +141 -32
  25. package/dist/admin/apps/api-keys.js.map +1 -1
  26. package/dist/admin/apps/earn-vaults.d.ts +1069 -0
  27. package/dist/admin/apps/earn-vaults.d.ts.map +1 -0
  28. package/dist/admin/apps/earn-vaults.js +409 -0
  29. package/dist/admin/apps/earn-vaults.js.map +1 -0
  30. package/dist/admin/apps/verified-tokens.d.ts +166 -166
  31. package/dist/admin/ui.generated.d.ts +1 -1
  32. package/dist/admin/ui.generated.d.ts.map +1 -1
  33. package/dist/admin/ui.generated.js +151 -95
  34. package/dist/admin/ui.generated.js.map +1 -1
  35. package/dist/analytics/Analytics.d.ts +2 -0
  36. package/dist/analytics/Analytics.d.ts.map +1 -1
  37. package/dist/analytics/Analytics.js +18 -2
  38. package/dist/analytics/Analytics.js.map +1 -1
  39. package/dist/analytics/tables/requestEvents.d.ts +22 -1
  40. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  41. package/dist/analytics/tables/requestEvents.js +59 -39
  42. package/dist/analytics/tables/requestEvents.js.map +1 -1
  43. package/dist/apps/Zones.d.ts +8335 -0
  44. package/dist/apps/Zones.d.ts.map +1 -0
  45. package/dist/apps/Zones.js +38 -0
  46. package/dist/apps/Zones.js.map +1 -0
  47. package/dist/apps/data/App.d.ts +14377 -10165
  48. package/dist/apps/data/App.d.ts.map +1 -1
  49. package/dist/apps/data/App.js +91 -19
  50. package/dist/apps/data/App.js.map +1 -1
  51. package/dist/apps/data/FxOracle.d.ts +1 -1
  52. package/dist/apps/data/FxOracle.d.ts.map +1 -1
  53. package/dist/apps/data/index.d.ts +0 -1
  54. package/dist/apps/data/index.d.ts.map +1 -1
  55. package/dist/apps/data/index.js +0 -1
  56. package/dist/apps/data/index.js.map +1 -1
  57. package/dist/apps/data/routes/activities.d.ts +32 -32
  58. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  59. package/dist/apps/data/routes/activities.js +130 -65
  60. package/dist/apps/data/routes/activities.js.map +1 -1
  61. package/dist/apps/data/routes/addresses.d.ts +312 -312
  62. package/dist/apps/data/routes/balances.d.ts +18 -18
  63. package/dist/apps/data/routes/balances.js +5 -5
  64. package/dist/apps/data/routes/balances.js.map +1 -1
  65. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  66. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  67. package/dist/apps/data/routes/coingecko.js +62 -35
  68. package/dist/apps/data/routes/coingecko.js.map +1 -1
  69. package/dist/apps/data/routes/earn.d.ts +2144 -0
  70. package/dist/apps/data/routes/earn.d.ts.map +1 -0
  71. package/dist/apps/data/routes/earn.js +947 -0
  72. package/dist/apps/data/routes/earn.js.map +1 -0
  73. package/dist/apps/data/routes/exchanges.d.ts +1231 -324
  74. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  75. package/dist/apps/data/routes/exchanges.js +656 -193
  76. package/dist/apps/data/routes/exchanges.js.map +1 -1
  77. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  78. package/dist/apps/data/routes/fee-amm.js +59 -8
  79. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  80. package/dist/apps/data/routes/funding.d.ts +4092 -387
  81. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  82. package/dist/apps/data/routes/funding.js +1403 -256
  83. package/dist/apps/data/routes/funding.js.map +1 -1
  84. package/dist/apps/data/routes/indexer.d.ts +5 -5
  85. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  86. package/dist/apps/data/routes/indexer.js +19 -2
  87. package/dist/apps/data/routes/indexer.js.map +1 -1
  88. package/dist/apps/data/routes/receipts.d.ts +265 -265
  89. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  90. package/dist/apps/data/routes/receipts.js +11 -11
  91. package/dist/apps/data/routes/receipts.js.map +1 -1
  92. package/dist/apps/data/routes/rpc.d.ts +5 -5
  93. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  94. package/dist/apps/data/routes/rpc.js +211 -22
  95. package/dist/apps/data/routes/rpc.js.map +1 -1
  96. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  97. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  98. package/dist/apps/data/routes/tokenlist.js +49 -9
  99. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  100. package/dist/apps/data/routes/tokens.d.ts +116 -88
  101. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  102. package/dist/apps/data/routes/tokens.js +198 -91
  103. package/dist/apps/data/routes/tokens.js.map +1 -1
  104. package/dist/apps/data/routes/transactions.d.ts +55 -55
  105. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  106. package/dist/apps/data/routes/transactions.js +78 -39
  107. package/dist/apps/data/routes/transactions.js.map +1 -1
  108. package/dist/apps/data/routes/transfers.d.ts +78 -29
  109. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  110. package/dist/apps/data/routes/transfers.js +165 -67
  111. package/dist/apps/data/routes/transfers.js.map +1 -1
  112. package/dist/apps/data/routes/valuation.d.ts +10 -10
  113. package/dist/apps/data/routes/valuation.js +2 -2
  114. package/dist/apps/data/routes/valuation.js.map +1 -1
  115. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  116. package/dist/apps/data/routes/verified-tokens.js +34 -9
  117. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  118. package/dist/apps/data/routes/webhooks.d.ts +5 -3
  119. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  120. package/dist/apps/data/routes/webhooks.js +175 -65
  121. package/dist/apps/data/routes/webhooks.js.map +1 -1
  122. package/dist/apps/data/routes/zones.d.ts +281 -0
  123. package/dist/apps/data/routes/zones.d.ts.map +1 -0
  124. package/dist/apps/data/routes/zones.js +252 -0
  125. package/dist/apps/data/routes/zones.js.map +1 -0
  126. package/dist/apps/docs/reference.js +6 -5
  127. package/dist/apps/docs/reference.js.map +1 -1
  128. package/dist/apps/index.d.ts +2 -1
  129. package/dist/apps/index.d.ts.map +1 -1
  130. package/dist/apps/index.js +2 -1
  131. package/dist/apps/index.js.map +1 -1
  132. package/dist/apps/management/App.d.ts +670 -670
  133. package/dist/apps/management/Billing.d.ts +8 -0
  134. package/dist/apps/management/Billing.d.ts.map +1 -1
  135. package/dist/apps/management/Billing.js +12 -0
  136. package/dist/apps/management/Billing.js.map +1 -1
  137. package/dist/apps/management/Error.d.ts +12 -0
  138. package/dist/apps/management/Error.d.ts.map +1 -0
  139. package/dist/apps/management/Error.js +10 -0
  140. package/dist/apps/management/Error.js.map +1 -0
  141. package/dist/apps/management/routes/api-keys.d.ts +5 -5
  142. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  143. package/dist/apps/management/routes/api-keys.js +2 -8
  144. package/dist/apps/management/routes/api-keys.js.map +1 -1
  145. package/dist/apps/management/routes/billing.d.ts +119 -119
  146. package/dist/apps/management/routes/faucet.d.ts +35 -35
  147. package/dist/apps/management/routes/invitations.d.ts +62 -62
  148. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  149. package/dist/apps/management/routes/invitations.js +4 -10
  150. package/dist/apps/management/routes/invitations.js.map +1 -1
  151. package/dist/apps/management/routes/invite-links.d.ts +30 -30
  152. package/dist/apps/management/routes/me.d.ts +6 -6
  153. package/dist/apps/management/routes/members.d.ts +78 -78
  154. package/dist/apps/management/routes/orgs.d.ts +48 -48
  155. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  156. package/dist/apps/management/routes/orgs.js +2 -8
  157. package/dist/apps/management/routes/orgs.js.map +1 -1
  158. package/dist/apps/management/routes/projects.d.ts +9 -9
  159. package/dist/apps/management/routes/scopes.js +2 -2
  160. package/dist/apps/management/routes/scopes.js.map +1 -1
  161. package/dist/apps/management/routes/usage.d.ts +80 -80
  162. package/dist/apps/management/routes/verified-token-requests.d.ts +36 -36
  163. package/dist/apps/management/routes/webhooks.d.ts +50 -50
  164. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  165. package/dist/apps/management/routes/webhooks.js +15 -3
  166. package/dist/apps/management/routes/webhooks.js.map +1 -1
  167. package/dist/apps/mpp/App.d.ts +55 -56
  168. package/dist/apps/mpp/App.d.ts.map +1 -1
  169. package/dist/apps/mpp/App.js +192 -27
  170. package/dist/apps/mpp/App.js.map +1 -1
  171. package/dist/apps/mpp/Mpp.d.ts +0 -20
  172. package/dist/apps/mpp/Mpp.d.ts.map +1 -1
  173. package/dist/apps/mpp/Mpp.js +0 -24
  174. package/dist/apps/mpp/Mpp.js.map +1 -1
  175. package/dist/apps/mpp/internal/Adapter.d.ts +40 -0
  176. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -0
  177. package/dist/apps/mpp/internal/Adapter.js +66 -0
  178. package/dist/apps/mpp/internal/Adapter.js.map +1 -0
  179. package/dist/apps/mpp/internal/Error.d.ts +4 -0
  180. package/dist/apps/mpp/internal/Error.d.ts.map +1 -0
  181. package/dist/apps/mpp/internal/Error.js +41 -0
  182. package/dist/apps/mpp/internal/Error.js.map +1 -0
  183. package/dist/apps/mpp/internal/Idempotency.d.ts +33 -0
  184. package/dist/apps/mpp/internal/Idempotency.d.ts.map +1 -0
  185. package/dist/apps/mpp/internal/Idempotency.js +28 -0
  186. package/dist/apps/mpp/internal/Idempotency.js.map +1 -0
  187. package/dist/apps/mpp/internal/Policy.d.ts +46 -0
  188. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -0
  189. package/dist/apps/mpp/internal/Policy.js +57 -0
  190. package/dist/apps/mpp/internal/Policy.js.map +1 -0
  191. package/dist/apps/mpp/internal/ScreenManager.d.ts +27 -0
  192. package/dist/apps/mpp/internal/ScreenManager.d.ts.map +1 -0
  193. package/dist/apps/mpp/internal/ScreenManager.js +18 -0
  194. package/dist/apps/mpp/internal/ScreenManager.js.map +1 -0
  195. package/dist/apps/mpp/internal/Screening.d.ts +35 -0
  196. package/dist/apps/mpp/internal/Screening.d.ts.map +1 -0
  197. package/dist/apps/mpp/internal/Screening.js +20 -0
  198. package/dist/apps/mpp/internal/Screening.js.map +1 -0
  199. package/dist/apps/mpp/internal/Sponsorship.d.ts +36 -0
  200. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -0
  201. package/dist/apps/mpp/internal/Sponsorship.js +103 -0
  202. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -0
  203. package/dist/apps/mpp/internal/Utils.d.ts +7 -0
  204. package/dist/apps/mpp/internal/Utils.d.ts.map +1 -0
  205. package/dist/apps/mpp/internal/Utils.js +24 -0
  206. package/dist/apps/mpp/internal/Utils.js.map +1 -0
  207. package/dist/apps/relay/App.js +184 -24
  208. package/dist/apps/relay/App.js.map +1 -1
  209. package/dist/apps/relay/Sponsorships.d.ts +4 -0
  210. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  211. package/dist/apps/relay/Sponsorships.js +16 -7
  212. package/dist/apps/relay/Sponsorships.js.map +1 -1
  213. package/dist/db/Db.d.ts +20 -0
  214. package/dist/db/Db.d.ts.map +1 -1
  215. package/dist/db/Db.js +342 -0
  216. package/dist/db/Db.js.map +1 -1
  217. package/dist/db/Schema.d.ts +921 -1
  218. package/dist/db/Schema.d.ts.map +1 -1
  219. package/dist/db/Schema.js +269 -1
  220. package/dist/db/Schema.js.map +1 -1
  221. package/dist/db/tables/earnVaults.d.ts +68 -0
  222. package/dist/db/tables/earnVaults.d.ts.map +1 -0
  223. package/dist/db/tables/earnVaults.js +72 -0
  224. package/dist/db/tables/earnVaults.js.map +1 -0
  225. package/dist/db/tables/fundingCatalog.d.ts +56 -0
  226. package/dist/db/tables/fundingCatalog.d.ts.map +1 -0
  227. package/dist/db/tables/fundingCatalog.js +85 -0
  228. package/dist/db/tables/fundingCatalog.js.map +1 -0
  229. package/dist/db/tables/fundingDepositAddresses.d.ts +159 -0
  230. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -0
  231. package/dist/db/tables/fundingDepositAddresses.js +206 -0
  232. package/dist/db/tables/fundingDepositAddresses.js.map +1 -0
  233. package/dist/db/tables/fundingDeposits.d.ts +115 -0
  234. package/dist/db/tables/fundingDeposits.d.ts.map +1 -0
  235. package/dist/db/tables/fundingDeposits.js +100 -0
  236. package/dist/db/tables/fundingDeposits.js.map +1 -0
  237. package/dist/db/tables/fundingIdempotency.d.ts +91 -0
  238. package/dist/db/tables/fundingIdempotency.d.ts.map +1 -0
  239. package/dist/db/tables/fundingIdempotency.js +103 -0
  240. package/dist/db/tables/fundingIdempotency.js.map +1 -0
  241. package/dist/db/tables/fundingTransferEvents.d.ts +12 -0
  242. package/dist/db/tables/fundingTransferEvents.d.ts.map +1 -0
  243. package/dist/db/tables/fundingTransferEvents.js +18 -0
  244. package/dist/db/tables/fundingTransferEvents.js.map +1 -0
  245. package/dist/db/tables/fundingTransferTransactions.d.ts +15 -0
  246. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -0
  247. package/dist/db/tables/fundingTransferTransactions.js +27 -0
  248. package/dist/db/tables/fundingTransferTransactions.js.map +1 -0
  249. package/dist/db/tables/fundingTransfers.d.ts +82 -0
  250. package/dist/db/tables/fundingTransfers.d.ts.map +1 -0
  251. package/dist/db/tables/fundingTransfers.js +89 -0
  252. package/dist/db/tables/fundingTransfers.js.map +1 -0
  253. package/dist/db/tables/organizations.d.ts +8 -0
  254. package/dist/db/tables/organizations.d.ts.map +1 -1
  255. package/dist/db/tables/organizations.js +35 -0
  256. package/dist/db/tables/organizations.js.map +1 -1
  257. package/dist/db/tables/projects.d.ts +8 -0
  258. package/dist/db/tables/projects.d.ts.map +1 -1
  259. package/dist/db/tables/projects.js +16 -0
  260. package/dist/db/tables/projects.js.map +1 -1
  261. package/dist/db/tables/requestUsage.d.ts +13 -0
  262. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  263. package/dist/db/tables/requestUsage.js +16 -0
  264. package/dist/db/tables/requestUsage.js.map +1 -1
  265. package/dist/db/tables/sponsoredTransactions.d.ts +4 -2
  266. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  267. package/dist/db/tables/sponsoredTransactions.js +14 -4
  268. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  269. package/dist/db/tables/users.d.ts +8 -0
  270. package/dist/db/tables/users.d.ts.map +1 -1
  271. package/dist/db/tables/users.js +16 -0
  272. package/dist/db/tables/users.js.map +1 -1
  273. package/dist/db/tables/verifiedTokens.d.ts +14 -7
  274. package/dist/db/tables/verifiedTokens.d.ts.map +1 -1
  275. package/dist/db/tables/verifiedTokens.js +9 -7
  276. package/dist/db/tables/verifiedTokens.js.map +1 -1
  277. package/dist/db/tables/webhookSubscriptions.d.ts +31 -1
  278. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  279. package/dist/db/tables/webhookSubscriptions.js +24 -3
  280. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  281. package/dist/handlers/relay.js +108 -108
  282. package/dist/handlers/relay.js.map +1 -1
  283. package/dist/internal/Auth.d.ts +26 -0
  284. package/dist/internal/Auth.d.ts.map +1 -1
  285. package/dist/internal/Auth.js +35 -2
  286. package/dist/internal/Auth.js.map +1 -1
  287. package/dist/internal/Cache.d.ts.map +1 -1
  288. package/dist/internal/Cache.js +16 -10
  289. package/dist/internal/Cache.js.map +1 -1
  290. package/dist/internal/Cursor.js +1 -1
  291. package/dist/internal/Cursor.js.map +1 -1
  292. package/dist/internal/EarnRates.d.ts +89 -0
  293. package/dist/internal/EarnRates.d.ts.map +1 -0
  294. package/dist/internal/EarnRates.js +266 -0
  295. package/dist/internal/EarnRates.js.map +1 -0
  296. package/dist/internal/EarnVaults.d.ts +171 -0
  297. package/dist/internal/EarnVaults.d.ts.map +1 -0
  298. package/dist/internal/EarnVaults.js +547 -0
  299. package/dist/internal/EarnVaults.js.map +1 -0
  300. package/dist/internal/EdgeCache.d.ts +9 -2
  301. package/dist/internal/EdgeCache.d.ts.map +1 -1
  302. package/dist/internal/EdgeCache.js +36 -9
  303. package/dist/internal/EdgeCache.js.map +1 -1
  304. package/dist/internal/Id.d.ts +11 -0
  305. package/dist/internal/Id.d.ts.map +1 -1
  306. package/dist/internal/Id.js +13 -0
  307. package/dist/internal/Id.js.map +1 -1
  308. package/dist/internal/Idempotency.d.ts +83 -0
  309. package/dist/internal/Idempotency.d.ts.map +1 -0
  310. package/dist/internal/Idempotency.js +113 -0
  311. package/dist/internal/Idempotency.js.map +1 -0
  312. package/dist/internal/Log.d.ts +64 -9
  313. package/dist/internal/Log.d.ts.map +1 -1
  314. package/dist/internal/Log.js +29 -7
  315. package/dist/internal/Log.js.map +1 -1
  316. package/dist/internal/MetricSink.d.ts.map +1 -1
  317. package/dist/internal/MetricSink.js +47 -1
  318. package/dist/internal/MetricSink.js.map +1 -1
  319. package/dist/internal/OpenApi.d.ts +1 -1
  320. package/dist/internal/OpenApi.d.ts.map +1 -1
  321. package/dist/internal/OpenApi.js +3 -0
  322. package/dist/internal/OpenApi.js.map +1 -1
  323. package/dist/internal/Provider.d.ts +14 -0
  324. package/dist/internal/Provider.d.ts.map +1 -0
  325. package/dist/internal/Provider.js +5 -0
  326. package/dist/internal/Provider.js.map +1 -0
  327. package/dist/internal/Schema.d.ts +8 -3
  328. package/dist/internal/Schema.d.ts.map +1 -1
  329. package/dist/internal/Schema.js +24 -13
  330. package/dist/internal/Schema.js.map +1 -1
  331. package/dist/internal/Store.d.ts +6 -0
  332. package/dist/internal/Store.d.ts.map +1 -1
  333. package/dist/internal/Store.js +35 -0
  334. package/dist/internal/Store.js.map +1 -1
  335. package/dist/internal/Tidx.d.ts +43 -24
  336. package/dist/internal/Tidx.d.ts.map +1 -1
  337. package/dist/internal/Tidx.js +79 -11
  338. package/dist/internal/Tidx.js.map +1 -1
  339. package/dist/internal/Viem.d.ts +82 -65
  340. package/dist/internal/Viem.d.ts.map +1 -1
  341. package/dist/internal/Viem.js +83 -12
  342. package/dist/internal/Viem.js.map +1 -1
  343. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  344. package/dist/internal/WebhookDestination.js +34 -28
  345. package/dist/internal/WebhookDestination.js.map +1 -1
  346. package/dist/internal/Webhooks.d.ts +9 -0
  347. package/dist/internal/Webhooks.d.ts.map +1 -1
  348. package/dist/internal/Webhooks.js +15 -10
  349. package/dist/internal/Webhooks.js.map +1 -1
  350. package/dist/internal/exchange/Provider.d.ts +219 -0
  351. package/dist/internal/exchange/Provider.d.ts.map +1 -0
  352. package/dist/internal/exchange/Provider.js +117 -0
  353. package/dist/internal/exchange/Provider.js.map +1 -0
  354. package/dist/internal/exchange/index.d.ts +2 -0
  355. package/dist/internal/exchange/index.d.ts.map +1 -0
  356. package/dist/internal/exchange/index.js +2 -0
  357. package/dist/internal/exchange/index.js.map +1 -0
  358. package/dist/internal/exchange/providers/uniswap.d.ts +57 -0
  359. package/dist/internal/exchange/providers/uniswap.d.ts.map +1 -0
  360. package/dist/internal/exchange/providers/uniswap.js +553 -0
  361. package/dist/internal/exchange/providers/uniswap.js.map +1 -0
  362. package/dist/internal/funding/Action.d.ts +55 -0
  363. package/dist/internal/funding/Action.d.ts.map +1 -0
  364. package/dist/internal/funding/Action.js +74 -0
  365. package/dist/internal/funding/Action.js.map +1 -0
  366. package/dist/internal/funding/Catalog.d.ts +107 -0
  367. package/dist/internal/funding/Catalog.d.ts.map +1 -0
  368. package/dist/internal/funding/Catalog.js +270 -0
  369. package/dist/internal/funding/Catalog.js.map +1 -0
  370. package/dist/internal/funding/Chain.d.ts +76 -0
  371. package/dist/internal/funding/Chain.d.ts.map +1 -0
  372. package/dist/internal/funding/Chain.js +38 -0
  373. package/dist/internal/funding/Chain.js.map +1 -0
  374. package/dist/internal/funding/Deposit.d.ts +366 -0
  375. package/dist/internal/funding/Deposit.d.ts.map +1 -0
  376. package/dist/internal/funding/Deposit.js +286 -0
  377. package/dist/internal/funding/Deposit.js.map +1 -0
  378. package/dist/internal/funding/DepositAddress.d.ts +345 -0
  379. package/dist/internal/funding/DepositAddress.d.ts.map +1 -0
  380. package/dist/internal/funding/DepositAddress.js +189 -0
  381. package/dist/internal/funding/DepositAddress.js.map +1 -0
  382. package/dist/internal/funding/Evidence.d.ts +52 -0
  383. package/dist/internal/funding/Evidence.d.ts.map +1 -0
  384. package/dist/internal/funding/Evidence.js +314 -0
  385. package/dist/internal/funding/Evidence.js.map +1 -0
  386. package/dist/internal/funding/Provider.d.ts +1186 -0
  387. package/dist/internal/funding/Provider.d.ts.map +1 -0
  388. package/dist/internal/funding/Provider.js +1081 -0
  389. package/dist/internal/funding/Provider.js.map +1 -0
  390. package/dist/internal/funding/Reconciliation.d.ts +113 -0
  391. package/dist/internal/funding/Reconciliation.d.ts.map +1 -0
  392. package/dist/internal/funding/Reconciliation.js +676 -0
  393. package/dist/internal/funding/Reconciliation.js.map +1 -0
  394. package/dist/internal/funding/Route.d.ts +21 -0
  395. package/dist/internal/funding/Route.d.ts.map +1 -0
  396. package/dist/internal/funding/Route.js +5 -0
  397. package/dist/internal/funding/Route.js.map +1 -0
  398. package/dist/internal/funding/Subsidy.d.ts +113 -0
  399. package/dist/internal/funding/Subsidy.d.ts.map +1 -0
  400. package/dist/internal/funding/Subsidy.js +138 -0
  401. package/dist/internal/funding/Subsidy.js.map +1 -0
  402. package/dist/internal/funding/Token.d.ts +41 -0
  403. package/dist/internal/funding/Token.d.ts.map +1 -0
  404. package/dist/internal/funding/Token.js +22 -0
  405. package/dist/internal/funding/Token.js.map +1 -0
  406. package/dist/internal/funding/Transfer.d.ts +643 -0
  407. package/dist/internal/funding/Transfer.d.ts.map +1 -0
  408. package/dist/internal/funding/Transfer.js +332 -0
  409. package/dist/internal/funding/Transfer.js.map +1 -0
  410. package/dist/internal/funding/index.d.ts +13 -0
  411. package/dist/internal/funding/index.d.ts.map +1 -0
  412. package/dist/internal/funding/index.js +13 -0
  413. package/dist/internal/funding/index.js.map +1 -0
  414. package/dist/internal/funding/providers/across.d.ts +21 -0
  415. package/dist/internal/funding/providers/across.d.ts.map +1 -0
  416. package/dist/internal/funding/providers/across.js +117 -0
  417. package/dist/internal/funding/providers/across.js.map +1 -0
  418. package/dist/internal/funding/providers/relay.d.ts +46 -0
  419. package/dist/internal/funding/providers/relay.d.ts.map +1 -0
  420. package/dist/internal/funding/providers/relay.js +464 -0
  421. package/dist/internal/funding/providers/relay.js.map +1 -0
  422. package/dist/internal/funding/providers/rhino.d.ts +15 -0
  423. package/dist/internal/funding/providers/rhino.d.ts.map +1 -0
  424. package/dist/internal/funding/providers/rhino.js +116 -0
  425. package/dist/internal/funding/providers/rhino.js.map +1 -0
  426. package/dist/internal/funding/providers/squid.d.ts +19 -0
  427. package/dist/internal/funding/providers/squid.d.ts.map +1 -0
  428. package/dist/internal/funding/providers/squid.js +120 -0
  429. package/dist/internal/funding/providers/squid.js.map +1 -0
  430. package/dist/internal/funding/providers/symbiosis.d.ts +20 -0
  431. package/dist/internal/funding/providers/symbiosis.d.ts.map +1 -0
  432. package/dist/internal/funding/providers/symbiosis.js +176 -0
  433. package/dist/internal/funding/providers/symbiosis.js.map +1 -0
  434. package/dist/internal/index.d.ts +5 -1
  435. package/dist/internal/index.d.ts.map +1 -1
  436. package/dist/internal/index.js +5 -1
  437. package/dist/internal/index.js.map +1 -1
  438. package/dist/internal/webhookDestinations/url.js +61 -57
  439. package/dist/internal/webhookDestinations/url.js.map +1 -1
  440. package/dist/providers/index.d.ts +43 -0
  441. package/dist/providers/index.d.ts.map +1 -0
  442. package/dist/providers/index.js +19 -0
  443. package/dist/providers/index.js.map +1 -0
  444. package/package.json +9 -4
  445. package/src/ApiKeys.test.ts +11 -7
  446. package/src/ApiKeys.ts +37 -9
  447. package/src/App.test.ts +85 -10
  448. package/src/App.ts +224 -137
  449. package/src/Assets.ts +29 -4
  450. package/src/Client.test-d.ts +650 -30
  451. package/src/Scope.test.ts +19 -0
  452. package/src/Scope.ts +28 -11
  453. package/src/Webhooks.test.ts +25 -0
  454. package/src/Webhooks.ts +1 -1
  455. package/src/admin/App.test.ts +9 -2
  456. package/src/admin/App.ts +32 -9
  457. package/src/admin/apps/api-keys.test.ts +167 -23
  458. package/src/admin/apps/api-keys.ts +166 -28
  459. package/src/admin/apps/earn-vaults.test.ts +338 -0
  460. package/src/admin/apps/earn-vaults.ts +572 -0
  461. package/src/admin/apps/scopes.test.ts +16 -1
  462. package/src/admin/ui/plugin.ts +2 -2
  463. package/src/admin/ui/src/components/EarnVaultForm.tsx +137 -0
  464. package/src/admin/ui/src/components/MobileProfile.tsx +2 -2
  465. package/src/admin/ui/src/components/Page.tsx +1 -1
  466. package/src/admin/ui/src/components/ThemeToggle.tsx +1 -1
  467. package/src/admin/ui/src/lib/api-keys.ts +37 -7
  468. package/src/admin/ui/src/lib/client.ts +1 -1
  469. package/src/admin/ui/src/lib/config.ts +1 -1
  470. package/src/admin/ui/src/lib/early-access.ts +8 -9
  471. package/src/admin/ui/src/lib/earn-vaults.ts +207 -0
  472. package/src/admin/ui/src/lib/format.ts +13 -0
  473. package/src/admin/ui/src/lib/organizations.test.ts +14 -4
  474. package/src/admin/ui/src/lib/organizations.ts +7 -1
  475. package/src/admin/ui/src/lib/response.ts +22 -0
  476. package/src/admin/ui/src/lib/scopes.test.ts +19 -11
  477. package/src/admin/ui/src/lib/scopes.ts +28 -7
  478. package/src/admin/ui/src/lib/verified-tokens.ts +10 -9
  479. package/src/admin/ui/src/routeTree.gen.ts +70 -7
  480. package/src/admin/ui/src/router.tsx +2 -2
  481. package/src/admin/ui/src/routes/__root.tsx +9 -8
  482. package/src/admin/ui/src/routes/api-keys.tsx +475 -64
  483. package/src/admin/ui/src/routes/early-access.tsx +5 -12
  484. package/src/admin/ui/src/routes/earn.vaults.tsx +348 -0
  485. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +154 -0
  486. package/src/admin/ui/src/routes/earn.vaults_.new.tsx +190 -0
  487. package/src/admin/ui/src/routes/organizations.tsx +4 -7
  488. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +56 -54
  489. package/src/admin/ui/src/routes/verified-tokens.tsx +4 -4
  490. package/src/admin/ui/vite.config.ts +4 -1
  491. package/src/admin/ui.generated.ts +189 -95
  492. package/src/analytics/Analytics.test.ts +69 -10
  493. package/src/analytics/Analytics.ts +20 -1
  494. package/src/analytics/tables/requestEvents.ts +79 -37
  495. package/src/apps/Zones.test.ts +33 -0
  496. package/src/apps/Zones.ts +40 -0
  497. package/src/apps/composition.test.ts +84 -1
  498. package/src/apps/data/App.test.ts +38 -9
  499. package/src/apps/data/App.ts +96 -33
  500. package/src/apps/data/FxOracle.ts +1 -1
  501. package/src/apps/data/index.ts +0 -1
  502. package/src/apps/data/routes/activities.test.ts +82 -6
  503. package/src/apps/data/routes/activities.ts +131 -71
  504. package/src/apps/data/routes/balances.test.ts +50 -37
  505. package/src/apps/data/routes/balances.ts +5 -5
  506. package/src/apps/data/routes/blocks.test.ts +2 -0
  507. package/src/apps/data/routes/coingecko.test.ts +22 -0
  508. package/src/apps/data/routes/coingecko.ts +84 -48
  509. package/src/apps/data/routes/earn.test.ts +1108 -0
  510. package/src/apps/data/routes/earn.ts +1391 -0
  511. package/src/apps/data/routes/exchanges.test.ts +82 -33
  512. package/src/apps/data/routes/exchanges.ts +842 -227
  513. package/src/apps/data/routes/fee-amm.test.ts +4 -6
  514. package/src/apps/data/routes/fee-amm.ts +69 -12
  515. package/src/apps/data/routes/funding.test.ts +2712 -221
  516. package/src/apps/data/routes/funding.ts +1604 -387
  517. package/src/apps/data/routes/indexer.test.ts +43 -34
  518. package/src/apps/data/routes/indexer.ts +19 -2
  519. package/src/apps/data/routes/receipts.test.ts +5 -3
  520. package/src/apps/data/routes/receipts.ts +13 -11
  521. package/src/apps/data/routes/rpc.test.ts +446 -28
  522. package/src/apps/data/routes/rpc.ts +243 -25
  523. package/src/apps/data/routes/tokenlist.test.ts +15 -0
  524. package/src/apps/data/routes/tokenlist.ts +88 -22
  525. package/src/apps/data/routes/tokens.ts +262 -86
  526. package/src/apps/data/routes/transactions.test.ts +2 -0
  527. package/src/apps/data/routes/transactions.ts +77 -42
  528. package/src/apps/data/routes/transfers.test.ts +166 -4
  529. package/src/apps/data/routes/transfers.ts +216 -78
  530. package/src/apps/data/routes/valuation.test.ts +62 -9
  531. package/src/apps/data/routes/valuation.ts +2 -2
  532. package/src/apps/data/routes/verified-tokens.test.ts +51 -0
  533. package/src/apps/data/routes/verified-tokens.ts +52 -15
  534. package/src/apps/data/routes/webhooks.test.ts +432 -33
  535. package/src/apps/data/routes/webhooks.ts +189 -79
  536. package/src/apps/data/routes/zones.test.ts +473 -0
  537. package/src/apps/data/routes/zones.ts +338 -0
  538. package/src/apps/docs/reference.ts +8 -6
  539. package/src/apps/index.ts +2 -1
  540. package/src/apps/management/Billing.test.ts +29 -0
  541. package/src/apps/management/Billing.ts +23 -0
  542. package/src/apps/management/Error.ts +12 -0
  543. package/src/apps/management/routes/api-keys.test.ts +5 -3
  544. package/src/apps/management/routes/api-keys.ts +2 -9
  545. package/src/apps/management/routes/invitations.ts +4 -11
  546. package/src/apps/management/routes/orgs.ts +2 -9
  547. package/src/apps/management/routes/scopes.test.ts +7 -2
  548. package/src/apps/management/routes/scopes.ts +2 -2
  549. package/src/apps/management/routes/webhooks.test.ts +6 -1
  550. package/src/apps/management/routes/webhooks.ts +15 -3
  551. package/src/apps/mpp/App.ts +276 -29
  552. package/src/apps/mpp/Mpp.test.ts +631 -26
  553. package/src/apps/mpp/Mpp.ts +0 -42
  554. package/src/apps/mpp/internal/Adapter.ts +108 -3
  555. package/src/apps/mpp/internal/Error.test.ts +33 -0
  556. package/src/apps/mpp/internal/Error.ts +44 -6
  557. package/src/apps/mpp/internal/Idempotency.test.ts +213 -0
  558. package/src/apps/mpp/internal/Idempotency.ts +59 -5
  559. package/src/apps/mpp/internal/Policy.test.ts +122 -0
  560. package/src/apps/mpp/internal/Policy.ts +98 -0
  561. package/src/apps/mpp/internal/ScreenManager.test.ts +68 -0
  562. package/src/apps/mpp/internal/ScreenManager.ts +41 -0
  563. package/src/apps/mpp/internal/Screening.ts +40 -3
  564. package/src/apps/mpp/internal/Sponsorship.ts +144 -0
  565. package/src/apps/mpp/internal/Utils.ts +30 -0
  566. package/src/apps/relay/App.test.ts +495 -64
  567. package/src/apps/relay/App.ts +234 -24
  568. package/src/apps/relay/Sponsorships.test.ts +51 -0
  569. package/src/apps/relay/Sponsorships.ts +19 -7
  570. package/src/db/Db.ts +439 -0
  571. package/src/db/Schema.ts +325 -2
  572. package/src/db/tables/earnVaults.test.ts +185 -0
  573. package/src/db/tables/earnVaults.ts +151 -0
  574. package/src/db/tables/fundingCatalog.test.ts +108 -0
  575. package/src/db/tables/fundingCatalog.ts +157 -0
  576. package/src/db/tables/fundingDepositAddresses.test.ts +580 -0
  577. package/src/db/tables/fundingDepositAddresses.ts +376 -0
  578. package/src/db/tables/fundingDeposits.test.ts +261 -0
  579. package/src/db/tables/fundingDeposits.ts +229 -0
  580. package/src/db/tables/fundingIdempotency.test.ts +182 -0
  581. package/src/db/tables/fundingIdempotency.ts +180 -0
  582. package/src/db/tables/fundingTransferEvents.ts +28 -0
  583. package/src/db/tables/fundingTransferTransactions.test.ts +69 -0
  584. package/src/db/tables/fundingTransferTransactions.ts +37 -0
  585. package/src/db/tables/fundingTransfers.test.ts +89 -0
  586. package/src/db/tables/fundingTransfers.ts +153 -0
  587. package/src/db/tables/organizations.test.ts +88 -0
  588. package/src/db/tables/organizations.ts +42 -1
  589. package/src/db/tables/projects.ts +16 -0
  590. package/src/db/tables/requestUsage.ts +23 -0
  591. package/src/db/tables/sponsoredTransactions.test.ts +52 -2
  592. package/src/db/tables/sponsoredTransactions.ts +18 -6
  593. package/src/db/tables/users.ts +16 -0
  594. package/src/db/tables/verifiedTokens.ts +19 -14
  595. package/src/db/tables/webhookSubscriptions.ts +58 -3
  596. package/src/handlers/relay.test.ts +2 -0
  597. package/src/handlers/relay.ts +95 -99
  598. package/src/internal/Auth.test.ts +48 -0
  599. package/src/internal/Auth.ts +61 -2
  600. package/src/internal/Cache.ts +13 -9
  601. package/src/internal/Cursor.test.ts +2 -2
  602. package/src/internal/Cursor.ts +1 -1
  603. package/src/internal/EarnRates.test.ts +125 -0
  604. package/src/internal/EarnRates.ts +409 -0
  605. package/src/internal/EarnVaults.test.ts +195 -0
  606. package/src/internal/EarnVaults.ts +876 -0
  607. package/src/internal/EdgeCache.test.ts +37 -5
  608. package/src/internal/EdgeCache.ts +39 -7
  609. package/src/internal/Id.ts +14 -0
  610. package/src/internal/Idempotency.test.ts +109 -0
  611. package/src/internal/Idempotency.ts +237 -0
  612. package/src/internal/Log.test-d.ts +41 -8
  613. package/src/internal/Log.test.ts +189 -8
  614. package/src/internal/Log.ts +124 -17
  615. package/src/internal/MetricSink.test.ts +244 -4
  616. package/src/internal/MetricSink.ts +51 -1
  617. package/src/internal/OpenApi.ts +3 -0
  618. package/src/internal/Provider.test.ts +17 -0
  619. package/src/internal/Provider.ts +17 -0
  620. package/src/internal/Schema.test.ts +13 -0
  621. package/src/internal/Schema.ts +41 -24
  622. package/src/internal/Store.test.ts +25 -0
  623. package/src/internal/Store.ts +46 -0
  624. package/src/internal/Tidx.local.test.ts +78 -3
  625. package/src/internal/Tidx.test.ts +212 -18
  626. package/src/internal/Tidx.ts +112 -32
  627. package/src/internal/VerifiedTokens.test.ts +15 -0
  628. package/src/internal/Viem.test.ts +170 -17
  629. package/src/internal/Viem.ts +111 -28
  630. package/src/internal/WebhookDestination.ts +25 -23
  631. package/src/internal/Webhooks.test.ts +59 -0
  632. package/src/internal/Webhooks.ts +34 -10
  633. package/src/internal/exchange/Provider.test.ts +216 -0
  634. package/src/internal/exchange/Provider.ts +326 -0
  635. package/src/internal/exchange/index.test.ts +6 -0
  636. package/src/internal/exchange/index.ts +1 -0
  637. package/src/internal/exchange/providers/uniswap.test.ts +122 -0
  638. package/src/internal/exchange/providers/uniswap.ts +682 -0
  639. package/src/internal/funding/Action.test.ts +138 -0
  640. package/src/internal/funding/Action.ts +117 -0
  641. package/src/internal/funding/Catalog.test.ts +311 -0
  642. package/src/internal/funding/Catalog.ts +347 -0
  643. package/src/internal/funding/Chain.test.ts +79 -0
  644. package/src/internal/funding/Chain.ts +120 -0
  645. package/src/internal/funding/Deposit.test.ts +26 -0
  646. package/src/internal/funding/Deposit.ts +453 -0
  647. package/src/internal/funding/DepositAddress.test.ts +18 -0
  648. package/src/internal/funding/DepositAddress.ts +312 -0
  649. package/src/internal/funding/Evidence.test.ts +297 -0
  650. package/src/internal/funding/Evidence.ts +430 -0
  651. package/src/internal/funding/Provider.test.ts +1328 -0
  652. package/src/internal/funding/Provider.ts +1796 -0
  653. package/src/internal/funding/Reconciliation.local.test.ts +504 -0
  654. package/src/internal/funding/Reconciliation.test.ts +2020 -0
  655. package/src/internal/funding/Reconciliation.ts +930 -0
  656. package/src/internal/funding/Route.test.ts +66 -0
  657. package/src/internal/funding/Route.ts +29 -0
  658. package/src/internal/funding/Subsidy.local.test.ts +152 -0
  659. package/src/internal/funding/Subsidy.test.ts +75 -0
  660. package/src/internal/funding/Subsidy.ts +260 -0
  661. package/src/internal/funding/Token.test.ts +47 -0
  662. package/src/internal/funding/Token.ts +59 -0
  663. package/src/internal/funding/Transfer.test.ts +369 -0
  664. package/src/internal/funding/Transfer.ts +522 -0
  665. package/src/internal/funding/index.test.ts +20 -0
  666. package/src/internal/funding/index.ts +12 -0
  667. package/src/internal/funding/providers/across.ts +149 -0
  668. package/src/{apps/data/FundingQuoteProviders.test.ts → internal/funding/providers/index.test.ts} +178 -157
  669. package/src/internal/funding/providers/relay.test.ts +474 -0
  670. package/src/internal/funding/providers/relay.ts +559 -0
  671. package/src/internal/funding/providers/rhino.ts +139 -0
  672. package/src/internal/funding/providers/squid.ts +151 -0
  673. package/src/internal/funding/providers/symbiosis.ts +221 -0
  674. package/src/internal/index.test.ts +5 -1
  675. package/src/internal/index.ts +5 -1
  676. package/src/internal/webhookDestinations/url.ts +49 -45
  677. package/src/providers/index.test.ts +14 -0
  678. package/src/providers/index.ts +54 -0
  679. package/dist/apps/data/FundingQuoteProviders.d.ts +0 -174
  680. package/dist/apps/data/FundingQuoteProviders.d.ts.map +0 -1
  681. package/dist/apps/data/FundingQuoteProviders.js +0 -775
  682. package/dist/apps/data/FundingQuoteProviders.js.map +0 -1
  683. package/dist/internal/FundingQuotes.d.ts +0 -864
  684. package/dist/internal/FundingQuotes.d.ts.map +0 -1
  685. package/dist/internal/FundingQuotes.js +0 -672
  686. package/dist/internal/FundingQuotes.js.map +0 -1
  687. package/src/apps/data/FundingQuoteProviders.ts +0 -1043
  688. package/src/internal/FundingQuotes.test.ts +0 -456
  689. package/src/internal/FundingQuotes.ts +0 -944
@@ -1,7 +1,10 @@
1
+ /** @module-tag localnet */
2
+
1
3
  import type * as z from 'zod/mini'
2
4
  import { Value as core_Value } from 'ox'
3
5
  import { Db, Schema } from 'tapimo'
4
6
  import { FxOracle } from 'tapimo/apps'
7
+ import { Addresses } from 'viem/tempo'
5
8
 
6
9
  import * as TestApp from '../../../../test/App.js'
7
10
  import * as Runtime from '../../../../test/runtime.js'
@@ -32,6 +35,50 @@ describe('GET /transfers', () => {
32
35
  expect(Transfers.schema.getTransfers.Query.safeParse({ include: 'token' }).success).toBe(false)
33
36
  })
34
37
 
38
+ test.runIf(Runtime.get().mode === 'testnet')(
39
+ 'excludes historical non-TIP-20 transfers for affected mainnet accounts',
40
+ async () => {
41
+ const client = TestApp.client()
42
+ const cases = [
43
+ {
44
+ address: Schema.Address.parse('0xad511ba481696d73bcf47d411ad9ea4f77023ddc'),
45
+ blockNumber: '17681311',
46
+ expectedCount: 1,
47
+ },
48
+ {
49
+ address: Schema.Address.parse('0x5eb391a6c707ae1168989bbc6c5f5db4a0e06837'),
50
+ blockNumber: '31227810',
51
+ expectedCount: 0,
52
+ },
53
+ ]
54
+
55
+ for (const { address, blockNumber, expectedCount } of cases) {
56
+ const response = await client.v1.transfers.$get(
57
+ {
58
+ query: {
59
+ address,
60
+ 'blockNumber.from': blockNumber,
61
+ 'blockNumber.to': blockNumber,
62
+ chainId: '4217',
63
+ include: 'totalCount',
64
+ limit: '10',
65
+ page: '1',
66
+ },
67
+ },
68
+ TestApp.auth,
69
+ )
70
+ const body = await TestApp.json(response, Transfers.schema.getTransfers.Response)
71
+
72
+ expect(response.status).toBe(200)
73
+ expect(body.data).toHaveLength(expectedCount)
74
+ expect(body.data.every((entry) => entry.sourceToken.address.startsWith('0x20c0'))).toBe(
75
+ true,
76
+ )
77
+ expect(body.meta?.totalCount).toBe(expectedCount)
78
+ }
79
+ },
80
+ )
81
+
35
82
  test('degrades unavailable verification data to null valuations', async () => {
36
83
  const db = () =>
37
84
  Db.postgres({
@@ -63,9 +110,9 @@ describe('GET /transfers', () => {
63
110
  const unsupportedResponse = await client.v1.transfers.$get(
64
111
  {
65
112
  query: {
66
- denomination: 'JPY',
67
113
  limit: '5',
68
114
  token: TestApp.tokenWithHolders,
115
+ 'valuation.currency': 'JPY',
69
116
  },
70
117
  },
71
118
  TestApp.auth,
@@ -666,7 +713,7 @@ describe('GET /transfers', () => {
666
713
  amount: core_Value.format(BigInt(entry.sourceAmount.baseUnits), 6),
667
714
  currency: 'USD',
668
715
  })
669
- expect(body.meta?.pricing).toBeUndefined()
716
+ expect(body.meta?.valuation).toBeUndefined()
670
717
  })
671
718
 
672
719
  test('values transfer legs in a requested denomination', async () => {
@@ -676,7 +723,13 @@ describe('GET /transfers', () => {
676
723
 
677
724
  // Denominations are case-insensitive.
678
725
  const response = await client.v1.transfers.$get(
679
- { query: { denomination: 'aud', limit: '5', token: TestApp.tokenWithHolders } },
726
+ {
727
+ query: {
728
+ limit: '5',
729
+ token: TestApp.tokenWithHolders,
730
+ 'valuation.currency': 'aud',
731
+ },
732
+ },
680
733
  TestApp.auth,
681
734
  )
682
735
  const body = await TestApp.json(response, Transfers.schema.getTransfers.Response)
@@ -689,7 +742,7 @@ describe('GET /transfers', () => {
689
742
  amount: core_Value.format((BigInt(entry.sourceAmount.baseUnits) * 16n) / 10n, 6),
690
743
  currency: 'AUD',
691
744
  })
692
- expect(body.meta?.pricing).toMatchInlineSnapshot(`
745
+ expect(body.meta?.valuation).toMatchInlineSnapshot(`
693
746
  {
694
747
  "asOf": "2026-01-01T00:00:00.000Z",
695
748
  "basis": "nominal",
@@ -751,3 +804,112 @@ describe('GET /transfers', () => {
751
804
  `)
752
805
  })
753
806
  })
807
+
808
+ describe('collate', () => {
809
+ /** A minimal transfer row at a `(block, log index)` position. */
810
+ const row = (block: number, logIndex: number) => ({
811
+ block_num: block,
812
+ log_idx: logIndex,
813
+ tx_hash: `0x${'cc'.repeat(32)}`,
814
+ })
815
+
816
+ test('passes a lone engine-paged stream through untouched', () => {
817
+ const rows = [row(40, 4), row(20, 2)]
818
+ expect(
819
+ Transfers.collate([{ rows, sample: false }], { limit: 3, offset: 0, order: 'desc' }),
820
+ ).toEqual({
821
+ capped: false,
822
+ rows,
823
+ })
824
+ })
825
+
826
+ test('merges split streams: dedupes, orders globally, applies the window', () => {
827
+ const shared = row(30, 3)
828
+ const { capped, rows } = Transfers.collate(
829
+ [
830
+ { rows: [row(40, 4), shared], sample: false },
831
+ { rows: [{ ...shared }, row(10, 1)], sample: false },
832
+ ],
833
+ { limit: 2, offset: 1, order: 'desc' },
834
+ )
835
+ expect(capped).toBe(false)
836
+ expect(rows.map((entry) => entry['block_num'])).toEqual([30, 10])
837
+ })
838
+
839
+ test('orders and pages a below-cap fallback sample', () => {
840
+ const { capped, rows } = Transfers.collate([{ rows: [row(10, 1), row(30, 3)], sample: true }], {
841
+ limit: 2,
842
+ offset: 0,
843
+ order: 'desc',
844
+ })
845
+ expect(capped).toBe(false)
846
+ expect(rows.map((entry) => entry['block_num'])).toEqual([30, 10])
847
+ })
848
+
849
+ test('flags a fallback sample that hit the cap', () => {
850
+ const sample = Array.from({ length: Schema.countCap }, (_, index) => row(index + 1, index))
851
+ const { capped, rows } = Transfers.collate(
852
+ [
853
+ { rows: [row(40_000, 20_000)], sample: false },
854
+ { rows: sample, sample: true },
855
+ ],
856
+ { limit: 3, offset: 0, order: 'desc' },
857
+ )
858
+ expect(capped).toBe(true)
859
+ expect(rows.map((entry) => entry['block_num'])).toEqual([40_000, 10_000, 9_999])
860
+ })
861
+ })
862
+
863
+ describe('foldCount', () => {
864
+ const swapper = `0x${'11'.repeat(20)}`
865
+ const recipient = `0x${'22'.repeat(20)}`
866
+ const sourceToken = '0x20c0000000000000000000000000000000000001'
867
+ const destinationToken = '0x20c0000000000000000000000000000000000002'
868
+ /** One `Transfer` leg row within a transaction. */
869
+ const leg = (txHash: string, logIndex: number, from: string, to: string, token: string) => ({
870
+ address: token,
871
+ from,
872
+ log_idx: logIndex,
873
+ to,
874
+ tx_hash: txHash,
875
+ value: '100',
876
+ })
877
+ const plain = leg(`0x${'ee'.repeat(32)}`, 0, swapper, recipient, sourceToken)
878
+
879
+ test('subtracts classified bundle legs', () => {
880
+ const txHash = `0x${'dd'.repeat(32)}`
881
+ // The account server's approve+swap+forward multicall: source into the
882
+ // DEX, swapped token back, delivery to the recipient, plus the fee leg.
883
+ const rows = [
884
+ leg(txHash, 0, swapper, Addresses.stablecoinDex, sourceToken),
885
+ leg(txHash, 1, Addresses.stablecoinDex, swapper, destinationToken),
886
+ leg(txHash, 2, swapper, recipient, destinationToken),
887
+ leg(txHash, 3, swapper, Addresses.feeManager, sourceToken),
888
+ plain,
889
+ ]
890
+ expect(Transfers.foldCount(rows, { capped: false })).toEqual({
891
+ totalCount: 2,
892
+ totalCountCapped: false,
893
+ })
894
+ })
895
+
896
+ test('keeps a cap-truncated sample conservative', () => {
897
+ const txHash = `0x${'dd'.repeat(32)}`
898
+ // A partial bundle: the DEX return leg fell outside the cap, defeating
899
+ // classification. The conservative pass still suppresses the plumbing.
900
+ const rows = [
901
+ leg(txHash, 0, swapper, Addresses.stablecoinDex, sourceToken),
902
+ leg(txHash, 2, swapper, recipient, destinationToken),
903
+ leg(txHash, 3, swapper, Addresses.feeManager, sourceToken),
904
+ plain,
905
+ ]
906
+ expect(Transfers.foldCount(rows, { capped: false })).toEqual({
907
+ totalCount: 4,
908
+ totalCountCapped: false,
909
+ })
910
+ expect(Transfers.foldCount(rows, { capped: true })).toEqual({
911
+ totalCount: 2,
912
+ totalCountCapped: true,
913
+ })
914
+ })
915
+ })
@@ -12,7 +12,7 @@ import * as OpenApi from '../../../internal/OpenApi.js'
12
12
  import * as Response from '../../../internal/Response.js'
13
13
  import * as Schema from '../../../internal/Schema.js'
14
14
  import * as Store from '../../../internal/Store.js'
15
- import type * as Tidx from '../../../internal/Tidx.js'
15
+ import * as Tidx from '../../../internal/Tidx.js'
16
16
  import * as Timing from '../../../internal/Timing.js'
17
17
  import * as Ttl from '../../../internal/Ttl.js'
18
18
  import * as Value from '../../../internal/Value.js'
@@ -26,6 +26,13 @@ import * as Valuation from './valuation.js'
26
26
  // the indexer SQL.
27
27
  export const signature = 'event Transfer(address indexed from, address indexed to, uint256 value)'
28
28
 
29
+ // TIP-20 contracts occupy the deterministic `0x20c0…` range. Full-width
30
+ // bounds compare correctly as PostgreSQL bytea and ClickHouse hex strings.
31
+ const tip20AddressRange = [
32
+ '0x20c0000000000000000000000000000000000000',
33
+ '0x20c1000000000000000000000000000000000000',
34
+ ] as const
35
+
29
36
  // A memo'd TIP-20 transfer emits `TransferWithMemo` alongside the plain
30
37
  // `Transfer`; the two rows match on `(tx_hash, from, to, amount, token)`. A
31
38
  // text memo is decoded into `memo`; an MPP attribution fingerprint memo is
@@ -96,7 +103,7 @@ export namespace schema {
96
103
  .optional(z.nullable(Valuation.schema.Value))
97
104
  .check(
98
105
  z.describe(
99
- 'The amount’s nominal value in the requested `denomination`. `null` when the token is ' +
106
+ 'The amount’s nominal value in the requested `valuation.currency`. `null` when the token is ' +
100
107
  'unverified, its display currency has no rate, or rates were unavailable.',
101
108
  ),
102
109
  ),
@@ -169,13 +176,13 @@ export namespace schema {
169
176
  'blockNumber.to': Schema.blockNumberBound('transfers', 'to'),
170
177
  chainId: Schema.ChainIdQuery,
171
178
  cursor: Schema.Cursor,
172
- denomination: Schema.Denomination,
173
179
  include: includeQuery,
174
180
  limit: Schema.Limit,
175
181
  order: Schema.Order,
176
182
  page: Schema.Page,
177
183
  'timestamp.from': Schema.timestampBound('transfers', 'from'),
178
184
  'timestamp.to': Schema.timestampBound('transfers', 'to'),
185
+ 'valuation.currency': Schema.Denomination,
179
186
  })
180
187
  .check(
181
188
  z.refine((query) => !(query.address && (query.sender || query.recipient)), {
@@ -281,7 +288,7 @@ export namespace schema {
281
288
  /** Page-level resources: opt-in counts plus valuation rate provenance. */
282
289
  export const Meta = z
283
290
  .object({
284
- pricing: z
291
+ valuation: z
285
292
  .optional(Valuation.schema.Pricing)
286
293
  .check(
287
294
  z.describe(
@@ -343,7 +350,7 @@ export function transfers(options: transfers.Options = {}) {
343
350
  }),
344
351
  Cache.response<App.Environment>({
345
352
  cacheControl: Cache.policies.feed,
346
- name: 'tempo-api:transfers:v2',
353
+ name: 'tempo-api:transfers:v3',
347
354
  key: (c) => Cache.urlKey(c, schema.getTransfers.Query),
348
355
  }),
349
356
  async (c) => {
@@ -356,7 +363,7 @@ export function transfers(options: transfers.Options = {}) {
356
363
 
357
364
  const options = c.req.valid('query')
358
365
  const chainId = options.chainId ?? c.get('chainId')
359
- const denomination = options.denomination ?? 'USD'
366
+ const denomination = options['valuation.currency'] ?? 'USD'
360
367
 
361
368
  // Cursor pages carry current valuations and curated verification data.
362
369
  if (options.cursor !== undefined) Cache.setPolicy(c, Cache.policies.metadata)
@@ -569,9 +576,16 @@ export function transfers(options: transfers.Options = {}) {
569
576
  },
570
577
  )
571
578
 
572
- const counts = countPromise ? await countPromise : undefined
579
+ // Wait at most 2.5s more for a still-pending count (cold indexer
580
+ // reads have stalled 20s+); a timed-out count omits `meta` counts.
581
+ const counts = countPromise
582
+ ? await Promise.race([
583
+ countPromise,
584
+ new Promise<undefined>((resolve) => setTimeout(() => resolve(undefined), 2_500)),
585
+ ])
586
+ : undefined
573
587
  const meta = {
574
- ...(rates ? { pricing: Valuation.pricing(rates, oracle) } : {}),
588
+ ...(rates ? { valuation: Valuation.pricing(rates, oracle) } : {}),
575
589
  ...counts,
576
590
  }
577
591
 
@@ -630,7 +644,7 @@ export function query(c: Context<App.Environment>, options: query.Options) {
630
644
  }
631
645
  },
632
646
  {
633
- key: `transfers:v1:${chainId}:${options.order}:${options.token ?? ''}:${options.eitherSide ?? ''}:${options.sender ?? ''}:${options.recipient ?? ''}:${options.fromBlock ?? ''}:${options.toBlock ?? ''}:${options.fromTimestamp ?? ''}:${options.toTimestamp ?? ''}:${cursor ? `cursor:${cursor[0]}:${cursor[1]}` : options.page !== undefined && options.page > 1 ? `page:${options.page}` : 'head'}:${options.limit}`,
647
+ key: `transfers:v2:${chainId}:${options.order}:${options.token ?? ''}:${options.eitherSide ?? ''}:${options.sender ?? ''}:${options.recipient ?? ''}:${options.fromBlock ?? ''}:${options.toBlock ?? ''}:${options.fromTimestamp ?? ''}:${options.toTimestamp ?? ''}:${cursor ? `cursor:${cursor[0]}:${cursor[1]}` : options.page !== undefined && options.page > 1 ? `page:${options.page}` : 'head'}:${options.limit}`,
634
648
  store,
635
649
  ttl: Ttl.seconds(15),
636
650
  },
@@ -713,35 +727,56 @@ export async function scan(deps: scan.Deps, options: scan.Options): Promise<scan
713
727
  )
714
728
  const addressScoped = address !== undefined
715
729
  const window = addressScoped ? (offset ?? 0) + limit + 1 : limit + 1
730
+ // Only one-sided `"from"`/`"to"` equality shapes hit the planner's
731
+ // deterministic top-N kill, so only they take the capped fallback.
732
+ const sideFiltered =
733
+ addressScoped || options.sender !== undefined || options.recipient !== undefined
716
734
  const fetchRows = async (side?: 'from' | 'to') => {
717
735
  const sideFilters =
718
736
  side !== undefined && address !== undefined
719
737
  ? [`"${side}" = '${address}'`, ...filters]
720
738
  : filters
721
739
  const where = sideFilters.length > 0 ? `WHERE ${sideFilters.join(' AND ')}` : ''
722
- const result = await tidx.fetch({
723
- chainId,
724
- ...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
725
- query: `
726
- SELECT "from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}, tx_hash, block_num, log_idx, block_timestamp
727
- FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
728
- ${where}
729
- ORDER BY block_num ${direction}, log_idx ${direction}
730
- LIMIT ${window}${!addressScoped && offset !== undefined ? ` OFFSET ${offset}` : ''}
731
- ` as string,
732
- })
733
- return result.rows
740
+ const select = `"from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}, tx_hash, block_num, log_idx, block_timestamp`
741
+ const table = tokenScoped ? 'token_transfers' : 'Transfer'
742
+ try {
743
+ const result = await tidx.fetch({
744
+ chainId,
745
+ ...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
746
+ query: `
747
+ SELECT ${select}
748
+ FROM ${table}
749
+ ${where}
750
+ ORDER BY block_num ${direction}, log_idx ${direction}
751
+ LIMIT ${window}${!addressScoped && offset !== undefined ? ` OFFSET ${offset}` : ''}
752
+ ` as string,
753
+ })
754
+ return { rows: result.rows, sample: false }
755
+ } catch (error) {
756
+ // The planner kills a one-sided top-N walk when its matches sit far
757
+ // from head (422 `db error`). Retry as a plain capped scan, returning
758
+ // the whole sample so truncation stays detectable.
759
+ if (!sideFiltered || !Tidx.isDeterministicError(error)) throw error
760
+ const result = await tidx.fetch({
761
+ chainId,
762
+ ...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
763
+ query: `
764
+ SELECT ${select}
765
+ FROM ${table}
766
+ ${where}
767
+ LIMIT ${Schema.countCap}
768
+ ` as string,
769
+ })
770
+ return { rows: result.rows, sample: true }
771
+ }
734
772
  }
735
773
 
736
774
  // TIDX cannot plan `(from = X OR to = X)` over the decoded Transfer table.
737
- // Merge two bounded, server-filtered streams while preserving global order.
738
- const rows = addressScoped
739
- ? mergeTransferRows((await Promise.all([fetchRows('from'), fetchRows('to')])).flat(), {
740
- limit: limit + 1,
741
- offset: offset ?? 0,
742
- order,
743
- })
744
- : await fetchRows()
775
+ // Fetch one bounded, server-filtered stream per side and collate app-side.
776
+ const fetched = addressScoped
777
+ ? await Promise.all([fetchRows('from'), fetchRows('to')])
778
+ : [await fetchRows()]
779
+ const { capped, rows } = collate(fetched, { limit: limit + 1, offset: offset ?? 0, order })
745
780
 
746
781
  // The next page anchors below the last fetched row's `(block, log_idx)`.
747
782
  const page = Cursor.paginate({
@@ -789,13 +824,69 @@ export async function scan(deps: scan.Deps, options: scan.Options): Promise<scan
789
824
  })
790
825
  }
791
826
 
792
- return { hasMore: page.hasMore, items, limit, nextCursor: page.nextCursor }
827
+ // A truncated sample cannot anchor keyset pagination without silently
828
+ // skipping unseen matches, so the page ends the list instead.
829
+ return {
830
+ capped,
831
+ hasMore: capped ? false : page.hasMore,
832
+ items,
833
+ limit,
834
+ nextCursor: capped ? null : page.nextCursor,
835
+ }
836
+ }
837
+
838
+ /**
839
+ * Collates per-side transfer streams into one ordered page window: split and
840
+ * sampled streams are deduped, globally ordered, and paged app-side, while a
841
+ * lone engine-paged stream passes through. `capped` marks a fallback sample
842
+ * that hit `Schema.countCap`: an arbitrary subset of that side's matches,
843
+ * unfit to anchor pagination or exact counts.
844
+ */
845
+ export function collate(
846
+ fetched: readonly collate.Stream[],
847
+ options: collate.Options,
848
+ ): collate.Result {
849
+ const capped = fetched.some(({ rows, sample }) => sample && rows.length >= Schema.countCap)
850
+ const rows =
851
+ fetched.length > 1 || fetched.some(({ sample }) => sample)
852
+ ? mergeTransferRows(
853
+ fetched.flatMap(({ rows }) => rows),
854
+ options,
855
+ )
856
+ : (fetched[0]?.rows ?? [])
857
+ return { capped, rows }
858
+ }
859
+
860
+ export declare namespace collate {
861
+ /** One fetched transfer stream. */
862
+ type Stream = {
863
+ /** Raw TIDX rows. */
864
+ rows: readonly Record<string, unknown>[]
865
+ /** Whether the rows are an unordered, unpaged capped fallback sample. */
866
+ sample: boolean
867
+ }
868
+ /** The page window applied to the merged stream. */
869
+ type Options = {
870
+ /** Maximum merged rows to return. */
871
+ limit: number
872
+ /** Number of globally ordered rows to skip. */
873
+ offset: number
874
+ /** Global transfer ordering. */
875
+ order: 'asc' | 'desc'
876
+ }
877
+ /** The collated page window. */
878
+ type Result = {
879
+ /** Whether any fallback sample hit `Schema.countCap`. */
880
+ capped: boolean
881
+ /** The ordered page window rows. */
882
+ rows: readonly Record<string, unknown>[]
883
+ }
793
884
  }
794
885
 
795
886
  /** Deduplicates and globally orders bounded `from`/`to` transfer streams. */
796
887
  function mergeTransferRows(
797
888
  rows: readonly Record<string, unknown>[],
798
- options: mergeTransferRows.Options,
889
+ options: collate.Options,
799
890
  ): Record<string, unknown>[] {
800
891
  const positioned: mergeTransferRows.PositionedRow[] = []
801
892
  const seen = new Set<string>()
@@ -817,14 +908,6 @@ function mergeTransferRows(
817
908
  }
818
909
 
819
910
  declare namespace mergeTransferRows {
820
- type Options = {
821
- /** Maximum merged rows to return. */
822
- limit: number
823
- /** Number of globally ordered rows to skip. */
824
- offset: number
825
- /** Global transfer ordering. */
826
- order: 'asc' | 'desc'
827
- }
828
911
  type PositionedRow = {
829
912
  /** Transfer block number. */
830
913
  blockNumber: number
@@ -852,6 +935,8 @@ export declare namespace scan {
852
935
  }
853
936
  /** A page of scanned `Transfer` rows. */
854
937
  type Page = {
938
+ /** Whether the page came from a cap-truncated fallback sample; such a page must not advance a poller cursor. */
939
+ capped: boolean
855
940
  hasMore: boolean
856
941
  items: readonly Item[]
857
942
  limit: number
@@ -860,13 +945,15 @@ export declare namespace scan {
860
945
  }
861
946
 
862
947
  /**
863
- * Builds shared transfer filters. A token predicate targets `token_transfers`'s
864
- * physical `token` column; unscoped queries omit it and use the `Transfer` CTE.
948
+ * Builds shared transfer filters. Token-scoped queries target `token_transfers`;
949
+ * unscoped queries constrain the decoded `Transfer` emitter to TIP-20 contracts.
865
950
  */
866
951
  function filterClauses(options: filterClauses.Options): string[] {
867
952
  const { fromBlock, fromTimestamp, recipient, sender, toBlock, toTimestamp, token } = options
868
- const filters: string[] = []
869
- if (token !== undefined) filters.push(`token = '${token}'`)
953
+ const filters =
954
+ token === undefined
955
+ ? [`address >= '${tip20AddressRange[0]}'`, `address < '${tip20AddressRange[1]}'`]
956
+ : [`token = '${token}'`]
870
957
  if (sender !== undefined) filters.push(`"from" = '${sender}'`)
871
958
  if (recipient !== undefined) filters.push(`"to" = '${recipient}'`)
872
959
  if (fromBlock !== undefined) filters.push(`block_num >= ${fromBlock}`)
@@ -897,7 +984,7 @@ export function count(c: Context<App.Environment>, options: count.Options): Prom
897
984
 
898
985
  return Timing.time(c, timing, () =>
899
986
  Store.memoize(() => countScan({ tidx }, { chainId, ...rest }), {
900
- key: `transfers:count:v1:${chainId}:${rest.crossToken ? 'fold' : 'raw'}:${rest.token ?? ''}:${rest.eitherSide ?? ''}:${rest.sender ?? ''}:${rest.recipient ?? ''}:${rest.fromBlock ?? ''}:${rest.toBlock ?? ''}:${rest.fromTimestamp ?? ''}:${rest.toTimestamp ?? ''}`,
987
+ key: `transfers:count:v2:${chainId}:${rest.crossToken ? 'fold' : 'raw'}:${rest.token ?? ''}:${rest.eitherSide ?? ''}:${rest.sender ?? ''}:${rest.recipient ?? ''}:${rest.fromBlock ?? ''}:${rest.toBlock ?? ''}:${rest.fromTimestamp ?? ''}:${rest.toTimestamp ?? ''}`,
901
988
  store,
902
989
  ttl: Ttl.seconds(15),
903
990
  }),
@@ -946,40 +1033,54 @@ export async function countScan(
946
1033
  // rows, classify each transaction, and subtract the folded-away legs. Bounded
947
1034
  // by `countCap` like the raw count, so it stays within the indexer's budget.
948
1035
  if (options.crossToken) {
1036
+ // Only one-sided `"from"`/`"to"` equality shapes hit the planner's
1037
+ // deterministic top-N kill, so only they take the capped fallback.
1038
+ const sideFiltered =
1039
+ address !== undefined || options.sender !== undefined || options.recipient !== undefined
949
1040
  const fetchRows = async (side?: 'from' | 'to') => {
950
1041
  const sideFilters =
951
1042
  side !== undefined && address !== undefined
952
1043
  ? [`"${side}" = '${address}'`, ...filters]
953
1044
  : filters
954
1045
  const sideWhere = sideFilters.length > 0 ? `WHERE ${sideFilters.join(' AND ')}` : ''
955
- const result = await tidx.fetch({
956
- chainId,
957
- ...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
958
- query: `
959
- SELECT tx_hash, block_num, log_idx, "from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}
960
- FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
961
- ${sideWhere}
962
- ORDER BY block_num, log_idx
963
- LIMIT ${Schema.countCap}
964
- ` as string,
965
- })
966
- return result.rows
1046
+ const select = `tx_hash, block_num, log_idx, "from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}`
1047
+ const table = tokenScoped ? 'token_transfers' : 'Transfer'
1048
+ try {
1049
+ const result = await tidx.fetch({
1050
+ chainId,
1051
+ ...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
1052
+ query: `
1053
+ SELECT ${select}
1054
+ FROM ${table}
1055
+ ${sideWhere}
1056
+ ORDER BY block_num, log_idx
1057
+ LIMIT ${Schema.countCap}
1058
+ ` as string,
1059
+ })
1060
+ return { rows: result.rows, sample: false }
1061
+ } catch (error) {
1062
+ // Same planner kill as the page's side scan; the folded count is
1063
+ // already capped, so a plain capped scan keeps its semantics.
1064
+ if (!sideFiltered || !Tidx.isDeterministicError(error)) throw error
1065
+ const result = await tidx.fetch({
1066
+ chainId,
1067
+ ...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
1068
+ query: `
1069
+ SELECT ${select}
1070
+ FROM ${table}
1071
+ ${sideWhere}
1072
+ LIMIT ${Schema.countCap}
1073
+ ` as string,
1074
+ })
1075
+ return { rows: result.rows, sample: true }
1076
+ }
967
1077
  }
968
- const rows =
1078
+ const fetched =
969
1079
  address !== undefined
970
- ? mergeTransferRows((await Promise.all([fetchRows('from'), fetchRows('to')])).flat(), {
971
- limit: Schema.countCap,
972
- offset: 0,
973
- order: 'asc',
974
- })
975
- : await fetchRows()
976
- const rawCount = rows.length
977
- let suppressed = 0
978
- for (const legs of legsByTransaction(rows).values()) {
979
- const bundle = classifyCrossToken(legs)
980
- if (bundle) suppressed += bundle.suppressLogIndexes.length
981
- }
982
- return { totalCountCapped: rawCount >= Schema.countCap, totalCount: rawCount - suppressed }
1080
+ ? await Promise.all([fetchRows('from'), fetchRows('to')])
1081
+ : [await fetchRows()]
1082
+ const { capped, rows } = collate(fetched, { limit: Schema.countCap, offset: 0, order: 'asc' })
1083
+ return foldCount(rows, { capped })
983
1084
  }
984
1085
 
985
1086
  const countRows = async (additionalFilters: readonly string[] = []) => {
@@ -991,7 +1092,7 @@ export async function countScan(
991
1092
  query: `
992
1093
  SELECT count(*) AS total
993
1094
  FROM (
994
- SELECT 1
1095
+ SELECT DISTINCT tx_hash, log_idx
995
1096
  FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
996
1097
  ${countWhere}
997
1098
  LIMIT ${Schema.countCap}
@@ -1028,6 +1129,40 @@ export declare namespace countScan {
1028
1129
  }
1029
1130
  }
1030
1131
 
1132
+ /**
1133
+ * Folds a capped `Transfer` scan into the cross-token count. Classified
1134
+ * bundles subtract their folded-away legs; a cap-truncated sample can hold
1135
+ * partial bundles that defeat classification, so it subtracts every DEX and
1136
+ * fee leg instead, keeping the reported lower bound from overcounting.
1137
+ */
1138
+ export function foldCount(
1139
+ rows: readonly Record<string, unknown>[],
1140
+ options: foldCount.Options,
1141
+ ): count.Result {
1142
+ const { capped } = options
1143
+ let suppressed = 0
1144
+ for (const legs of legsByTransaction(rows).values()) {
1145
+ if (capped) {
1146
+ suppressed += legs.filter(isBundleLeg).length
1147
+ continue
1148
+ }
1149
+ const bundle = classifyCrossToken(legs)
1150
+ if (bundle) suppressed += bundle.suppressLogIndexes.length
1151
+ }
1152
+ return {
1153
+ totalCount: rows.length - suppressed,
1154
+ totalCountCapped: capped || rows.length >= Schema.countCap,
1155
+ }
1156
+ }
1157
+
1158
+ export declare namespace foldCount {
1159
+ /** Options for {@link foldCount}. */
1160
+ type Options = {
1161
+ /** Whether the rows include a cap-truncated fallback sample. */
1162
+ capped: boolean
1163
+ }
1164
+ }
1165
+
1031
1166
  /** `'tx1', 'tx2', …` SQL `IN` list of the page's distinct transaction hashes. */
1032
1167
  function txInList(rows: memoMap.Options['rows']): string {
1033
1168
  return [...new Set(rows.map((row) => row.transactionHash.toLowerCase()))]
@@ -1118,6 +1253,15 @@ type Leg = {
1118
1253
  to: z.output<typeof Schema.Address>
1119
1254
  }
1120
1255
 
1256
+ /** Whether a leg is cross-token bundle plumbing: a DEX leg or the fee leg. */
1257
+ function isBundleLeg(leg: Leg): boolean {
1258
+ return (
1259
+ Address.isEqual(leg.to, Addresses.stablecoinDex) ||
1260
+ Address.isEqual(leg.from, Addresses.stablecoinDex) ||
1261
+ Address.isEqual(leg.to, Addresses.feeManager)
1262
+ )
1263
+ }
1264
+
1121
1265
  /** The folded shape of a detected cross-token transfer, anchored on its delivery leg. */
1122
1266
  type CrossTokenBundle = {
1123
1267
  /** Log index of the delivery leg the transfer folds onto. */
@@ -1161,13 +1305,7 @@ function classifyCrossToken(legs: readonly Leg[]): CrossTokenBundle | undefined
1161
1305
  // Require a token change so plain same-token DEX activity is not folded.
1162
1306
  if (!delivery || Address.isEqual(toDex.address, delivery.address)) return undefined
1163
1307
  const suppressLogIndexes = legs
1164
- .filter(
1165
- (leg) =>
1166
- leg.logIndex !== delivery.logIndex &&
1167
- (Address.isEqual(leg.to, Addresses.stablecoinDex) ||
1168
- Address.isEqual(leg.from, Addresses.stablecoinDex) ||
1169
- Address.isEqual(leg.to, Addresses.feeManager)),
1170
- )
1308
+ .filter((leg) => leg.logIndex !== delivery.logIndex && isBundleLeg(leg))
1171
1309
  .map((leg) => leg.logIndex)
1172
1310
  return {
1173
1311
  deliveryLogIndex: delivery.logIndex,