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
@@ -14,14 +14,13 @@ import * as Store from '../../../internal/Store.js'
14
14
  import * as Timing from '../../../internal/Timing.js'
15
15
  import * as Ttl from '../../../internal/Ttl.js'
16
16
  import * as Value from '../../../internal/Value.js'
17
+ import * as FxOracle from '../FxOracle.js'
17
18
  import * as Tokens from './tokens.js'
19
+ import * as Transactions from './transactions.js'
20
+ import * as Valuation from './valuation.js'
18
21
 
19
- // Receipts and transactions deliberately form an import cycle (receipts reuses
20
- // the transaction-type decoder and fee-token embedder; transactions embeds
21
- // receipts). Neither module may touch the other's exports during module
22
- // evaluation: schema references across the cycle must stay inside `z.lazy`, and
23
- // function references are call-time only.
24
- import { decodeTransactionType, embedFeeTokens, schema as txSchema } from './transactions.js'
22
+ // Receipts and transactions import each other. Cross-module schemas stay lazy,
23
+ // while function references resolve at call time.
25
24
 
26
25
  /** Example transaction hash surfaced in OpenAPI docs. */
27
26
  const exampleHash = '0x515801d7f9a5ac705e793e85904c9c69b3f1694b465cc8fb6ba3f0298dc82665'
@@ -33,7 +32,7 @@ const exampleHash = '0x515801d7f9a5ac705e793e85904c9c69b3f1694b465cc8fb6ba3f0298
33
32
  */
34
33
  function computeFeeAmount(gasUsed: number | undefined, effectiveGasPrice: string | undefined) {
35
34
  if (gasUsed === undefined || effectiveGasPrice === undefined) return undefined
36
- return Fees.fromGas(BigInt(gasUsed), BigInt(effectiveGasPrice)).toString()
35
+ return Fees.fromGas(BigInt(gasUsed), BigInt(effectiveGasPrice))
37
36
  }
38
37
 
39
38
  /** Zod schemas owned by the receipt handlers. */
@@ -187,28 +186,39 @@ export namespace schema {
187
186
  .check(z.describe('The transaction receipt exactly as returned by JSON-RPC.'))
188
187
  }
189
188
 
190
- /**
191
- * Metadata for a receipt: the verbatim JSON-RPC receipt (always present) plus
192
- * resources embedded on demand via `include`.
193
- */
189
+ /** Receipt metadata containing valuation provenance and the verbatim JSON-RPC receipt. */
194
190
  export const ReceiptMeta = z
195
191
  .object({
196
- feeToken: z
197
- .optional(z.lazy(() => Tokens.schema.getToken.Response))
198
- .check(z.describe('Fee-token details included only when you request `include=feeToken`.')),
192
+ pricing: z
193
+ .optional(Valuation.schema.Pricing)
194
+ .check(
195
+ z.describe(
196
+ 'Rate provenance for the fee valuation. Present when conversion rates were consulted.',
197
+ ),
198
+ ),
199
199
  rpc: Rpc.Receipt.check(z.describe('The original JSON-RPC receipt payload.')),
200
200
  })
201
+ .check(z.describe('Metadata for this receipt, including pricing and the JSON-RPC payload.'))
202
+
203
+ /** The fee charged to the fee payer, carrying its value in the requested denomination. */
204
+ export const FeeAmount = z
205
+ .extend(Schema.TokenAmount, {
206
+ valuation: z
207
+ .optional(z.nullable(Valuation.schema.Value))
208
+ .check(
209
+ z.describe(
210
+ 'The fee’s nominal value in the requested `denomination`. `null` when rates were unavailable.',
211
+ ),
212
+ ),
213
+ })
201
214
  .check(
202
- z.describe(
203
- 'Metadata for this receipt: the original JSON-RPC payload plus any requested `include` resources.',
204
- ),
215
+ z.describe('The fee charged to the fee payer, with its value in the requested denomination.'),
205
216
  )
206
217
 
207
218
  /**
208
219
  * A humanized transaction receipt: decoded `status`/`type`, plain-number gas
209
- * and block fields, and decimal-string fee/price values. The raw event logs
210
- * are surfaced verbatim under `logs`, and the full JSON-RPC receipt under
211
- * `meta.rpc`.
220
+ * and block fields, a structured fee amount, and a decimal-string gas price.
221
+ * The raw event logs are surfaced under `logs`, and the RPC receipt under `meta.rpc`.
212
222
  */
213
223
  export const Receipt = z
214
224
  .object({
@@ -245,19 +255,15 @@ export namespace schema {
245
255
  effectiveGasPrice: Schema.DecimalString.check(
246
256
  z.describe('Effective gas price paid for this transaction, in attodollars per gas.'),
247
257
  ),
248
- feeAmount: Schema.DecimalString.check(
249
- z.describe(
250
- 'Fee charged to the fee payer, as a decimal integer string in the fee token’s base units.',
251
- ),
252
- ),
258
+ feeAmount: FeeAmount.check(z.describe('Fee charged to the fee payer, denominated in USD.')),
253
259
  feePayer: z
254
260
  .optional(Schema.Address)
255
261
  .check(z.describe('The address that paid the transaction fee.')),
256
262
  feeToken: z
257
- .optional(Schema.Address)
263
+ .optional(z.lazy(() => Tokens.schema.TokenReference))
258
264
  .check(
259
265
  z.describe(
260
- 'The token used to pay the fee; on Tempo this is a USD stablecoin such as `pathUSD`.',
266
+ 'The token used to pay the fee, with RPC metadata and optional curated fields.',
261
267
  ),
262
268
  ),
263
269
  gasUsed: z
@@ -277,7 +283,7 @@ export namespace schema {
277
283
  z.describe('Event logs emitted by this transaction, returned in JSON-RPC log format.'),
278
284
  ),
279
285
  meta: ReceiptMeta.check(
280
- z.describe('The original JSON-RPC payload plus any resources requested with `include`.'),
286
+ z.describe('Receipt metadata containing pricing provenance and the JSON-RPC payload.'),
281
287
  ),
282
288
  recipient: z
283
289
  .nullable(Schema.Address)
@@ -312,7 +318,9 @@ export namespace schema {
312
318
  z.describe('The transaction position within its block.'),
313
319
  z.meta({ examples: [0] }),
314
320
  ),
315
- type: z.lazy(() => txSchema.TransactionType).check(z.meta({ examples: ['eip1559'] })),
321
+ type: z
322
+ .lazy(() => Transactions.schema.TransactionType)
323
+ .check(z.meta({ examples: ['eip1559'] })),
316
324
  })
317
325
  .check(
318
326
  z.describe(
@@ -334,25 +342,25 @@ export namespace schema {
334
342
  })
335
343
  .check(z.describe('Path parameters used to look up a receipt by transaction hash.'))
336
344
 
337
- /** Optional related resource that callers opt into via `include`. */
345
+ /** Optional curated token fields that callers opt into via `include`. */
338
346
  export const Include = z
339
- .enum(['feeToken'])
340
- .check(z.describe('Related resource you can opt into with the `include` query parameter.'))
347
+ .enum(['feeToken.logoUri', 'feeToken.verified'])
348
+ .check(z.describe('Additional fee-token fields you can request with `include`.'))
341
349
 
342
350
  /**
343
351
  * Parses a comma-separated `include` query value into a list of optional
344
- * resources to embed. Extra lookups (e.g. fee-token metadata) only run when
345
- * explicitly requested, keeping the base receipt response fast.
352
+ * token fields to embed. Curated lookups only run when explicitly requested.
346
353
  */
347
354
  export const includeQuery = Schema.includeQuery(
348
355
  Include,
349
- 'Comma-separated related resources to include, such as `feeToken`.',
356
+ 'Comma-separated fee-token fields to include, such as `feeToken.logoUri,feeToken.verified`.',
350
357
  )
351
358
 
352
359
  /** Query parameters for receipt requests. */
353
360
  export const Query = z
354
361
  .strictObject({
355
362
  chainId: Schema.ChainIdQuery,
363
+ denomination: Schema.Denomination,
356
364
  include: includeQuery,
357
365
  })
358
366
  .check(z.describe('Query parameters for looking up a receipt.'))
@@ -363,13 +371,9 @@ export namespace schema {
363
371
 
364
372
  /** Schemas for the getReceipts (list) operation. */
365
373
  export namespace getReceipts {
366
- /**
367
- * Optional resources for the receipt list: the per-row `feeToken` embed
368
- * plus the response-wide capped `totalCount`. The detail route keeps its
369
- * own `feeToken`-only {@link getReceipt.Include}.
370
- */
374
+ /** Optional fee-token fields and response-wide count metadata. */
371
375
  export const Include = z
372
- .enum(['feeToken', 'totalCount'])
376
+ .enum(['feeToken.logoUri', 'feeToken.verified', 'totalCount'])
373
377
  .check(
374
378
  z.describe('Related resources to include in each receipt row or in response metadata.'),
375
379
  )
@@ -377,7 +381,7 @@ export namespace schema {
377
381
  /** Parses the comma-separated `include` value for the receipt list. */
378
382
  export const includeQuery = Schema.includeQuery(
379
383
  Include,
380
- 'Comma-separated related resources to include, such as `feeToken,totalCount`.',
384
+ 'Comma-separated resources to include, such as `feeToken.logoUri,feeToken.verified,totalCount`.',
381
385
  )
382
386
 
383
387
  /** Query parameters for receipt list requests. */
@@ -394,6 +398,7 @@ export namespace schema {
394
398
  'blockNumber.to': Schema.blockNumberBound('receipts', 'to'),
395
399
  chainId: Schema.ChainIdQuery,
396
400
  cursor: Schema.Cursor,
401
+ denomination: Schema.Denomination,
397
402
  feePayer: z
398
403
  .optional(Schema.Address)
399
404
  .check(z.describe('Filter to receipts whose transaction fee was paid by this address.')),
@@ -430,14 +435,28 @@ export namespace schema {
430
435
  * which stores neither the block hash nor the event logs, so those fields are
431
436
  * `null`/empty in the list `meta.rpc` payload.
432
437
  */
438
+ /** Page-level resources: opt-in counts plus valuation rate provenance. */
439
+ export const Meta = z
440
+ .object({
441
+ pricing: z
442
+ .optional(Valuation.schema.Pricing)
443
+ .check(
444
+ z.describe(
445
+ 'Rate provenance for fee valuations. Present when conversion rates were ' +
446
+ 'consulted; absent when every value was identity-valued or rates were unavailable.',
447
+ ),
448
+ ),
449
+ totalCount: z.optional(Schema.TotalCount),
450
+ totalCountCapped: z.optional(Schema.TotalCountCapped),
451
+ })
452
+ .check(z.describe('Page-level resources attached to this response.'))
453
+
433
454
  export const Response = z
434
455
  .object({
435
456
  data: z.array(getReceipt.Response).check(z.describe('Receipts on this page.')),
436
457
  meta: z
437
- .optional(Schema.CountMeta)
438
- .check(
439
- z.describe('Response-level metadata requested with `include`, such as `totalCount`.'),
440
- ),
458
+ .optional(Meta)
459
+ .check(z.describe('Page-level resources: opt-in counts and valuation rate provenance.')),
441
460
  nextCursor: Schema.NextCursor,
442
461
  })
443
462
  .check(z.describe('A page of receipts ordered by block number and transaction position.'))
@@ -445,7 +464,8 @@ export namespace schema {
445
464
  }
446
465
 
447
466
  /** Creates receipt handlers. */
448
- export function receipts() {
467
+ export function receipts(options: receipts.Options = {}) {
468
+ const oracle = options.fx?.oracle ?? FxOracle.ecb()
449
469
  return new Hono<App.Environment>()
450
470
  .get(
451
471
  '/v1/transactions/receipts',
@@ -459,7 +479,7 @@ export function receipts() {
459
479
  'List transaction receipts across Tempo, with filters for addresses, blocks, timestamps, fees, and status.',
460
480
  operationId: 'getReceipts',
461
481
  responses: OpenApi.responses({
462
- errors: { 502: 'Could not read indexed receipt data from TIDX.' },
482
+ errors: { 502: 'Could not read receipt or token data from an upstream service.' },
463
483
  success: {
464
484
  description: 'A page of transaction receipts.',
465
485
  schema: schema.getReceipts.Response,
@@ -484,9 +504,8 @@ export function receipts() {
484
504
  const query = c.req.valid('query')
485
505
  const chainId = query.chainId ?? c.get('chainId')
486
506
 
487
- // Cursor pages are anchored below the head and effectively immutable, so
488
- // cache them longer; the head page keeps the route's short `feed` default.
489
- if (query.cursor !== undefined) Cache.setPolicy(c, Cache.policies.immutable)
507
+ // Cursor pages carry current fee valuations and curated verification data.
508
+ if (query.cursor !== undefined) Cache.setPolicy(c, Cache.policies.metadata)
490
509
 
491
510
  try {
492
511
  // `totalCount` is opt-in and shares the page's filters; run it
@@ -516,7 +535,10 @@ export function receipts() {
516
535
  feeToken: query.feeToken,
517
536
  fromBlock: query['blockNumber.from'],
518
537
  fromTimestamp: query['timestamp.from'],
519
- include: query.include.includes('feeToken') ? ['feeToken'] : [],
538
+ include: query.include.filter(
539
+ (resource): resource is z.output<typeof schema.getReceipt.Include> =>
540
+ resource !== 'totalCount',
541
+ ),
520
542
  limit: query.limit,
521
543
  order: query.order,
522
544
  page: query.page,
@@ -527,15 +549,43 @@ export function receipts() {
527
549
  toTimestamp: query['timestamp.to'],
528
550
  })
529
551
 
530
- const meta = countPromise ? await countPromise : undefined
552
+ // Value each fee via its own currency tag, in the requested
553
+ // denomination.
554
+ const denomination = query.denomination ?? 'USD'
555
+ const rates = await Valuation.ratesFor(c, {
556
+ currencies: receipts.data.map((receipt) => receipt.feeAmount.currency),
557
+ denomination,
558
+ oracle,
559
+ })
560
+ const data = receipts.data.map((receipt) => ({
561
+ ...receipt,
562
+ feeAmount: {
563
+ ...receipt.feeAmount,
564
+ valuation: Valuation.valuationFor({
565
+ amount: BigInt(receipt.feeAmount.baseUnits),
566
+ denomination,
567
+ rates,
568
+ token: receipt.feeAmount,
569
+ }),
570
+ },
571
+ }))
572
+
573
+ const counts = countPromise ? await countPromise : undefined
574
+ const meta = {
575
+ ...(rates ? { pricing: Valuation.pricing(rates, oracle) } : {}),
576
+ ...counts,
577
+ }
531
578
  return c.json(
532
579
  Response.validated(schema.getReceipts.Response, {
533
- ...receipts,
534
- ...(meta ? { meta } : {}),
580
+ data,
581
+ ...(Object.keys(meta).length > 0 ? { meta } : {}),
582
+ nextCursor: receipts.nextCursor,
535
583
  }),
536
584
  200,
537
585
  )
538
586
  } catch (cause) {
587
+ if (cause instanceof Valuation.UnsupportedDenominationError)
588
+ return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
539
589
  return Response.upstream(c, cause)
540
590
  }
541
591
  },
@@ -560,7 +610,7 @@ export function receipts() {
560
610
  description: 'No receipt was found for that transaction hash.',
561
611
  codes: ['receipt_not_found'],
562
612
  },
563
- 502: 'Could not read receipt data from the upstream JSON-RPC node.',
613
+ 502: 'Could not read receipt or token data from upstream JSON-RPC.',
564
614
  },
565
615
  success: {
566
616
  description: 'A single transaction receipt with status, gas, fees, and logs.',
@@ -571,11 +621,8 @@ export function receipts() {
571
621
  tags: ['Transactions'],
572
622
  }),
573
623
  Cache.response<App.Environment>({
574
- // A receipt exists only once the transaction is mined: the lookup is
575
- // either a 404 (unknown / not-yet-mined hash) or a final 200, never a
576
- // pending 200. Default to no-store so the 404 isn't cached with a
577
- // long-lived header (which would mask the receipt once mined); the handler
578
- // unconditionally upgrades the 200 to `immutable` below.
624
+ // Default to no-store so a 404 does not mask the eventual mined receipt.
625
+ // Successful responses use the metadata policy for current valuations.
579
626
  cacheControl: Cache.policies.noStore,
580
627
  name: 'tempo-api:receipts:v1',
581
628
  key: (c) => Cache.urlKey(c, schema.getReceipt.Query),
@@ -619,50 +666,73 @@ export function receipts() {
619
666
 
620
667
  const data = humanizeReceipt(Response.validated(schema.Rpc.Receipt, rpc))
621
668
 
622
- // A receipt is only returned once the transaction is mined, so it is
623
- // immutable and can be cached aggressively.
624
- Cache.setPolicy(c, Cache.policies.immutable)
669
+ // Mined receipts still carry current valuations and verification data.
670
+ Cache.setPolicy(c, Cache.policies.metadata)
625
671
 
626
- // Fee-token metadata is opt-in: resolving it is an extra lookup, so it
627
- // only runs when requested via `include=feeToken` and the receipt
628
- // declares a fee token.
629
- const feeToken = data.feeToken
630
- if (!query.include.includes('feeToken') || feeToken === undefined)
631
- return c.json(Response.validated(schema.getReceipt.Response, data), 200)
672
+ const [enriched] = await enrichReceipts(c, {
673
+ chainId,
674
+ include: query.include,
675
+ receipts: [data],
676
+ })
677
+ if (!enriched) throw new Error('Unable to enrich transaction receipt')
632
678
 
633
- const token = await Timing.time(c, 'fee_token', () =>
634
- Tokens.resolveToken(c, { address: feeToken, chainId }).catch(() => undefined),
635
- )
636
- return c.json(
637
- Response.validated(
638
- schema.getReceipt.Response,
639
- token ? { ...data, meta: { ...data.meta, feeToken: token } } : data,
640
- ),
641
- 200,
642
- )
679
+ // Value the fee via its own currency tag, in the requested
680
+ // denomination.
681
+ const denomination = query.denomination ?? 'USD'
682
+ const rates = await Valuation.ratesFor(c, {
683
+ currencies: [enriched.feeAmount.currency],
684
+ denomination,
685
+ oracle,
686
+ })
687
+ const valued = {
688
+ ...enriched,
689
+ feeAmount: {
690
+ ...enriched.feeAmount,
691
+ valuation: Valuation.valuationFor({
692
+ amount: BigInt(enriched.feeAmount.baseUnits),
693
+ denomination,
694
+ rates,
695
+ token: enriched.feeAmount,
696
+ }),
697
+ },
698
+ meta: {
699
+ ...enriched.meta,
700
+ ...(rates ? { pricing: Valuation.pricing(rates, oracle) } : {}),
701
+ },
702
+ }
703
+ return c.json(Response.validated(schema.getReceipt.Response, valued), 200)
643
704
  } catch (cause) {
705
+ if (cause instanceof Valuation.UnsupportedDenominationError)
706
+ return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
644
707
  return Response.upstream(c, cause)
645
708
  }
646
709
  },
647
710
  )
648
711
  }
649
712
 
713
+ export declare namespace receipts {
714
+ /** Options for the receipt handlers. */
715
+ type Options = {
716
+ /** FX configuration backing fee valuation. */
717
+ fx?: Valuation.addresses.Fx | undefined
718
+ }
719
+ }
720
+
650
721
  /**
651
- * Lists humanized receipts and embeds opt-in `include` resources (currently
652
- * `feeToken`).
722
+ * Lists humanized receipts with required fee-token metadata and optional curated fields.
653
723
  */
654
724
  export async function listReceipts(c: Context<App.Environment>, options: listReceipts.Options) {
655
725
  const { include, ...rest } = options
656
726
  const page = await getReceipts(c, rest)
657
- const data = include.includes('feeToken')
658
- ? await embedFeeTokens(c, page.data, rest.chainId)
659
- : page.data
660
- return { ...page, data }
727
+ return {
728
+ ...page,
729
+ data: await enrichReceipts(c, { chainId: rest.chainId, include, receipts: page.data }),
730
+ }
661
731
  }
662
732
 
663
733
  export declare namespace listReceipts {
664
734
  type Options = getReceipts.Options & {
665
- /** Optional related resources to embed (e.g. `feeToken`). */
735
+ /** Optional curated fee-token fields to embed. */
666
736
  include: readonly z.output<typeof schema.getReceipt.Include>[]
667
737
  }
668
738
  }
@@ -825,7 +895,7 @@ export function getReceipts(c: Context<App.Environment>, options: getReceipts.Op
825
895
  // Reconstruct the RPC shape from the indexed columns, then humanize it
826
896
  // through the same path as the detail endpoint so the list and detail
827
897
  // responses are identical. Rows that fail to reconstruct are skipped.
828
- const data: z.output<typeof schema.Receipt>[] = []
898
+ const data: humanizeReceipt.Output[] = []
829
899
  for (const row of page.rows) {
830
900
  const tx = typeof row['tx_hash'] === 'string' ? txByHash.get(row['tx_hash']) : undefined
831
901
  const merged = { ...row, fee_token: tx?.feeToken, type: tx?.type }
@@ -977,8 +1047,8 @@ export async function embedReceipts(
977
1047
  c: Context<App.Environment>,
978
1048
  options: embedReceipts.Options,
979
1049
  ): Promise<Map<string, z.output<typeof schema.Receipt>>> {
980
- const { chainId, rows } = options
981
- const found = new Map<string, z.output<typeof schema.Receipt>>()
1050
+ const { chainId, include, rows } = options
1051
+ const found = new Map<string, humanizeReceipt.Output>()
982
1052
 
983
1053
  // Only mined rows can have a receipt; their block numbers also feed the
984
1054
  // block bound the `logs` lookup requires.
@@ -989,7 +1059,7 @@ export async function embedReceipts(
989
1059
  blocks.push(row.blockNumber)
990
1060
  hashes.add(row.hash.toLowerCase())
991
1061
  }
992
- if (hashes.size === 0) return found
1062
+ if (hashes.size === 0) return new Map()
993
1063
 
994
1064
  const tidx = c.get('getTidx')(chainId)
995
1065
  const inList = [...hashes].map((hash) => `'${hash}'`).join(', ')
@@ -1057,7 +1127,8 @@ export async function embedReceipts(
1057
1127
  meta: { ...receipt.meta, rpc: { ...receipt.meta.rpc, logs } },
1058
1128
  })
1059
1129
  }
1060
- return found
1130
+ const receipts = await enrichReceipts(c, { chainId, include, receipts: [...found.values()] })
1131
+ return new Map(receipts.map((receipt) => [receipt.transactionHash.toLowerCase(), receipt]))
1061
1132
  }
1062
1133
 
1063
1134
  const receiptRowByHash = new Map<string, Record<string, unknown>>()
@@ -1087,7 +1158,8 @@ export async function embedReceipts(
1087
1158
  })
1088
1159
  if (rpc.success) found.set(hash, humanizeReceipt(rpc.data))
1089
1160
  }
1090
- return found
1161
+ const receipts = await enrichReceipts(c, { chainId, include, receipts: [...found.values()] })
1162
+ return new Map(receipts.map((receipt) => [receipt.transactionHash.toLowerCase(), receipt]))
1091
1163
  }
1092
1164
 
1093
1165
  export declare namespace embedReceipts {
@@ -1095,12 +1167,14 @@ export declare namespace embedReceipts {
1095
1167
  type Options = {
1096
1168
  /** Target chain id. */
1097
1169
  chainId: z.output<typeof Schema.ChainId>
1170
+ /** Optional curated fee-token fields to embed. */
1171
+ include: readonly string[]
1098
1172
  /**
1099
1173
  * Pre-fetched humanized receipts for the page (the `status`-filtered
1100
1174
  * transactions path). When provided, the receipts query is skipped and only
1101
1175
  * the page's event logs are fetched and reattached.
1102
1176
  */
1103
- receipts?: readonly z.output<typeof schema.Receipt>[] | undefined
1177
+ receipts?: readonly humanizeReceipt.Output[] | undefined
1104
1178
  /**
1105
1179
  * The page's humanized transaction rows. Typed structurally (rather than as
1106
1180
  * the transactions schema output) so this module never touches the
@@ -1119,18 +1193,37 @@ export declare namespace embedReceipts {
1119
1193
  }
1120
1194
  }
1121
1195
 
1196
+ /** Resolves required RPC fee-token metadata and optional curated fields for receipts. */
1197
+ export function enrichReceipts(c: Context<App.Environment>, options: enrichReceipts.Options) {
1198
+ return Transactions.enrichFeeTokens(c, {
1199
+ chainId: options.chainId,
1200
+ include: options.include,
1201
+ rows: options.receipts,
1202
+ })
1203
+ }
1204
+
1205
+ export declare namespace enrichReceipts {
1206
+ /** Inputs for enriching humanized receipts. */
1207
+ type Options = {
1208
+ /** Target chain id. */
1209
+ chainId: z.output<typeof Schema.ChainId>
1210
+ /** Optional curated fee-token fields to embed. */
1211
+ include: readonly string[]
1212
+ /** Humanized receipts whose fee token is still an address. */
1213
+ receipts: readonly humanizeReceipt.Output[]
1214
+ }
1215
+ }
1216
+
1122
1217
  /**
1123
1218
  * Humanizes a JSON-RPC transaction receipt: decodes `status`/`type`, converts
1124
- * hex quantities to decimal strings and block fields to numbers, and derives the
1219
+ * gas price and fee values, converts block fields to numbers, and derives the
1125
1220
  * block `timestamp` from the top-level `blockTimestamp` (present when
1126
1221
  * reconstructed from the index) or the (shared) per-log timestamp — `null` when
1127
1222
  * neither is available. The verbatim payload is preserved under `meta.rpc` and
1128
1223
  * the raw logs under `logs`. Shared by the receipt detail (RPC source) and list
1129
1224
  * (reconstructed from the indexer) endpoints, so both expose an identical shape.
1130
1225
  */
1131
- export function humanizeReceipt(
1132
- rpc: z.output<typeof schema.Rpc.Receipt>,
1133
- ): z.output<typeof schema.Receipt> {
1226
+ export function humanizeReceipt(rpc: z.output<typeof schema.Rpc.Receipt>): humanizeReceipt.Output {
1134
1227
  const gasUsed = Value.hexToNumber(rpc.gasUsed) ?? 0
1135
1228
  const effectiveGasPrice = Value.fromHex(rpc.effectiveGasPrice) ?? '0'
1136
1229
  return {
@@ -1139,7 +1232,11 @@ export function humanizeReceipt(
1139
1232
  contractAddress: rpc.contractAddress ?? null,
1140
1233
  cumulativeGasUsed: Value.hexToNumber(rpc.cumulativeGasUsed) ?? 0,
1141
1234
  effectiveGasPrice,
1142
- feeAmount: computeFeeAmount(gasUsed, effectiveGasPrice) ?? '0',
1235
+ feeAmount: Value.tokenAmount({
1236
+ baseUnits: computeFeeAmount(gasUsed, effectiveGasPrice) ?? 0n,
1237
+ currency: 'USD',
1238
+ decimals: Fees.tokenDecimals,
1239
+ }),
1143
1240
  feePayer: rpc.feePayer ?? undefined,
1144
1241
  feeToken: rpc.feeToken ?? undefined,
1145
1242
  gasUsed,
@@ -1152,7 +1249,15 @@ export function humanizeReceipt(
1152
1249
  timestamp: Value.hexSecondsToIso(rpc.blockTimestamp ?? rpc.logs[0]?.blockTimestamp) ?? null,
1153
1250
  transactionHash: rpc.transactionHash,
1154
1251
  transactionIndex: Value.hexToNumber(rpc.transactionIndex) ?? 0,
1155
- type: decodeTransactionType(rpc.type),
1252
+ type: Transactions.decodeTransactionType(rpc.type),
1253
+ }
1254
+ }
1255
+
1256
+ export declare namespace humanizeReceipt {
1257
+ /** Humanized receipt before its fee-token address is resolved to metadata. */
1258
+ type Output = Omit<z.output<typeof schema.Receipt>, 'feeToken'> & {
1259
+ /** Address of the token used to pay the fee. */
1260
+ feeToken?: z.output<typeof Schema.Address> | undefined
1156
1261
  }
1157
1262
  }
1158
1263
 
@@ -980,6 +980,27 @@ describe('GET /tokens/:symbol', () => {
980
980
  })
981
981
 
982
982
  describe('GET /tokens/:token/transactions', () => {
983
+ test('parses granular fee-token includes', () => {
984
+ expect(
985
+ Tokens.schema.getTokenTransactions.Query.parse({
986
+ include: 'feeToken.logoUri,feeToken.verified,token,totalCount',
987
+ }).include,
988
+ ).toMatchInlineSnapshot(`
989
+ [
990
+ "feeToken.logoUri",
991
+ "feeToken.verified",
992
+ "token",
993
+ "totalCount",
994
+ ]
995
+ `)
996
+ expect(
997
+ Tokens.schema.getTokenTransactions.Query.safeParse({ include: 'feeToken' }).success,
998
+ ).toBe(false)
999
+ expect(
1000
+ Tokens.schema.getTokenTransactions.Query.safeParse({ include: 'token.logoUri' }).success,
1001
+ ).toBe(false)
1002
+ })
1003
+
983
1004
  test('returns a transaction page touching the token contract', async () => {
984
1005
  const client = app()
985
1006
  const response = await client.v1.tokens[':token'].transactions.$get(
@@ -996,6 +1017,12 @@ describe('GET /tokens/:token/transactions', () => {
996
1017
  for (const tx of body.data) {
997
1018
  const involves = tx.sender === TestApp.token || tx.recipient === TestApp.token
998
1019
  expect(involves).toBe(true)
1020
+ if (tx.feeToken) {
1021
+ expect(tx.feeToken.address).toBe(tx.meta.rpc.feeToken)
1022
+ expect(typeof tx.feeToken.symbol).toBe('string')
1023
+ expect(tx.feeToken.logoUri).toBeUndefined()
1024
+ expect(tx.feeToken.verified).toBeUndefined()
1025
+ }
999
1026
  }
1000
1027
  // Default ordering is newest-first (descending block then in-block index).
1001
1028
  const blocks = body.data
@@ -1006,6 +1033,22 @@ describe('GET /tokens/:token/transactions', () => {
1006
1033
  expect(body.meta).toBeUndefined()
1007
1034
  })
1008
1035
 
1036
+ test('includes requested fee-token fields', async () => {
1037
+ const client = app()
1038
+ const response = await client.v1.tokens[':token'].transactions.$get(
1039
+ {
1040
+ param: { token: TestApp.token },
1041
+ query: { include: 'feeToken.logoUri,feeToken.verified', limit: '5' },
1042
+ },
1043
+ TestApp.auth,
1044
+ )
1045
+ const body = await TestApp.json(response, Tokens.schema.getTokenTransactions.Response)
1046
+
1047
+ expect(response.status).toBe(200)
1048
+ const withFeeToken = body.data.find((transaction) => transaction.feeToken)
1049
+ if (withFeeToken) expect(typeof withFeeToken.feeToken!.verified).toBe('boolean')
1050
+ })
1051
+
1009
1052
  test('embeds a capped total count on demand via include=totalCount', async () => {
1010
1053
  const client = app()
1011
1054
  const response = await client.v1.tokens[':token'].transactions.$get(