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
@@ -1,12 +1,41 @@
1
1
  import type * as z from 'zod/mini'
2
- import { Schema } from 'tapimo'
2
+ import { Address, Value as core_Value } from 'ox'
3
+ import { Db, Schema } from 'tapimo'
4
+ import { FxOracle } from 'tapimo/apps'
5
+ import { decodeFunctionData, zeroHash } from 'viem'
6
+ import { Abis, Actions, Addresses } from 'viem/tempo'
3
7
 
4
8
  import * as TestApp from '../../../../test/App.js'
9
+ import * as Relay from '../../../../test/Relay.js'
10
+ import * as Runtime from '../../../../test/runtime.js'
5
11
  import * as Exchanges from './exchanges.js'
6
12
 
13
+ type JsonSchema = {
14
+ examples?: readonly unknown[]
15
+ oneOf?: readonly JsonSchema[]
16
+ properties?: Record<string, JsonSchema>
17
+ required?: readonly string[]
18
+ }
7
19
  type OrdersResponse = z.output<typeof Exchanges.schema.getOrders.Response>
20
+ type OpenApiDocument = {
21
+ paths: Record<
22
+ string,
23
+ {
24
+ post?: {
25
+ requestBody?: { content?: Record<string, { schema?: JsonSchema }> }
26
+ responses?: Record<string, { content?: Record<string, { schema?: JsonSchema }> }>
27
+ }
28
+ }
29
+ >
30
+ }
8
31
  type PairsResponse = z.output<typeof Exchanges.schema.getPairs.Response>
9
32
 
33
+ // Deterministic EUR-based rate set so conversion math is exact in assertions.
34
+ const fixed = FxOracle.from({
35
+ name: 'fixed',
36
+ rates: async () => ({ asOf: '2026-01-01', base: 'EUR', rates: { AUD: '1.6', USD: '1.0' } }),
37
+ })
38
+
10
39
  type Client = ReturnType<typeof TestApp.client>
11
40
  type OrdersQuery = {
12
41
  chainId?: string
@@ -569,6 +598,68 @@ describe('GET /exchange/pairs/:base/depth', () => {
569
598
  describe('GET /exchange/swaps', () => {
570
599
  type GlobalSwapsResponse = z.output<typeof Exchanges.schema.getSwaps.Response>
571
600
 
601
+ test('validates denominations without swap rows', async () => {
602
+ const client = TestApp.client({ fx: { oracle: fixed } })
603
+ const response = await client.v1.exchange.swaps.$get(
604
+ { query: { denomination: 'JPY', transactionHash: zeroHash } },
605
+ TestApp.auth,
606
+ )
607
+ expect(response.status).toBe(400)
608
+ })
609
+
610
+ test('parses granular token includes', () => {
611
+ expect(
612
+ Exchanges.schema.getSwaps.Query.parse({
613
+ include: 'token.logoUri,token.verified',
614
+ }).include,
615
+ ).toMatchInlineSnapshot(`
616
+ [
617
+ "token.logoUri",
618
+ "token.verified",
619
+ ]
620
+ `)
621
+ expect(Exchanges.schema.getSwaps.Query.safeParse({ include: 'tokens' }).success).toBe(false)
622
+ })
623
+
624
+ test.skipIf(Runtime.get().mode !== 'localnet')(
625
+ 'values swap legs in a requested denomination',
626
+ async () => {
627
+ const db = TestApp.database()
628
+ await TestApp.verifiedSeed(db, Runtime.get().chainId)
629
+ const client = TestApp.client({ db, fx: { oracle: fixed } })
630
+
631
+ const response = await client.v1.exchange.swaps.$get(
632
+ { query: { denomination: 'AUD', limit: '10' } },
633
+ TestApp.auth,
634
+ )
635
+ expect(response.status).toBe(200)
636
+ const body = await TestApp.json(response, Exchanges.schema.getSwaps.Response)
637
+ expect(body.data.length).toBeGreaterThan(0)
638
+
639
+ // Curated legs value at the fixed 1.6 USD -> AUD rate; uncurated
640
+ // created-token legs stay null.
641
+ const legs = body.data.flatMap((swap) => [
642
+ swap.sourceAmount,
643
+ swap.destinationAmount,
644
+ ...swap.fills.flatMap((fill) => [fill.sourceAmount, fill.destinationAmount]),
645
+ ])
646
+ const valued = legs.filter((leg) => leg.valuation)
647
+ expect(valued.length).toBeGreaterThan(0)
648
+ for (const leg of valued)
649
+ expect(leg.valuation).toEqual({
650
+ amount: core_Value.format((BigInt(leg.baseUnits) * 16n) / 10n, 6),
651
+ currency: 'AUD',
652
+ })
653
+ expect(body.meta?.pricing).toMatchInlineSnapshot(`
654
+ {
655
+ "asOf": "2026-01-01",
656
+ "basis": "nominal",
657
+ "source": "fixed",
658
+ }
659
+ `)
660
+ },
661
+ )
662
+
572
663
  test('returns a global, newest-first page of swaps across all pairs', async () => {
573
664
  const client = TestApp.client()
574
665
  const response = await client.v1.exchange.swaps.$get({ query: { limit: '10' } }, TestApp.auth)
@@ -582,9 +673,29 @@ describe('GET /exchange/swaps', () => {
582
673
  expect(swap.transactionHash).toMatch(/^0x[0-9a-f]{64}$/)
583
674
  expect(['exactSource', 'exactDestination', null]).toContain(swap.mode)
584
675
  expect(swap.sourceToken.address).toMatch(/^0x[0-9a-f]{40}$/)
585
- expect(swap.sourceToken.amount).toMatch(/^\d+$/)
676
+ expect(swap.sourceToken).not.toHaveProperty('amount')
677
+ expect(typeof swap.sourceToken.currency).toBe('string')
678
+ expect(typeof swap.sourceToken.decimals).toBe('number')
679
+ expect(typeof swap.sourceToken.name).toBe('string')
680
+ expect(typeof swap.sourceToken.symbol).toBe('string')
681
+ expect(swap.sourceToken.logoUri).toBeUndefined()
682
+ expect(swap.sourceToken.verified).toBeUndefined()
683
+ expect(swap.sourceAmount.baseUnits).toMatch(/^\d+$/)
684
+ expect(swap.sourceAmount.currency).toBe(swap.sourceToken.currency)
685
+ expect(swap.sourceAmount.decimals).toBe(swap.sourceToken.decimals)
686
+ expect(swap.sourceAmount.formatted).toMatch(/^\d+(\.\d+)?$/)
586
687
  expect(swap.destinationToken.address).toMatch(/^0x[0-9a-f]{40}$/)
587
- expect(swap.destinationToken.amount).toMatch(/^\d+$/)
688
+ expect(swap.destinationToken).not.toHaveProperty('amount')
689
+ expect(typeof swap.destinationToken.currency).toBe('string')
690
+ expect(typeof swap.destinationToken.decimals).toBe('number')
691
+ expect(typeof swap.destinationToken.name).toBe('string')
692
+ expect(typeof swap.destinationToken.symbol).toBe('string')
693
+ expect(swap.destinationToken.logoUri).toBeUndefined()
694
+ expect(swap.destinationToken.verified).toBeUndefined()
695
+ expect(swap.destinationAmount.baseUnits).toMatch(/^\d+$/)
696
+ expect(swap.destinationAmount.currency).toBe(swap.destinationToken.currency)
697
+ expect(swap.destinationAmount.decimals).toBe(swap.destinationToken.decimals)
698
+ expect(swap.destinationAmount.formatted).toMatch(/^\d+(\.\d+)?$/)
588
699
  expect(swap.rate).toMatch(/^\d+(\.\d+)?$/)
589
700
  expect(swap.filledAt).toMatch(/^\d{4}-\d{2}-\d{2}T/)
590
701
  // `route` spans source → destination; its ends mirror the side tokens.
@@ -600,6 +711,14 @@ describe('GET /exchange/swaps', () => {
600
711
  expect(fill.price).toMatch(/^\d+(\.\d+)?$/)
601
712
  expect(swap.route).toContain(fill.sourceToken.address)
602
713
  expect(swap.route).toContain(fill.destinationToken.address)
714
+ expect(fill.sourceToken).not.toHaveProperty('amount')
715
+ expect(fill.sourceAmount.baseUnits).toMatch(/^\d+$/)
716
+ expect(fill.sourceAmount.currency).toBe(fill.sourceToken.currency)
717
+ expect(fill.sourceAmount.decimals).toBe(fill.sourceToken.decimals)
718
+ expect(fill.destinationToken).not.toHaveProperty('amount')
719
+ expect(fill.destinationAmount.baseUnits).toMatch(/^\d+$/)
720
+ expect(fill.destinationAmount.currency).toBe(fill.destinationToken.currency)
721
+ expect(fill.destinationAmount.decimals).toBe(fill.destinationToken.decimals)
603
722
  }
604
723
  for (let i = 1; i < swap.fills.length; i++)
605
724
  expect(swap.fills[i]!.logIndex).toBeGreaterThan(swap.fills[i - 1]!.logIndex)
@@ -627,6 +746,9 @@ describe('GET /exchange/swaps', () => {
627
746
  TestApp.auth,
628
747
  )
629
748
  const secondBody = await TestApp.json(second, Exchanges.schema.getSwaps.Response)
749
+ expect(second.headers.get('cache-control')).toMatchInlineSnapshot(
750
+ `"private, max-age=60, stale-while-revalidate=300"`,
751
+ )
630
752
 
631
753
  // A swap row is unique on `(block, first fill logIndex)`.
632
754
  const key = (swap: GlobalSwapsResponse['data'][number]) =>
@@ -669,24 +791,433 @@ describe('GET /exchange/swaps', () => {
669
791
  for (const swap of body.data) expect(swap.fills.some((fill) => fill.maker === maker)).toBe(true)
670
792
  }, 60000)
671
793
 
672
- test('embeds token metadata on swap sides when requested via `include=tokens`', async () => {
794
+ test('includes curated token fields on swap sides and fills', async () => {
673
795
  const client = TestApp.client()
674
796
  const response = await client.v1.exchange.swaps.$get(
675
- { query: { include: 'tokens', limit: '10' } },
797
+ { query: { include: 'token.logoUri,token.verified', limit: '10' } },
676
798
  TestApp.auth,
677
799
  )
678
800
  expect(response.status).toBe(200)
679
801
  const body = await TestApp.json(response, Exchanges.schema.getSwaps.Response)
680
802
  if (body.data.length === 0) return
681
803
 
682
- const enriched = body.data.find((swap) => typeof swap.sourceToken.symbol === 'string')
683
- if (enriched) {
684
- expect(typeof enriched.sourceToken.symbol).toBe('string')
685
- expect(typeof enriched.sourceToken.decimals).toBe('number')
804
+ const enriched = body.data[0]!
805
+ expect(typeof enriched.sourceToken.verified).toBe('boolean')
806
+ expect(typeof enriched.destinationToken.verified).toBe('boolean')
807
+ for (const fill of enriched.fills) {
808
+ expect(typeof fill.sourceToken.verified).toBe('boolean')
809
+ expect(typeof fill.destinationToken.verified).toBe('boolean')
686
810
  }
687
811
  }, 60000)
688
812
  })
689
813
 
814
+ describe('POST /exchange/quotes', () => {
815
+ test('validates denominations without verification data', async () => {
816
+ const db = () =>
817
+ Db.postgres({ connectionString: 'postgresql://postgres:postgres@127.0.0.1:1/none' })
818
+ const client = TestApp.client({ db, fx: { oracle: fixed } })
819
+ const response = await client.v1.exchange.quotes.$post(
820
+ {
821
+ json: {
822
+ amount: '1',
823
+ destinationToken: Addresses.pathUsd,
824
+ mode: 'exactSource',
825
+ slippageBps: 50,
826
+ sourceToken: '0x20c0000000000000000000008f5425160ebe5525',
827
+ },
828
+ query: { denomination: 'JPY' },
829
+ },
830
+ TestApp.auth,
831
+ )
832
+ expect(response.status).toBe(400)
833
+ })
834
+
835
+ test('parses granular token includes', () => {
836
+ expect(
837
+ Exchanges.schema.createQuote.Query.parse({
838
+ include: 'token.logoUri,token.verified',
839
+ }).include,
840
+ ).toMatchInlineSnapshot(`
841
+ [
842
+ "token.logoUri",
843
+ "token.verified",
844
+ ]
845
+ `)
846
+ expect(Exchanges.schema.createQuote.Query.safeParse({ include: 'tokens' }).success).toBe(false)
847
+ })
848
+
849
+ test.each([
850
+ {
851
+ destinationToken: '0x20c0000000000000000000008f5425160ebe5525' as const,
852
+ label: 'identical tokens',
853
+ slippageBps: 50,
854
+ sourceToken: '0x20c0000000000000000000008f5425160ebe5525' as const,
855
+ },
856
+ {
857
+ destinationToken: '0x20c000000000000000000000b9537d11c60e8b50' as const,
858
+ label: 'invalid slippage',
859
+ slippageBps: 10_000,
860
+ sourceToken: '0x20c0000000000000000000008f5425160ebe5525' as const,
861
+ },
862
+ ])('rejects $label', async ({ destinationToken, slippageBps, sourceToken }) => {
863
+ const response = await TestApp.client().v1.exchange.quotes.$post(
864
+ {
865
+ json: {
866
+ amount: '1000000',
867
+ destinationToken,
868
+ mode: 'exactSource',
869
+ slippageBps,
870
+ sourceToken,
871
+ },
872
+ query: {},
873
+ },
874
+ TestApp.auth,
875
+ )
876
+ expect(response.status).toBe(400)
877
+ expect(await response.json()).toMatchObject({ error: { code: 'body_invalid' } })
878
+ })
879
+
880
+ test('publishes valid request and executable call examples', async () => {
881
+ const app = TestApp.create({ auth: false })
882
+ const spec = (await (await app.request('/openapi.json')).json()) as OpenApiDocument
883
+ const operation = spec.paths['/v1/exchange/quotes']?.post
884
+ if (!operation) throw new Error('Missing create quote OpenAPI operation')
885
+
886
+ const requestSchema = operation.requestBody?.content?.['application/json']?.schema
887
+ const requestExample = Object.fromEntries(
888
+ ['amount', 'destinationToken', 'mode', 'slippageBps', 'sourceToken'].map((property) => [
889
+ property,
890
+ requestSchema?.properties?.[property]?.examples?.[0],
891
+ ]),
892
+ )
893
+ expect(requestExample).toEqual({
894
+ amount: '1000000',
895
+ destinationToken: '0x20c000000000000000000000b9537d11c60e8b50',
896
+ mode: 'exactSource',
897
+ slippageBps: 50,
898
+ sourceToken: '0x20c0000000000000000000008f5425160ebe5525',
899
+ })
900
+ expect(Exchanges.schema.createQuote.Request.safeParse(requestExample).success).toBe(true)
901
+
902
+ const responseSchema = operation.responses?.['200']?.content?.['application/json']?.schema
903
+ for (const [variant, names] of [
904
+ [responseSchema?.oneOf?.[0], ['destinationAmount', 'destinationAmountMin', 'sourceAmount']],
905
+ [responseSchema?.oneOf?.[1], ['destinationAmount', 'sourceAmount', 'sourceAmountMax']],
906
+ ] as const)
907
+ for (const name of names) {
908
+ const amount = variant?.properties?.[name]
909
+ expect(amount?.required).toEqual(['baseUnits', 'currency', 'decimals', 'formatted'])
910
+ expect(
911
+ Object.fromEntries(
912
+ ['baseUnits', 'currency', 'decimals', 'formatted'].map((property) => [
913
+ property,
914
+ amount?.properties?.[property]?.examples?.[0],
915
+ ]),
916
+ ),
917
+ ).toEqual({ baseUnits: '1000000', currency: 'USD', decimals: 6, formatted: '1' })
918
+ }
919
+
920
+ for (const variant of responseSchema?.oneOf ?? [])
921
+ for (const name of ['destinationToken', 'sourceToken']) {
922
+ const token = variant.properties?.[name]
923
+ expect(token?.required).toEqual(['address', 'currency', 'decimals', 'name', 'symbol'])
924
+ expect(token?.properties).not.toHaveProperty('amount')
925
+ expect(token?.properties).not.toHaveProperty('maximumAmount')
926
+ expect(token?.properties).not.toHaveProperty('minimumAmount')
927
+ expect(token?.required).not.toContain('logoUri')
928
+ expect(token?.required).not.toContain('verified')
929
+ }
930
+
931
+ const callsExample =
932
+ responseSchema?.oneOf?.[0]?.properties?.['transaction']?.properties?.['calls']?.examples?.[0]
933
+ if (!Array.isArray(callsExample)) throw new Error('Missing create quote call examples')
934
+ const calls = callsExample.map((call) => Exchanges.schema.createQuote.Call.parse(call))
935
+ expect(decodeFunctionData({ abi: Abis.tip20, data: calls[0]!.data })).toEqual({
936
+ args: [Address.checksum(Addresses.stablecoinDex), 1_000_000n],
937
+ functionName: 'approve',
938
+ })
939
+ expect(decodeFunctionData({ abi: Abis.stablecoinDex, data: calls[1]!.data })).toEqual({
940
+ args: [
941
+ Address.checksum('0x20c0000000000000000000008f5425160ebe5525'),
942
+ Address.checksum('0x20c000000000000000000000b9537d11c60e8b50'),
943
+ 1_000_000n,
944
+ 995_000n,
945
+ ],
946
+ functionName: 'swapExactAmountIn',
947
+ })
948
+ })
949
+
950
+ describe.skipIf(Runtime.get().mode !== 'localnet')('localnet quotes', () => {
951
+ const account = Relay.accounts[1]!
952
+ let baseToken: z.output<typeof Schema.TokenAddress>
953
+
954
+ beforeAll(async () => {
955
+ const rpc = Relay.getClient({ account })
956
+ const created = await Actions.token.createSync(rpc, {
957
+ currency: 'USD',
958
+ name: 'Quote Base',
959
+ symbol: 'QBASE',
960
+ })
961
+ baseToken = Schema.TokenAddress.parse(created.token)
962
+ await Actions.token.grantRolesSync(rpc, {
963
+ roles: ['issuer', 'pause', 'unpause'],
964
+ to: account.address,
965
+ token: baseToken,
966
+ })
967
+ await Actions.token.mintSync(rpc, {
968
+ amount: 2_000_000_000n,
969
+ to: account.address,
970
+ token: baseToken,
971
+ })
972
+ await Actions.token.approveSync(rpc, {
973
+ amount: 1_000_000_000n,
974
+ spender: Addresses.stablecoinDex,
975
+ token: baseToken,
976
+ })
977
+ await Actions.token.approveSync(rpc, {
978
+ amount: 1_000_000_000n,
979
+ spender: Addresses.stablecoinDex,
980
+ token: Addresses.pathUsd,
981
+ })
982
+ await Actions.dex.createPairSync(rpc, { base: baseToken })
983
+ await Actions.dex.placeSync(rpc, {
984
+ amount: 1_000_000_000n,
985
+ tick: 0,
986
+ token: baseToken,
987
+ type: 'buy',
988
+ })
989
+ await Actions.dex.placeSync(rpc, {
990
+ amount: 1_000_000_000n,
991
+ tick: -10,
992
+ token: baseToken,
993
+ type: 'sell',
994
+ })
995
+ }, 120_000)
996
+
997
+ test('quotes exact-source liquidity and returns approval then sell calls', async () => {
998
+ // Pin an empty curated list so both legs deterministically value `null`.
999
+ const db = TestApp.database()
1000
+ await TestApp.verifiedSeed(db, Runtime.get().chainId, { tokens: [] })
1001
+ const client = TestApp.client({ db })
1002
+ const request = {
1003
+ amount: '1',
1004
+ destinationToken: Addresses.pathUsd,
1005
+ mode: 'exactSource',
1006
+ slippageBps: 50,
1007
+ sourceToken: baseToken,
1008
+ } as const
1009
+ const response = await client.v1.exchange.quotes.$post(
1010
+ { json: request, query: { include: 'token.logoUri,token.verified' } },
1011
+ TestApp.auth,
1012
+ )
1013
+ expect(response.status).toBe(200)
1014
+ if (response.status !== 200) throw new Error(await response.text())
1015
+
1016
+ const body = await TestApp.json(response, Exchanges.schema.createQuote.Response)
1017
+ if (body.mode !== 'exactSource') throw new Error('Expected an exact-source exchange quote')
1018
+ expect(body.destinationAmount).toEqual({
1019
+ baseUnits: '1',
1020
+ currency: 'USD',
1021
+ decimals: 6,
1022
+ formatted: '0.000001',
1023
+ valuation: null,
1024
+ })
1025
+ expect(body.destinationToken).toMatchObject({
1026
+ address: Addresses.pathUsd,
1027
+ })
1028
+ expect(body.destinationToken).not.toHaveProperty('amount')
1029
+ expect(body.destinationToken).not.toHaveProperty('minimumAmount')
1030
+ expect(typeof body.destinationToken.symbol).toBe('string')
1031
+ expect(typeof body.destinationToken.verified).toBe('boolean')
1032
+ expect(body.destinationAmountMin).toEqual(body.destinationAmount)
1033
+ expect(body.sourceAmount).toEqual({
1034
+ baseUnits: '1',
1035
+ currency: 'USD',
1036
+ decimals: 6,
1037
+ formatted: '0.000001',
1038
+ valuation: null,
1039
+ })
1040
+ expect(body.sourceToken).toMatchObject({
1041
+ address: baseToken,
1042
+ name: 'Quote Base',
1043
+ symbol: 'QBASE',
1044
+ })
1045
+ expect(body.sourceToken).not.toHaveProperty('amount')
1046
+ expect(typeof body.sourceToken.verified).toBe('boolean')
1047
+ expect(body.transaction.calls).toHaveLength(2)
1048
+ expect(body.transaction.calls[0]!.to).toBe(baseToken)
1049
+ expect(body.transaction.calls[1]!.to).toBe(Addresses.stablecoinDex.toLowerCase())
1050
+ expect(
1051
+ decodeFunctionData({ abi: Abis.tip20, data: body.transaction.calls[0]!.data }),
1052
+ ).toEqual({
1053
+ args: [Address.checksum(Addresses.stablecoinDex), 1n],
1054
+ functionName: 'approve',
1055
+ })
1056
+ expect(
1057
+ decodeFunctionData({ abi: Abis.stablecoinDex, data: body.transaction.calls[1]!.data }),
1058
+ ).toEqual({
1059
+ args: [Address.checksum(baseToken), Address.checksum(Addresses.pathUsd), 1n, 1n],
1060
+ functionName: 'swapExactAmountIn',
1061
+ })
1062
+ }, 60_000)
1063
+
1064
+ test('quotes exact-destination liquidity and returns approval then buy calls', async () => {
1065
+ // Pin an empty curated list so both legs deterministically value `null`.
1066
+ const db = TestApp.database()
1067
+ await TestApp.verifiedSeed(db, Runtime.get().chainId, { tokens: [] })
1068
+ const client = TestApp.client({ db })
1069
+ const request = {
1070
+ amount: '1',
1071
+ destinationToken: baseToken,
1072
+ mode: 'exactDestination',
1073
+ slippageBps: 50,
1074
+ sourceToken: Addresses.pathUsd,
1075
+ } as const
1076
+ const response = await client.v1.exchange.quotes.$post(
1077
+ { json: request, query: {} },
1078
+ TestApp.auth,
1079
+ )
1080
+ expect(response.status).toBe(200)
1081
+ if (response.status !== 200) throw new Error(await response.text())
1082
+
1083
+ const body = await TestApp.json(response, Exchanges.schema.createQuote.Response)
1084
+ if (body.mode !== 'exactDestination')
1085
+ throw new Error('Expected an exact-destination exchange quote')
1086
+ expect(body.destinationAmount).toEqual({
1087
+ baseUnits: '1',
1088
+ currency: 'USD',
1089
+ decimals: 6,
1090
+ formatted: '0.000001',
1091
+ valuation: null,
1092
+ })
1093
+ expect(body.destinationToken).toMatchObject({
1094
+ address: baseToken,
1095
+ name: 'Quote Base',
1096
+ symbol: 'QBASE',
1097
+ })
1098
+ expect(body.destinationToken).not.toHaveProperty('amount')
1099
+ expect(body.destinationToken).not.toHaveProperty('logoUri')
1100
+ expect(body.destinationToken).not.toHaveProperty('verified')
1101
+ expect(body.sourceAmountMax).toEqual(body.destinationAmount)
1102
+ expect(body.sourceAmount).toEqual(body.destinationAmount)
1103
+ expect(body.sourceToken).toMatchObject({
1104
+ address: Addresses.pathUsd,
1105
+ })
1106
+ expect(body.sourceToken).not.toHaveProperty('amount')
1107
+ expect(body.sourceToken).not.toHaveProperty('maximumAmount')
1108
+ expect(typeof body.sourceToken.symbol).toBe('string')
1109
+ expect(body.sourceToken).not.toHaveProperty('logoUri')
1110
+ expect(body.sourceToken).not.toHaveProperty('verified')
1111
+ expect(body.transaction.calls).toHaveLength(2)
1112
+ expect(body.transaction.calls[0]!.to).toBe(Addresses.pathUsd)
1113
+ expect(body.transaction.calls[1]!.to).toBe(Addresses.stablecoinDex.toLowerCase())
1114
+ expect(
1115
+ decodeFunctionData({ abi: Abis.tip20, data: body.transaction.calls[0]!.data }),
1116
+ ).toEqual({
1117
+ args: [Address.checksum(Addresses.stablecoinDex), 1n],
1118
+ functionName: 'approve',
1119
+ })
1120
+ expect(
1121
+ decodeFunctionData({ abi: Abis.stablecoinDex, data: body.transaction.calls[1]!.data }),
1122
+ ).toEqual({
1123
+ args: [Address.checksum(Addresses.pathUsd), Address.checksum(baseToken), 1n, 1n],
1124
+ functionName: 'swapExactAmountOut',
1125
+ })
1126
+ }, 60_000)
1127
+
1128
+ test('values quoted amounts in a requested denomination', async () => {
1129
+ // Curate only pathUSD: its leg values at the fixed 1.6 USD -> AUD rate
1130
+ // while the uncurated base token leg stays null.
1131
+ const db = TestApp.database()
1132
+ await TestApp.verifiedSeed(db, Runtime.get().chainId, {
1133
+ tokens: [
1134
+ {
1135
+ address: Addresses.pathUsd,
1136
+ currency: 'USD',
1137
+ decimals: 6,
1138
+ name: 'PathUSD',
1139
+ symbol: 'pathUSD',
1140
+ },
1141
+ ],
1142
+ })
1143
+ const client = TestApp.client({ db, fx: { oracle: fixed } })
1144
+
1145
+ const response = await client.v1.exchange.quotes.$post(
1146
+ {
1147
+ json: {
1148
+ amount: '1000000',
1149
+ destinationToken: Addresses.pathUsd,
1150
+ mode: 'exactSource',
1151
+ slippageBps: 50,
1152
+ sourceToken: baseToken,
1153
+ },
1154
+ query: { denomination: 'aud' },
1155
+ },
1156
+ TestApp.auth,
1157
+ )
1158
+ expect(response.status).toBe(200)
1159
+ const body = await TestApp.json(response, Exchanges.schema.createQuote.Response)
1160
+
1161
+ expect(body.sourceAmount.valuation).toBeNull()
1162
+ expect(body.destinationAmount.valuation).toEqual({
1163
+ amount: core_Value.format((BigInt(body.destinationAmount.baseUnits) * 16n) / 10n, 6),
1164
+ currency: 'AUD',
1165
+ })
1166
+ expect(body.meta?.pricing).toMatchInlineSnapshot(`
1167
+ {
1168
+ "asOf": "2026-01-01",
1169
+ "basis": "nominal",
1170
+ "source": "fixed",
1171
+ }
1172
+ `)
1173
+ }, 60_000)
1174
+
1175
+ test('returns a client error when the quote amount overflows', async () => {
1176
+ const response = await TestApp.client().v1.exchange.quotes.$post(
1177
+ {
1178
+ json: {
1179
+ amount: (2n ** 128n - 1n).toString(),
1180
+ destinationToken: baseToken,
1181
+ mode: 'exactSource',
1182
+ slippageBps: 50,
1183
+ sourceToken: Addresses.pathUsd,
1184
+ },
1185
+ query: {},
1186
+ },
1187
+ TestApp.auth,
1188
+ )
1189
+ expect(response.status).toBe(400)
1190
+ expect(await response.json()).toMatchObject({
1191
+ error: { code: 'quote_amount_out_of_range' },
1192
+ })
1193
+ }, 60_000)
1194
+
1195
+ test('returns quote unavailable when a route token is paused', async () => {
1196
+ const rpc = Relay.getClient({ account })
1197
+ await Actions.token.pauseSync(rpc, { token: baseToken })
1198
+ try {
1199
+ const response = await TestApp.client().v1.exchange.quotes.$post(
1200
+ {
1201
+ json: {
1202
+ amount: '1',
1203
+ destinationToken: Addresses.pathUsd,
1204
+ mode: 'exactSource',
1205
+ slippageBps: 50,
1206
+ sourceToken: baseToken,
1207
+ },
1208
+ query: {},
1209
+ },
1210
+ TestApp.auth,
1211
+ )
1212
+ expect(response.status).toBe(404)
1213
+ expect(await response.json()).toMatchObject({ error: { code: 'quote_not_available' } })
1214
+ } finally {
1215
+ await Actions.token.unpauseSync(rpc, { token: baseToken })
1216
+ }
1217
+ }, 60_000)
1218
+ })
1219
+ })
1220
+
690
1221
  describe('GET /exchange/orders', () => {
691
1222
  type GlobalOrdersResponse = z.output<typeof Exchanges.schema.getOrders.Response>
692
1223