tapimo 0.5.2 → 0.6.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 (299) hide show
  1. package/dist/App.d.ts +13987 -5755
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js.map +1 -1
  4. package/dist/admin/App.d.ts +910 -412
  5. package/dist/admin/App.d.ts.map +1 -1
  6. package/dist/admin/App.js +4 -0
  7. package/dist/admin/App.js.map +1 -1
  8. package/dist/admin/apps/early-access.d.ts +148 -0
  9. package/dist/admin/apps/early-access.d.ts.map +1 -0
  10. package/dist/admin/apps/early-access.js +133 -0
  11. package/dist/admin/apps/early-access.js.map +1 -0
  12. package/dist/admin/apps/verified-tokens.d.ts +643 -118
  13. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  14. package/dist/admin/apps/verified-tokens.js +214 -1
  15. package/dist/admin/apps/verified-tokens.js.map +1 -1
  16. package/dist/admin/ui.generated.d.ts +1 -1
  17. package/dist/admin/ui.generated.d.ts.map +1 -1
  18. package/dist/admin/ui.generated.js +45 -33
  19. package/dist/admin/ui.generated.js.map +1 -1
  20. package/dist/apps/data/App.d.ts +3733 -2428
  21. package/dist/apps/data/App.d.ts.map +1 -1
  22. package/dist/apps/data/App.js +11 -6
  23. package/dist/apps/data/App.js.map +1 -1
  24. package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
  25. package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
  26. package/dist/apps/data/FundingQuoteProviders.js +193 -2
  27. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  28. package/dist/apps/data/FxOracle.d.ts +71 -0
  29. package/dist/apps/data/FxOracle.d.ts.map +1 -0
  30. package/dist/apps/data/FxOracle.js +97 -0
  31. package/dist/apps/data/FxOracle.js.map +1 -0
  32. package/dist/apps/data/index.d.ts +1 -0
  33. package/dist/apps/data/index.d.ts.map +1 -1
  34. package/dist/apps/data/index.js +1 -0
  35. package/dist/apps/data/index.js.map +1 -1
  36. package/dist/apps/data/routes/activities.d.ts +3575 -1358
  37. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  38. package/dist/apps/data/routes/activities.js +230 -88
  39. package/dist/apps/data/routes/activities.js.map +1 -1
  40. package/dist/apps/data/routes/addresses.d.ts +963 -342
  41. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  42. package/dist/apps/data/routes/addresses.js +11 -9
  43. package/dist/apps/data/routes/addresses.js.map +1 -1
  44. package/dist/apps/data/routes/balances.d.ts +108 -57
  45. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  46. package/dist/apps/data/routes/balances.js +88 -45
  47. package/dist/apps/data/routes/balances.js.map +1 -1
  48. package/dist/apps/data/routes/coingecko.d.ts +169 -169
  49. package/dist/apps/data/routes/exchanges.d.ts +952 -201
  50. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  51. package/dist/apps/data/routes/exchanges.js +518 -76
  52. package/dist/apps/data/routes/exchanges.js.map +1 -1
  53. package/dist/apps/data/routes/fee-amm.d.ts +24 -24
  54. package/dist/apps/data/routes/funding.d.ts +8 -2
  55. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  56. package/dist/apps/data/routes/funding.js +32 -6
  57. package/dist/apps/data/routes/funding.js.map +1 -1
  58. package/dist/apps/data/routes/indexer.d.ts +5 -5
  59. package/dist/apps/data/routes/receipts.d.ts +392 -315
  60. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  61. package/dist/apps/data/routes/receipts.js +152 -77
  62. package/dist/apps/data/routes/receipts.js.map +1 -1
  63. package/dist/apps/data/routes/tokens.d.ts +166 -173
  64. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  65. package/dist/apps/data/routes/tokens.js +14 -12
  66. package/dist/apps/data/routes/tokens.js.map +1 -1
  67. package/dist/apps/data/routes/transactions.d.ts +277 -536
  68. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  69. package/dist/apps/data/routes/transactions.js +96 -77
  70. package/dist/apps/data/routes/transactions.js.map +1 -1
  71. package/dist/apps/data/routes/transfers.d.ts +82 -17
  72. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  73. package/dist/apps/data/routes/transfers.js +70 -13
  74. package/dist/apps/data/routes/transfers.js.map +1 -1
  75. package/dist/apps/data/routes/valuation.d.ts +307 -0
  76. package/dist/apps/data/routes/valuation.d.ts.map +1 -0
  77. package/dist/apps/data/routes/valuation.js +292 -0
  78. package/dist/apps/data/routes/valuation.js.map +1 -0
  79. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  80. package/dist/apps/data/routes/verified-tokens.js +7 -5
  81. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  82. package/dist/apps/data/routes/webhooks.d.ts +459 -479
  83. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  84. package/dist/apps/data/routes/webhooks.js +124 -25
  85. package/dist/apps/data/routes/webhooks.js.map +1 -1
  86. package/dist/apps/index.d.ts +1 -1
  87. package/dist/apps/index.d.ts.map +1 -1
  88. package/dist/apps/index.js +1 -1
  89. package/dist/apps/index.js.map +1 -1
  90. package/dist/apps/management/App.d.ts +8606 -1681
  91. package/dist/apps/management/App.d.ts.map +1 -1
  92. package/dist/apps/management/App.js +12 -1
  93. package/dist/apps/management/App.js.map +1 -1
  94. package/dist/apps/management/routes/api-keys.d.ts +23 -0
  95. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  96. package/dist/apps/management/routes/api-keys.js +5 -3
  97. package/dist/apps/management/routes/api-keys.js.map +1 -1
  98. package/dist/apps/management/routes/billing.d.ts +88 -48
  99. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  100. package/dist/apps/management/routes/billing.js +10 -2
  101. package/dist/apps/management/routes/billing.js.map +1 -1
  102. package/dist/apps/management/routes/faucet.d.ts +218 -0
  103. package/dist/apps/management/routes/faucet.d.ts.map +1 -0
  104. package/dist/apps/management/routes/faucet.js +139 -0
  105. package/dist/apps/management/routes/faucet.js.map +1 -0
  106. package/dist/apps/management/routes/invitations.d.ts +121 -121
  107. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  108. package/dist/apps/management/routes/invitations.js +3 -0
  109. package/dist/apps/management/routes/invitations.js.map +1 -1
  110. package/dist/apps/management/routes/invite-links.d.ts +197 -168
  111. package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
  112. package/dist/apps/management/routes/invite-links.js +26 -1
  113. package/dist/apps/management/routes/invite-links.js.map +1 -1
  114. package/dist/apps/management/routes/me.d.ts +2 -0
  115. package/dist/apps/management/routes/me.d.ts.map +1 -1
  116. package/dist/apps/management/routes/me.js +4 -0
  117. package/dist/apps/management/routes/me.js.map +1 -1
  118. package/dist/apps/management/routes/members.d.ts +49 -49
  119. package/dist/apps/management/routes/orgs.d.ts +91 -91
  120. package/dist/apps/management/routes/projects.d.ts +115 -115
  121. package/dist/apps/management/routes/usage.d.ts +35 -32
  122. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  123. package/dist/apps/management/routes/usage.js +4 -0
  124. package/dist/apps/management/routes/usage.js.map +1 -1
  125. package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
  126. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
  127. package/dist/apps/management/routes/verified-token-requests.js +271 -0
  128. package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
  129. package/dist/apps/management/routes/webhooks.d.ts +6977 -0
  130. package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
  131. package/dist/apps/management/routes/webhooks.js +573 -0
  132. package/dist/apps/management/routes/webhooks.js.map +1 -0
  133. package/dist/apps/mcp.d.ts +9 -2
  134. package/dist/apps/mcp.d.ts.map +1 -1
  135. package/dist/apps/mcp.js +9 -4
  136. package/dist/apps/mcp.js.map +1 -1
  137. package/dist/cli/Cli.d.ts.map +1 -1
  138. package/dist/cli/Cli.js +7 -2
  139. package/dist/cli/Cli.js.map +1 -1
  140. package/dist/db/Db.d.ts +4 -0
  141. package/dist/db/Db.d.ts.map +1 -1
  142. package/dist/db/Db.js +83 -0
  143. package/dist/db/Db.js.map +1 -1
  144. package/dist/db/Schema.d.ts +41 -0
  145. package/dist/db/Schema.d.ts.map +1 -1
  146. package/dist/db/Schema.js +45 -0
  147. package/dist/db/Schema.js.map +1 -1
  148. package/dist/db/tables/earlyAccess.d.ts +50 -0
  149. package/dist/db/tables/earlyAccess.d.ts.map +1 -0
  150. package/dist/db/tables/earlyAccess.js +63 -0
  151. package/dist/db/tables/earlyAccess.js.map +1 -0
  152. package/dist/db/tables/invitations.d.ts +12 -1
  153. package/dist/db/tables/invitations.d.ts.map +1 -1
  154. package/dist/db/tables/invitations.js +23 -0
  155. package/dist/db/tables/invitations.js.map +1 -1
  156. package/dist/db/tables/inviteLinks.d.ts +10 -2
  157. package/dist/db/tables/inviteLinks.d.ts.map +1 -1
  158. package/dist/db/tables/inviteLinks.js +10 -1
  159. package/dist/db/tables/inviteLinks.js.map +1 -1
  160. package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
  161. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  162. package/dist/db/tables/sponsoredTransactions.js +2 -0
  163. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  164. package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
  165. package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
  166. package/dist/db/tables/verifiedTokenRequests.js +120 -0
  167. package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
  168. package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
  169. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  170. package/dist/db/tables/webhookSubscriptions.js +16 -0
  171. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  172. package/dist/internal/Auth.d.ts +43 -2
  173. package/dist/internal/Auth.d.ts.map +1 -1
  174. package/dist/internal/Auth.js +60 -45
  175. package/dist/internal/Auth.js.map +1 -1
  176. package/dist/internal/FundingQuotes.d.ts +69 -15
  177. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  178. package/dist/internal/FundingQuotes.js +74 -25
  179. package/dist/internal/FundingQuotes.js.map +1 -1
  180. package/dist/internal/Log.d.ts +19 -1
  181. package/dist/internal/Log.d.ts.map +1 -1
  182. package/dist/internal/Log.js +2 -0
  183. package/dist/internal/Log.js.map +1 -1
  184. package/dist/internal/MetricSink.d.ts.map +1 -1
  185. package/dist/internal/MetricSink.js +9 -0
  186. package/dist/internal/MetricSink.js.map +1 -1
  187. package/dist/internal/OpenApi.d.ts +2 -2
  188. package/dist/internal/OpenApi.d.ts.map +1 -1
  189. package/dist/internal/OpenApi.js +3 -0
  190. package/dist/internal/OpenApi.js.map +1 -1
  191. package/dist/internal/Schema.d.ts +14 -5
  192. package/dist/internal/Schema.d.ts.map +1 -1
  193. package/dist/internal/Schema.js +23 -5
  194. package/dist/internal/Schema.js.map +1 -1
  195. package/dist/internal/Value.d.ts +18 -0
  196. package/dist/internal/Value.d.ts.map +1 -1
  197. package/dist/internal/Value.js +11 -1
  198. package/dist/internal/Value.js.map +1 -1
  199. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  200. package/dist/internal/VerifiedTokens.js +10 -1
  201. package/dist/internal/VerifiedTokens.js.map +1 -1
  202. package/dist/internal/Viem.d.ts +1 -1
  203. package/dist/internal/Webhooks.d.ts +24 -3
  204. package/dist/internal/Webhooks.d.ts.map +1 -1
  205. package/dist/internal/Webhooks.js +34 -8
  206. package/dist/internal/Webhooks.js.map +1 -1
  207. package/package.json +2 -2
  208. package/src/App.ts +9 -1
  209. package/src/Client.test-d.ts +638 -6
  210. package/src/admin/App.ts +4 -0
  211. package/src/admin/apps/early-access.test.ts +125 -0
  212. package/src/admin/apps/early-access.ts +175 -0
  213. package/src/admin/apps/verified-tokens.test.ts +201 -0
  214. package/src/admin/apps/verified-tokens.ts +532 -286
  215. package/src/admin/ui/plugin.ts +2 -1
  216. package/src/admin/ui/src/lib/early-access.ts +63 -0
  217. package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
  218. package/src/admin/ui/src/routeTree.gen.ts +33 -2
  219. package/src/admin/ui/src/routes/__root.tsx +2 -1
  220. package/src/admin/ui/src/routes/early-access.tsx +157 -0
  221. package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
  222. package/src/admin/ui.generated.ts +48 -33
  223. package/src/apps/data/App.ts +22 -14
  224. package/src/apps/data/FundingQuoteProviders.test.ts +298 -0
  225. package/src/apps/data/FundingQuoteProviders.ts +257 -4
  226. package/src/apps/data/FxOracle.test.ts +118 -0
  227. package/src/apps/data/FxOracle.ts +153 -0
  228. package/src/apps/data/index.ts +1 -0
  229. package/src/apps/data/routes/activities.test.ts +173 -3
  230. package/src/apps/data/routes/activities.ts +403 -132
  231. package/src/apps/data/routes/addresses.ts +19 -9
  232. package/src/apps/data/routes/balances.test.ts +144 -11
  233. package/src/apps/data/routes/balances.ts +140 -51
  234. package/src/apps/data/routes/exchanges.test.ts +540 -9
  235. package/src/apps/data/routes/exchanges.ts +690 -103
  236. package/src/apps/data/routes/funding.test.ts +84 -0
  237. package/src/apps/data/routes/funding.ts +38 -6
  238. package/src/apps/data/routes/receipts.test.ts +125 -23
  239. package/src/apps/data/routes/receipts.ts +205 -100
  240. package/src/apps/data/routes/tokens.test.ts +43 -0
  241. package/src/apps/data/routes/tokens.ts +27 -12
  242. package/src/apps/data/routes/transactions.test.ts +72 -19
  243. package/src/apps/data/routes/transactions.ts +162 -92
  244. package/src/apps/data/routes/transfers.test.ts +146 -4
  245. package/src/apps/data/routes/transfers.ts +91 -13
  246. package/src/apps/data/routes/valuation.test.ts +422 -0
  247. package/src/apps/data/routes/valuation.ts +423 -0
  248. package/src/apps/data/routes/verified-tokens.ts +14 -2
  249. package/src/apps/data/routes/webhooks.test.ts +56 -8
  250. package/src/apps/data/routes/webhooks.ts +144 -27
  251. package/src/apps/index.ts +1 -1
  252. package/src/apps/management/App.ts +13 -0
  253. package/src/apps/management/routes/api-keys.test.ts +87 -0
  254. package/src/apps/management/routes/api-keys.ts +4 -3
  255. package/src/apps/management/routes/billing.test.ts +112 -1
  256. package/src/apps/management/routes/billing.ts +7 -2
  257. package/src/apps/management/routes/faucet.test.ts +79 -0
  258. package/src/apps/management/routes/faucet.ts +185 -0
  259. package/src/apps/management/routes/invitations.test.ts +172 -0
  260. package/src/apps/management/routes/invitations.ts +3 -0
  261. package/src/apps/management/routes/invite-links.test.ts +117 -0
  262. package/src/apps/management/routes/invite-links.ts +43 -1
  263. package/src/apps/management/routes/me.test.ts +140 -88
  264. package/src/apps/management/routes/me.ts +6 -0
  265. package/src/apps/management/routes/usage.test.ts +6 -0
  266. package/src/apps/management/routes/usage.ts +7 -0
  267. package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
  268. package/src/apps/management/routes/verified-token-requests.ts +397 -0
  269. package/src/apps/management/routes/webhooks.test.ts +426 -0
  270. package/src/apps/management/routes/webhooks.ts +672 -0
  271. package/src/apps/mcp.test.ts +13 -0
  272. package/src/apps/mcp.ts +18 -5
  273. package/src/cli/Cli.test.ts +36 -0
  274. package/src/cli/Cli.ts +7 -2
  275. package/src/db/Db.test.ts +19 -0
  276. package/src/db/Db.ts +95 -0
  277. package/src/db/Schema.ts +53 -0
  278. package/src/db/tables/earlyAccess.ts +86 -0
  279. package/src/db/tables/invitations.ts +26 -1
  280. package/src/db/tables/inviteLinks.ts +22 -3
  281. package/src/db/tables/sponsoredTransactions.ts +4 -0
  282. package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
  283. package/src/db/tables/verifiedTokenRequests.ts +182 -0
  284. package/src/db/tables/webhookSubscriptions.ts +31 -0
  285. package/src/internal/Auth.ts +73 -45
  286. package/src/internal/FundingQuotes.test.ts +143 -1
  287. package/src/internal/FundingQuotes.ts +95 -28
  288. package/src/internal/Log.test-d.ts +21 -0
  289. package/src/internal/Log.test.ts +50 -0
  290. package/src/internal/Log.ts +24 -1
  291. package/src/internal/MetricSink.test.ts +160 -0
  292. package/src/internal/MetricSink.ts +10 -0
  293. package/src/internal/OpenApi.ts +3 -0
  294. package/src/internal/Schema.ts +50 -5
  295. package/src/internal/Value.test.ts +20 -0
  296. package/src/internal/Value.ts +24 -1
  297. package/src/internal/VerifiedTokens.ts +9 -1
  298. package/src/internal/Webhooks.test.ts +140 -0
  299. package/src/internal/Webhooks.ts +53 -9
@@ -239,6 +239,12 @@ export namespace schema {
239
239
  'A compact TIP-20 token reference with identity and display fields only.',
240
240
  )
241
241
 
242
+ /** RPC token metadata embedded in resources, with optional curated fields. */
243
+ export const TokenReference = Schema.describe(
244
+ z.partial(z.omit(Token, { id: true }), { logoUri: true, verified: true }),
245
+ 'A compact TIP-20 token reference with RPC metadata and optional enrichments.',
246
+ )
247
+
242
248
  /** Schemas for the getTokenBySymbol operation. */
243
249
  export namespace getTokenBySymbol {
244
250
  /** Hono route pattern for verified token symbols. */
@@ -458,9 +464,9 @@ export namespace schema {
458
464
  })
459
465
  .check(z.describe('Path parameters for listing transactions involving this token contract.'))
460
466
 
461
- /** Optional related resources that callers opt into via `include`. */
467
+ /** Optional scoped-token resources and fee-token fields requested via `include`. */
462
468
  export const Include = z
463
- .enum(['feeToken', 'token', 'totalCount'])
469
+ .enum(['feeToken.logoUri', 'feeToken.verified', 'token', 'totalCount'])
464
470
  .check(z.describe('Related resources to include only when you request them with `include`.'))
465
471
 
466
472
  /**
@@ -470,7 +476,7 @@ export namespace schema {
470
476
  */
471
477
  export const includeQuery = Schema.includeQuery(
472
478
  Include,
473
- 'Comma-separated related resources to include, such as `feeToken,token,totalCount`.',
479
+ 'Comma-separated related resources to include, such as `feeToken.logoUri,feeToken.verified,token,totalCount`.',
474
480
  )
475
481
 
476
482
  /** Query parameters for token transaction requests. */
@@ -974,17 +980,22 @@ export function tokens() {
974
980
  const { token: address } = c.req.valid('param')
975
981
  const query = c.req.valid('query')
976
982
 
977
- // Cursor pages are anchored below the head and effectively immutable,
978
- // so cache them aggressively; the head page keeps the `feed` default.
979
- if (query.cursor !== undefined) Cache.setPolicy(c, Cache.policies.immutable)
983
+ // Cursor pages are anchored below the head. Cache chain data as immutable,
984
+ // but refresh requested token metadata on the metadata schedule.
985
+ if (query.cursor !== undefined)
986
+ Cache.setPolicy(
987
+ c,
988
+ query.include.some(
989
+ (resource) => resource === 'token' || resource.startsWith('feeToken.'),
990
+ )
991
+ ? Cache.policies.metadata
992
+ : Cache.policies.immutable,
993
+ )
980
994
 
981
995
  try {
982
996
  const chainId = query.chainId ?? c.get('chainId')
983
- // Resolve the transaction page and (opt-in) token metadata
984
- // concurrently so the token lookup is hidden behind the
985
- // transaction query rather than adding to it. The `feeToken`
986
- // include flows through to `listTransactions` for per-row embeds;
987
- // `token` is a root-level embed for the token being scoped to.
997
+ // Resolve the transaction page and scoped token concurrently so the
998
+ // token lookup is hidden behind the transaction query.
988
999
  const includeToken = query.include.includes('token')
989
1000
  // `totalCount` is opt-in; count transactions touching this token
990
1001
  // (address-scoped to the contract) under the same filters as the
@@ -1010,7 +1021,10 @@ export function tokens() {
1010
1021
  feeToken: query.feeToken,
1011
1022
  fromBlock: query['blockNumber.from'],
1012
1023
  fromTimestamp: query['timestamp.from'],
1013
- include: query.include.includes('feeToken') ? ['feeToken'] : [],
1024
+ include: query.include.filter(
1025
+ (resource): resource is 'feeToken.logoUri' | 'feeToken.verified' =>
1026
+ resource === 'feeToken.logoUri' || resource === 'feeToken.verified',
1027
+ ),
1014
1028
  limit: query.limit,
1015
1029
  order: query.order,
1016
1030
  page: query.page,
@@ -1176,6 +1190,7 @@ export function getTokenMetadata(c: Context<App.Environment>, options: getTokenM
1176
1190
 
1177
1191
  return Store.memoize(
1178
1192
  async () => {
1193
+ // TODO(viem): Flatten concurrent metadata multicalls before encoding aggregate3 calldata.
1179
1194
  const fresh = await getClient(chainId).token.getMetadata({ token: address as Address })
1180
1195
  return {
1181
1196
  currency: fresh.currency,
@@ -21,7 +21,27 @@ beforeAll(async () => {
21
21
  })
22
22
 
23
23
  describe('GET /transactions/:transactionHash', () => {
24
- test('humanizes the transaction, keeps the raw RPC under `meta.rpc`, and embeds the fee token on demand', async () => {
24
+ test('parses granular fee-token includes', () => {
25
+ expect(
26
+ Transactions.schema.getTransaction.Query.parse({
27
+ include: 'feeToken.logoUri,feeToken.verified,receipt',
28
+ }).include,
29
+ ).toMatchInlineSnapshot(`
30
+ [
31
+ "feeToken.logoUri",
32
+ "feeToken.verified",
33
+ "receipt",
34
+ ]
35
+ `)
36
+ expect(
37
+ Transactions.schema.getTransaction.Query.safeParse({ include: 'feeToken' }).success,
38
+ ).toBe(false)
39
+ expect(
40
+ Transactions.schema.getTransaction.Query.safeParse({ include: 'token.logoUri' }).success,
41
+ ).toBe(false)
42
+ })
43
+
44
+ test('humanizes the transaction and resolves its fee token', async () => {
25
45
  if (!transactionHash) {
26
46
  console.warn('[live-rpc] no recent transaction found; skipping transaction happy path')
27
47
  return
@@ -51,12 +71,24 @@ describe('GET /transactions/:transactionHash', () => {
51
71
  // transaction; they live on `/transactions/:transactionHash/receipt` instead.
52
72
  expect('status' in body).toBe(false)
53
73
  expect('gasUsed' in body).toBe(false)
54
- // Fee-token metadata is opt-in: the base response must not embed it.
55
- expect(body.meta.feeToken).toBeUndefined()
74
+ if (body.feeToken) {
75
+ expect(body.feeToken.address).toBe(body.meta.rpc.feeToken)
76
+ expect(typeof body.feeToken.currency).toBe('string')
77
+ expect(Number.isInteger(body.feeToken.decimals)).toBe(true)
78
+ expect(typeof body.feeToken.name).toBe('string')
79
+ expect(typeof body.feeToken.symbol).toBe('string')
80
+ expect(body.feeToken.logoUri).toBeUndefined()
81
+ expect(body.feeToken.verified).toBeUndefined()
82
+ expect(body.feeToken).not.toHaveProperty('amount')
83
+ expect(body.feeToken).not.toHaveProperty('id')
84
+ }
56
85
 
57
- // include=feeToken: resolved metadata is embedded under meta (best-effort).
86
+ // Curated fields are independently opt-in on the fee-token reference.
58
87
  const includedResponse = await client.v1.transactions[':transactionHash'].$get(
59
- { param: { transactionHash: transactionHash }, query: { include: 'feeToken' } },
88
+ {
89
+ param: { transactionHash: transactionHash },
90
+ query: { include: 'feeToken.logoUri,feeToken.verified' },
91
+ },
60
92
  TestApp.auth,
61
93
  )
62
94
  const includedBody = await TestApp.json(
@@ -65,12 +97,8 @@ describe('GET /transactions/:transactionHash', () => {
65
97
  )
66
98
  expect(includedResponse.status).toBe(200)
67
99
  if (includedBody.feeToken) {
68
- expect(includedBody.feeToken).toMatch(/^0x[0-9a-f]{40}$/)
69
- // When metadata resolves, it must describe the fee token itself.
70
- if (includedBody.meta?.feeToken) {
71
- expect(includedBody.meta.feeToken.address).toBe(includedBody.feeToken)
72
- expect(typeof includedBody.meta.feeToken.symbol).toBe('string')
73
- }
100
+ expect(includedBody.feeToken.address).toBe(includedBody.meta.rpc.feeToken)
101
+ expect(typeof includedBody.feeToken.verified).toBe('boolean')
74
102
  }
75
103
  })
76
104
 
@@ -132,7 +160,28 @@ describe('GET /transactions/:transactionHash', () => {
132
160
  })
133
161
 
134
162
  describe('GET /transactions', () => {
135
- test('returns a humanized transaction page, embedding fee tokens on demand', async () => {
163
+ test('parses granular fee-token and list includes', () => {
164
+ expect(
165
+ Transactions.schema.getTransactions.Query.parse({
166
+ include: 'feeToken.logoUri,feeToken.verified,receipt,totalCount',
167
+ }).include,
168
+ ).toMatchInlineSnapshot(`
169
+ [
170
+ "feeToken.logoUri",
171
+ "feeToken.verified",
172
+ "receipt",
173
+ "totalCount",
174
+ ]
175
+ `)
176
+ expect(
177
+ Transactions.schema.getTransactions.Query.safeParse({ include: 'feeToken' }).success,
178
+ ).toBe(false)
179
+ expect(
180
+ Transactions.schema.getTransactions.Query.safeParse({ include: 'token.verified' }).success,
181
+ ).toBe(false)
182
+ })
183
+
184
+ test('returns a humanized transaction page with fee-token references', async () => {
136
185
  const client = TestApp.client()
137
186
  const response = await client.v1.transactions.$get({ query: { limit: '5' } }, TestApp.auth)
138
187
  const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
@@ -169,13 +218,18 @@ describe('GET /transactions', () => {
169
218
  expect(typeof transaction!.nonce).toBe('number')
170
219
  expect(transaction!.meta.rpc.type).toMatch(/^0x[0-9a-f]+$/)
171
220
  // Receipt-derived fields are not on the list payload, matching the detail
172
- // endpoint; fee-token metadata is opt-in.
221
+ // endpoint; fee-token core metadata is included by default.
173
222
  expect('status' in transaction!).toBe(false)
174
- expect(transaction!.meta.feeToken).toBeUndefined()
223
+ if (transaction!.feeToken) {
224
+ expect(transaction!.feeToken.address).toBe(transaction!.meta.rpc.feeToken)
225
+ expect(typeof transaction!.feeToken.symbol).toBe('string')
226
+ expect(transaction!.feeToken.logoUri).toBeUndefined()
227
+ expect(transaction!.feeToken.verified).toBeUndefined()
228
+ }
175
229
  }
176
230
 
177
231
  const includedResponse = await client.v1.transactions.$get(
178
- { query: { limit: '5', include: 'feeToken' } },
232
+ { query: { limit: '5', include: 'feeToken.logoUri,feeToken.verified' } },
179
233
  TestApp.auth,
180
234
  )
181
235
  const includedBody = await TestApp.json(
@@ -183,11 +237,10 @@ describe('GET /transactions', () => {
183
237
  Transactions.schema.getTransactions.Response,
184
238
  )
185
239
  expect(includedResponse.status).toBe(200)
186
- const withFeeToken = includedBody.data.find((transaction) => transaction.meta?.feeToken)
240
+ const withFeeToken = includedBody.data.find((transaction) => transaction.feeToken)
187
241
  if (withFeeToken) {
188
- // When metadata resolves, it must describe the transaction's fee token.
189
- expect(withFeeToken.meta.feeToken!.address).toBe(withFeeToken.feeToken)
190
- expect(typeof withFeeToken.meta.feeToken!.symbol).toBe('string')
242
+ expect(withFeeToken.feeToken!.address).toBe(withFeeToken.meta.rpc.feeToken)
243
+ expect(typeof withFeeToken.feeToken!.verified).toBe('boolean')
191
244
  }
192
245
  })
193
246
 
@@ -286,20 +286,9 @@ export namespace schema {
286
286
  .check(z.describe('The transaction exactly as returned by JSON-RPC.'))
287
287
  }
288
288
 
289
- /**
290
- * Metadata for a transaction: the verbatim JSON-RPC transaction (always
291
- * present) plus resources embedded on demand via `include`.
292
- *
293
- * The `feeToken` and `receipt` schemas are wrapped in `z.lazy` because
294
- * `tokens.ts`/`receipts.ts` and `transactions.ts` mutually reference each
295
- * other's schemas; deferring these lookups avoids the import-cycle crash
296
- * during module init.
297
- */
289
+ /** Transaction metadata with the raw RPC transaction and optional receipt. */
298
290
  export const TransactionMeta = z
299
291
  .object({
300
- feeToken: z
301
- .optional(z.lazy(() => Tokens.schema.getToken.Response))
302
- .check(z.describe('Fee-token details included only when you request `include=feeToken`.')),
303
292
  receipt: z
304
293
  .optional(z.lazy(() => Receipts.schema.Receipt))
305
294
  .check(
@@ -367,10 +356,10 @@ export namespace schema {
367
356
  z.meta({ examples: [4217] }),
368
357
  ),
369
358
  feeToken: z
370
- .optional(Schema.Address)
359
+ .optional(z.lazy(() => Tokens.schema.TokenReference))
371
360
  .check(
372
361
  z.describe(
373
- 'The fee token requested by the transaction; Tempo fees are paid in USD stablecoins such as `pathUSD`.',
362
+ 'The token requested for transaction fees, with RPC metadata and optional curated fields.',
374
363
  ),
375
364
  ),
376
365
  gas: z
@@ -484,20 +473,19 @@ export namespace schema {
484
473
  })
485
474
  .check(z.describe('Path parameters used to look up a transaction.'))
486
475
 
487
- /** Optional related resource that callers opt into via `include`. */
476
+ /** Optional receipt and fee-token fields that callers opt into via `include`. */
488
477
  export const Include = z
489
- .enum(['feeToken', 'receipt'])
490
- .check(z.describe('Related resource you can opt into with the `include` query parameter.'))
478
+ .enum(['feeToken.logoUri', 'feeToken.verified', 'receipt'])
479
+ .check(z.describe('Related resources you can request with the `include` query parameter.'))
491
480
 
492
481
  /**
493
482
  * Parses a comma-separated `include` query value into a list of optional
494
- * resources to embed. Extra lookups (e.g. fee-token metadata, the receipt)
495
- * only run when explicitly requested, keeping the base transaction response
496
- * fast.
483
+ * resources to embed. Curated fee-token fields and the receipt only run when
484
+ * explicitly requested.
497
485
  */
498
486
  export const includeQuery = Schema.includeQuery(
499
487
  Include,
500
- 'Comma-separated related resources to include, such as `feeToken,receipt`.',
488
+ 'Comma-separated related resources to include, such as `feeToken.logoUri,feeToken.verified,receipt`.',
501
489
  )
502
490
 
503
491
  /** Query parameters for transaction requests. */
@@ -515,12 +503,11 @@ export namespace schema {
515
503
  /** Schemas for the getTransactions (list) operation. */
516
504
  export namespace getTransactions {
517
505
  /**
518
- * Optional resources for the transaction list: the per-row `feeToken` /
519
- * `receipt` embeds plus the response-wide capped `totalCount`. The detail
520
- * route keeps its own {@link getTransaction.Include}.
506
+ * Optional resources for transaction rows plus the response-wide capped
507
+ * `totalCount`. The detail route keeps its own include schema.
521
508
  */
522
509
  export const Include = z
523
- .enum(['feeToken', 'receipt', 'totalCount'])
510
+ .enum(['feeToken.logoUri', 'feeToken.verified', 'receipt', 'totalCount'])
524
511
  .check(
525
512
  z.describe('Related resources to include in each transaction row or in response metadata.'),
526
513
  )
@@ -528,7 +515,7 @@ export namespace schema {
528
515
  /** Parses the comma-separated `include` value for the transaction list. */
529
516
  export const includeQuery = Schema.includeQuery(
530
517
  Include,
531
- 'Comma-separated related resources to include, such as `feeToken,receipt,totalCount`.',
518
+ 'Comma-separated related resources to include, such as `feeToken.logoUri,feeToken.verified,receipt,totalCount`.',
532
519
  )
533
520
 
534
521
  /** Query parameters for transaction list requests. */
@@ -606,7 +593,7 @@ export function transactions() {
606
593
  'List transactions across Tempo, with filters for addresses, blocks, timestamps, fees, and status.',
607
594
  operationId: 'getTransactions',
608
595
  responses: OpenApi.responses({
609
- errors: { 502: 'Could not read indexed transaction data from TIDX.' },
596
+ errors: { 502: 'Could not read transaction or token data from an upstream service.' },
610
597
  success: {
611
598
  description: 'A page of transactions.',
612
599
  schema: schema.getTransactions.Response,
@@ -631,10 +618,15 @@ export function transactions() {
631
618
  const query = c.req.valid('query')
632
619
  const chainId = query.chainId ?? c.get('chainId')
633
620
 
634
- // A cursor page is anchored below the head, so its contents are
635
- // append-only and effectively immutable; cache it aggressively. The head
636
- // page (no cursor) keeps the route's short `feed` default.
637
- if (query.cursor !== undefined) Cache.setPolicy(c, Cache.policies.immutable)
621
+ // Cursor pages are anchored below the head. Cache chain data as immutable,
622
+ // but refresh requested curated token fields on the metadata schedule.
623
+ if (query.cursor !== undefined)
624
+ Cache.setPolicy(
625
+ c,
626
+ query.include.some((resource) => resource.startsWith('feeToken.'))
627
+ ? Cache.policies.metadata
628
+ : Cache.policies.immutable,
629
+ )
638
630
 
639
631
  try {
640
632
  // `totalCount` is opt-in and shares the page's filters; run it
@@ -711,7 +703,7 @@ export function transactions() {
711
703
  description: 'No transaction was found for that hash.',
712
704
  codes: ['transaction_not_found'],
713
705
  },
714
- 502: 'Could not read transaction data from the upstream JSON-RPC node.',
706
+ 502: 'Could not read transaction or token data from upstream JSON-RPC.',
715
707
  },
716
708
  success: {
717
709
  description: 'A single transaction with decoded fields and original JSON-RPC data.',
@@ -769,9 +761,15 @@ export function transactions() {
769
761
 
770
762
  let data = humanizeTransaction(Response.validated(schema.Rpc.Transaction, rpc))
771
763
 
772
- // A mined transaction is immutable, so it can be cached aggressively; a
773
- // pending one (no block yet) keeps the route's default no-store policy.
774
- if (data.blockNumber !== null) Cache.setPolicy(c, Cache.policies.immutable)
764
+ // Mined chain data is immutable, while curated token fields can still
765
+ // change. Pending transactions keep the route's no-store policy.
766
+ if (data.blockNumber !== null)
767
+ Cache.setPolicy(
768
+ c,
769
+ query.include.some((resource) => resource.startsWith('feeToken.'))
770
+ ? Cache.policies.metadata
771
+ : Cache.policies.immutable,
772
+ )
775
773
 
776
774
  // The receipt is opt-in: fetching it is an extra RPC, so it only runs
777
775
  // when requested via `include=receipt` and the transaction is mined.
@@ -784,22 +782,24 @@ export function transactions() {
784
782
  const humanized = Receipts.humanizeReceipt(
785
783
  Response.validated(Receipts.schema.Rpc.Receipt, receipt),
786
784
  )
787
- data = { ...data, meta: { ...data.meta, receipt: humanized } }
785
+ const [receipt_enriched] = await enrichFeeTokens(c, {
786
+ chainId,
787
+ include: query.include,
788
+ rows: [humanized],
789
+ })
790
+ if (!receipt_enriched) throw new Error('Unable to enrich transaction receipt')
791
+ data = { ...data, meta: { ...data.meta, receipt: receipt_enriched } }
788
792
  }
789
793
  }
790
794
 
791
- // Fee-token metadata is opt-in: resolving it is an extra RPC, so it
792
- // only runs when requested via `include=feeToken` and the transaction
793
- // declares a fee token (Tempo AA transactions).
794
- const feeToken = data.feeToken
795
- if (query.include.includes('feeToken') && feeToken !== undefined) {
796
- const token = await Timing.time(c, 'fee_token', () =>
797
- Tokens.resolveToken(c, { address: feeToken, chainId }).catch(() => undefined),
798
- )
799
- if (token) data = { ...data, meta: { ...data.meta, feeToken: token } }
800
- }
795
+ const [data_enriched] = await enrichFeeTokens(c, {
796
+ chainId,
797
+ include: query.include,
798
+ rows: [data],
799
+ })
800
+ if (!data_enriched) throw new Error('Unable to enrich transaction')
801
801
 
802
- return c.json(Response.validated(schema.getTransaction.Response, data), 200)
802
+ return c.json(Response.validated(schema.getTransaction.Response, data_enriched), 200)
803
803
  } catch (cause) {
804
804
  return Response.upstream(c, cause)
805
805
  }
@@ -808,10 +808,9 @@ export function transactions() {
808
808
  }
809
809
 
810
810
  /**
811
- * Lists humanized transactions and embeds opt-in `include` resources
812
- * (`feeToken`, `receipt`). A `status` filter routes the page through the
813
- * receipts list instead of the `txs` scan, since execution status lives on
814
- * receipts.
811
+ * Lists humanized transactions with required fee-token metadata and opt-in
812
+ * resources. Status filters page through receipts because execution status
813
+ * lives there.
815
814
  */
816
815
  export async function listTransactions(
817
816
  c: Context<App.Environment>,
@@ -858,6 +857,7 @@ export async function listTransactions(
858
857
  if (include.includes('receipt') && data.length > 0) {
859
858
  const receipts = await Receipts.embedReceipts(c, {
860
859
  chainId: rest.chainId,
860
+ include,
861
861
  receipts: receiptPage?.data,
862
862
  rows: data,
863
863
  })
@@ -866,13 +866,15 @@ export async function listTransactions(
866
866
  return receipt ? { ...row, meta: { ...row.meta, receipt } } : row
867
867
  })
868
868
  }
869
- if (include.includes('feeToken')) data = await embedFeeTokens(c, data, rest.chainId)
870
- return { ...page, data }
869
+ return {
870
+ ...page,
871
+ data: await enrichFeeTokens(c, { chainId: rest.chainId, include, rows: data }),
872
+ }
871
873
  }
872
874
 
873
875
  export declare namespace listTransactions {
874
876
  type Options = getTransactions.Options & {
875
- /** Optional related resources to embed (e.g. `feeToken`, `receipt`). */
877
+ /** Optional receipt and curated token fields to embed. */
876
878
  include: readonly z.output<typeof schema.getTransaction.Include>[]
877
879
  }
878
880
  }
@@ -885,11 +887,8 @@ export declare namespace listTransactions {
885
887
  */
886
888
  async function transactionsForReceipts(
887
889
  c: Context<App.Environment>,
888
- options: {
889
- chainId: z.output<typeof Schema.ChainId>
890
- receipts: readonly z.output<typeof Receipts.schema.Receipt>[]
891
- },
892
- ): Promise<z.output<typeof schema.Transaction>[]> {
890
+ options: transactionsForReceipts.Options,
891
+ ): Promise<humanizeTransaction.Output[]> {
893
892
  const { chainId, receipts } = options
894
893
  if (receipts.length === 0) return []
895
894
  const tidx = c.get('getTidx')(chainId)
@@ -907,7 +906,7 @@ async function transactionsForReceipts(
907
906
  }),
908
907
  )
909
908
 
910
- const byHash = new Map<string, z.output<typeof schema.Transaction>>()
909
+ const byHash = new Map<string, humanizeTransaction.Output>()
911
910
  for (const row of result.rows) {
912
911
  const rpc = schema.Rpc.Transaction.safeParse(toRpcTransaction(row))
913
912
  if (rpc.success) byHash.set(rpc.data.hash.toLowerCase(), humanizeTransaction(rpc.data))
@@ -917,47 +916,110 @@ async function transactionsForReceipts(
917
916
  .filter((transaction) => transaction !== undefined)
918
917
  }
919
918
 
920
- /**
921
- * Resolves fee-token metadata for each row and embeds it under `meta.feeToken`,
922
- * sharing a single verified-token fetch. Best-effort: a row whose fee token
923
- * can't be resolved (or that has none) is returned unchanged. Shared by the
924
- * transaction and receipt list endpoints (and their address-scoped variants),
925
- * whose rows all carry an optional `feeToken` address and a `meta` object.
926
- */
927
- export async function embedFeeTokens<
928
- row extends { feeToken?: Hex.Hex | undefined; meta: Record<string, unknown> },
929
- >(c: Context<App.Environment>, rows: row[], chainId: z.output<typeof Schema.ChainId>) {
930
- if (rows.length === 0) return rows
931
- const uniqueTokens = Array.from(
932
- new Set(
933
- rows.map((row) => row.feeToken).filter((address): address is Hex.Hex => address != null),
919
+ declare namespace transactionsForReceipts {
920
+ /** Inputs for matching receipt rows to transactions. */
921
+ type Options = {
922
+ /** Chain used to fetch matching transaction rows. */
923
+ chainId: z.output<typeof Schema.ChainId>
924
+ /** Receipt positions and hashes in page order. */
925
+ receipts: readonly Receipt[]
926
+ }
927
+
928
+ /** Receipt fields needed for transaction hydration. */
929
+ type Receipt = {
930
+ /** Block containing the receipt. */
931
+ blockNumber: number
932
+ /** Hash of the transaction that produced the receipt. */
933
+ transactionHash: string
934
+ }
935
+ }
936
+
937
+ /** Resolves required fee-token metadata and optional curated fields without mutating input rows. */
938
+ export async function enrichFeeTokens<const row extends object>(
939
+ c: Context<App.Environment>,
940
+ options: enrichFeeTokens.Options<row>,
941
+ ): Promise<enrichFeeTokens.Output<row>[]> {
942
+ const { chainId, include, rows } = options
943
+ const includeLogoUri = include.includes('feeToken.logoUri')
944
+ const includeVerified = include.includes('feeToken.verified')
945
+ const uniqueTokens = [
946
+ ...new Set(
947
+ rows
948
+ .map((row) => row.feeToken)
949
+ .filter((address): address is z.output<typeof Schema.Address> => address !== undefined)
950
+ .map((address) => address.toLowerCase())
951
+ .map((address) => Schema.Address.parse(address)),
934
952
  ),
935
- )
936
- if (uniqueTokens.length === 0) return rows
953
+ ]
954
+ if (uniqueTokens.length === 0)
955
+ return rows.map((row) => ({ ...row }) as enrichFeeTokens.Output<row>)
937
956
 
938
- const snapshot = await VerifiedTokens.snapshot(c, chainId)
957
+ const snapshot =
958
+ includeLogoUri || includeVerified
959
+ ? await VerifiedTokens.snapshot(c, chainId).catch(() => undefined)
960
+ : undefined
939
961
  const resolved = await Timing.time(c, 'tokens', () =>
940
962
  Promise.all(
941
- uniqueTokens.map((token) =>
942
- Tokens.resolveToken(c, {
943
- address: token,
944
- chainId,
945
- snapshot,
946
- })
947
- .then((metadata) => [token, metadata] as const)
948
- .catch(() => [token, undefined] as const),
949
- ),
963
+ uniqueTokens.map(async (address) => {
964
+ const [logoUri, metadata] = await Promise.all([
965
+ includeLogoUri
966
+ ? Tokens.getTokenLogo(c, { address, chainId }).catch(() => undefined)
967
+ : Promise.resolve(undefined),
968
+ Tokens.getTokenMetadata(c, { address, chainId }),
969
+ ])
970
+ return [
971
+ address,
972
+ {
973
+ address,
974
+ currency: metadata.currency,
975
+ decimals: metadata.decimals,
976
+ ...(includeLogoUri
977
+ ? {
978
+ logoUri: logoUri ?? snapshot?.byAddress.get(address)?.logoUri ?? metadata.logoUri,
979
+ }
980
+ : {}),
981
+ name: metadata.name,
982
+ symbol: metadata.symbol,
983
+ ...(includeVerified && snapshot ? { verified: snapshot.byAddress.has(address) } : {}),
984
+ },
985
+ ] as const
986
+ }),
950
987
  ),
951
988
  )
952
989
  const tokensByAddress = new Map(resolved)
953
990
  return rows.map((row) => {
954
- const token = row.feeToken == null ? undefined : tokensByAddress.get(row.feeToken)
955
- // The runtime shape stays `row`; the generic object spread just can't prove
956
- // the override keeps it assignable to `row`.
957
- return (token ? { ...row, meta: { ...row.meta, feeToken: token } } : row) as row
991
+ if (row.feeToken === undefined) return { ...row } as enrichFeeTokens.Output<row>
992
+ const address = Schema.Address.parse(row.feeToken.toLowerCase())
993
+ const feeToken = tokensByAddress.get(address)
994
+ if (!feeToken) throw new Error('Unable to resolve fee token metadata')
995
+ return { ...row, feeToken } as enrichFeeTokens.Output<row>
958
996
  })
959
997
  }
960
998
 
999
+ export declare namespace enrichFeeTokens {
1000
+ /** Fee-token enrichment inputs. */
1001
+ type Options<row extends object> = {
1002
+ /** Chain used for token metadata resolution. */
1003
+ chainId: z.output<typeof Schema.ChainId>
1004
+ /** Requested optional token fields. */
1005
+ include: readonly string[]
1006
+ /** Rows carrying unresolved fee-token addresses. */
1007
+ rows: readonly (row & Unresolved)[]
1008
+ }
1009
+
1010
+ /** A copied row whose fee-token address has been replaced by metadata. */
1011
+ type Output<row> = Omit<row, 'feeToken'> & {
1012
+ /** Resolved fee-token metadata when the row declares a fee token. */
1013
+ feeToken?: z.output<typeof Tokens.schema.TokenReference> | undefined
1014
+ }
1015
+
1016
+ /** Row field before fee-token metadata resolution. */
1017
+ type Unresolved = {
1018
+ /** Fee-token address from indexed or RPC data. */
1019
+ feeToken?: z.output<typeof Schema.Address> | undefined
1020
+ }
1021
+ }
1022
+
961
1023
  export function getTransactions(c: Context<App.Environment>, options: getTransactions.Options) {
962
1024
  const { chainId } = options
963
1025
  const store = c.get('store')
@@ -1263,7 +1325,7 @@ export declare namespace scanTransactions {
1263
1325
  /** Keyset position of the row. */
1264
1326
  cursor: readonly [blockNumber: number, transactionIndex: number]
1265
1327
  /** The humanized transaction (same shape the read endpoint returns). */
1266
- data: z.output<typeof schema.Transaction>
1328
+ data: humanizeTransaction.Output
1267
1329
  }
1268
1330
  /** A page of scanned transactions. */
1269
1331
  type Page = {
@@ -1334,7 +1396,7 @@ function humanizeCall(call: z.output<typeof schema.Call>): z.output<typeof schem
1334
1396
  */
1335
1397
  export function humanizeTransaction(
1336
1398
  rpc: z.output<typeof schema.Rpc.Transaction>,
1337
- ): z.output<typeof schema.Transaction> {
1399
+ ): humanizeTransaction.Output {
1338
1400
  return {
1339
1401
  blockHash: rpc.blockHash,
1340
1402
  blockNumber: Value.hexToNumber(rpc.blockNumber) ?? null,
@@ -1361,3 +1423,11 @@ export function humanizeTransaction(
1361
1423
  value: Value.fromHex(rpc.value) ?? '0',
1362
1424
  }
1363
1425
  }
1426
+
1427
+ export declare namespace humanizeTransaction {
1428
+ /** Humanized fields before asynchronous fee-token metadata resolution. */
1429
+ type Output = Omit<z.output<typeof schema.Transaction>, 'feeToken'> & {
1430
+ /** Unresolved fee-token address from the RPC transaction. */
1431
+ feeToken?: z.output<typeof Schema.Address> | undefined
1432
+ }
1433
+ }