tapimo 0.4.4 → 0.5.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 (162) hide show
  1. package/dist/App.d.ts +7403 -6127
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +4 -0
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +18 -1
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +24 -3
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/admin/apps/verified-tokens.d.ts +115 -115
  10. package/dist/apps/data/App.d.ts +1838 -1534
  11. package/dist/apps/data/App.d.ts.map +1 -1
  12. package/dist/apps/data/App.js +135 -44
  13. package/dist/apps/data/App.js.map +1 -1
  14. package/dist/apps/data/{FundingRouteProviders.d.ts → FundingQuoteProviders.d.ts} +7 -7
  15. package/dist/apps/data/{FundingRouteProviders.d.ts.map → FundingQuoteProviders.d.ts.map} +1 -1
  16. package/dist/apps/data/{FundingRouteProviders.js → FundingQuoteProviders.js} +36 -91
  17. package/dist/apps/data/FundingQuoteProviders.js.map +1 -0
  18. package/dist/apps/data/index.d.ts +1 -1
  19. package/dist/apps/data/index.js +1 -1
  20. package/dist/apps/data/routes/activities.d.ts +242 -242
  21. package/dist/apps/data/routes/addresses.d.ts +227 -227
  22. package/dist/apps/data/routes/balances.d.ts +12 -12
  23. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  24. package/dist/apps/data/routes/balances.js +67 -149
  25. package/dist/apps/data/routes/balances.js.map +1 -1
  26. package/dist/apps/data/routes/blocks.d.ts +125 -126
  27. package/dist/apps/data/routes/blocks.d.ts.map +1 -1
  28. package/dist/apps/data/routes/blocks.js +3 -5
  29. package/dist/apps/data/routes/blocks.js.map +1 -1
  30. package/dist/apps/data/routes/coingecko.d.ts +55 -54
  31. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  32. package/dist/apps/data/routes/coingecko.js +3 -8
  33. package/dist/apps/data/routes/coingecko.js.map +1 -1
  34. package/dist/apps/data/routes/funding.d.ts +207 -388
  35. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  36. package/dist/apps/data/routes/funding.js +143 -221
  37. package/dist/apps/data/routes/funding.js.map +1 -1
  38. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  39. package/dist/apps/data/routes/rpc.js +86 -16
  40. package/dist/apps/data/routes/rpc.js.map +1 -1
  41. package/dist/apps/data/routes/tokenlist.d.ts +33 -33
  42. package/dist/apps/data/routes/transactions.d.ts +281 -281
  43. package/dist/apps/data/routes/transfers.d.ts +58 -58
  44. package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
  45. package/dist/apps/data/routes/webhooks.d.ts +340 -0
  46. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  47. package/dist/apps/data/routes/webhooks.js +13 -1
  48. package/dist/apps/data/routes/webhooks.js.map +1 -1
  49. package/dist/apps/index.d.ts +1 -1
  50. package/dist/apps/index.js +1 -1
  51. package/dist/apps/management/App.d.ts +1090 -108
  52. package/dist/apps/management/App.d.ts.map +1 -1
  53. package/dist/apps/management/App.js +4 -0
  54. package/dist/apps/management/App.js.map +1 -1
  55. package/dist/apps/management/routes/api-keys.d.ts +118 -118
  56. package/dist/apps/management/routes/api-keys.js +2 -2
  57. package/dist/apps/management/routes/api-keys.js.map +1 -1
  58. package/dist/apps/management/routes/billing.d.ts +207 -207
  59. package/dist/apps/management/routes/invite-links.d.ts +1100 -0
  60. package/dist/apps/management/routes/invite-links.d.ts.map +1 -0
  61. package/dist/apps/management/routes/invite-links.js +411 -0
  62. package/dist/apps/management/routes/invite-links.js.map +1 -0
  63. package/dist/apps/management/routes/me.d.ts +17 -17
  64. package/dist/apps/management/routes/members.d.ts +71 -71
  65. package/dist/apps/management/routes/orgs.d.ts +12 -12
  66. package/dist/apps/management/routes/projects.d.ts +70 -70
  67. package/dist/apps/management/routes/scopes.d.ts +11 -11
  68. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  69. package/dist/apps/management/routes/scopes.js +2 -2
  70. package/dist/apps/management/routes/scopes.js.map +1 -1
  71. package/dist/apps/management/routes/usage.d.ts +75 -75
  72. package/dist/apps/relay/App.d.ts.map +1 -1
  73. package/dist/apps/relay/App.js +42 -4
  74. package/dist/apps/relay/App.js.map +1 -1
  75. package/dist/db/Db.d.ts +3 -0
  76. package/dist/db/Db.d.ts.map +1 -1
  77. package/dist/db/Db.js +57 -0
  78. package/dist/db/Db.js.map +1 -1
  79. package/dist/db/Schema.d.ts +31 -0
  80. package/dist/db/Schema.d.ts.map +1 -1
  81. package/dist/db/Schema.js +31 -0
  82. package/dist/db/Schema.js.map +1 -1
  83. package/dist/db/tables/inviteLinks.d.ts +42 -0
  84. package/dist/db/tables/inviteLinks.d.ts.map +1 -0
  85. package/dist/db/tables/inviteLinks.js +148 -0
  86. package/dist/db/tables/inviteLinks.js.map +1 -0
  87. package/dist/db/tables/organizations.d.ts.map +1 -1
  88. package/dist/db/tables/organizations.js +2 -0
  89. package/dist/db/tables/organizations.js.map +1 -1
  90. package/dist/handlers/relay.d.ts.map +1 -1
  91. package/dist/handlers/relay.js +13 -3
  92. package/dist/handlers/relay.js.map +1 -1
  93. package/dist/internal/Auth.d.ts +9 -1
  94. package/dist/internal/Auth.d.ts.map +1 -1
  95. package/dist/internal/Auth.js +130 -10
  96. package/dist/internal/Auth.js.map +1 -1
  97. package/dist/internal/EdgeCache.d.ts +3 -1
  98. package/dist/internal/EdgeCache.d.ts.map +1 -1
  99. package/dist/internal/EdgeCache.js +17 -2
  100. package/dist/internal/EdgeCache.js.map +1 -1
  101. package/dist/internal/{FundingRoutes.d.ts → FundingQuotes.d.ts} +185 -428
  102. package/dist/internal/FundingQuotes.d.ts.map +1 -0
  103. package/dist/internal/{FundingRoutes.js → FundingQuotes.js} +93 -218
  104. package/dist/internal/FundingQuotes.js.map +1 -0
  105. package/dist/internal/Viem.d.ts +3 -1
  106. package/dist/internal/Viem.d.ts.map +1 -1
  107. package/dist/internal/Viem.js +11 -2
  108. package/dist/internal/Viem.js.map +1 -1
  109. package/dist/internal/index.d.ts +1 -1
  110. package/dist/internal/index.js +1 -1
  111. package/package.json +1 -1
  112. package/src/ApiKeys.test.ts +23 -0
  113. package/src/App.test-d.ts +2 -0
  114. package/src/App.ts +6 -0
  115. package/src/Client.test-d.ts +20 -11
  116. package/src/Scope.test.ts +1 -0
  117. package/src/Scope.ts +30 -4
  118. package/src/apps/data/App.test.ts +354 -0
  119. package/src/apps/data/App.ts +159 -52
  120. package/src/apps/data/FundingQuoteProviders.test.ts +151 -0
  121. package/src/apps/data/{FundingRouteProviders.ts → FundingQuoteProviders.ts} +40 -101
  122. package/src/apps/data/index.ts +1 -1
  123. package/src/apps/data/routes/balances.test.ts +64 -18
  124. package/src/apps/data/routes/balances.ts +76 -177
  125. package/src/apps/data/routes/blocks.ts +3 -5
  126. package/src/apps/data/routes/coingecko.ts +3 -8
  127. package/src/apps/data/routes/funding.test.ts +151 -206
  128. package/src/apps/data/routes/funding.ts +156 -291
  129. package/src/apps/data/routes/indexer.test.ts +97 -1
  130. package/src/apps/data/routes/rpc.test.ts +219 -0
  131. package/src/apps/data/routes/rpc.ts +92 -17
  132. package/src/apps/data/routes/webhooks.test.ts +21 -0
  133. package/src/apps/data/routes/webhooks.ts +13 -1
  134. package/src/apps/index.ts +1 -1
  135. package/src/apps/management/App.ts +4 -0
  136. package/src/apps/management/routes/api-keys.test.ts +47 -3
  137. package/src/apps/management/routes/api-keys.ts +2 -2
  138. package/src/apps/management/routes/invite-links.test.ts +385 -0
  139. package/src/apps/management/routes/invite-links.ts +570 -0
  140. package/src/apps/management/routes/scopes.test.ts +3 -2
  141. package/src/apps/management/routes/scopes.ts +4 -2
  142. package/src/apps/relay/App.test.ts +324 -4
  143. package/src/apps/relay/App.ts +40 -4
  144. package/src/db/Db.ts +65 -0
  145. package/src/db/Schema.ts +39 -0
  146. package/src/db/tables/inviteLinks.ts +187 -0
  147. package/src/db/tables/organizations.ts +2 -0
  148. package/src/handlers/relay.ts +16 -4
  149. package/src/internal/Auth.test.ts +132 -22
  150. package/src/internal/Auth.ts +171 -14
  151. package/src/internal/EdgeCache.test.ts +24 -3
  152. package/src/internal/EdgeCache.ts +18 -2
  153. package/src/internal/FundingQuotes.test.ts +174 -0
  154. package/src/internal/{FundingRoutes.ts → FundingQuotes.ts} +153 -312
  155. package/src/internal/Viem.ts +14 -5
  156. package/src/internal/index.test.ts +1 -1
  157. package/src/internal/index.ts +1 -1
  158. package/dist/apps/data/FundingRouteProviders.js.map +0 -1
  159. package/dist/internal/FundingRoutes.d.ts.map +0 -1
  160. package/dist/internal/FundingRoutes.js.map +0 -1
  161. package/src/apps/data/FundingRouteProviders.test.ts +0 -225
  162. package/src/internal/FundingRoutes.test.ts +0 -213
@@ -1,279 +1,224 @@
1
1
  import { Schema } from 'tapimo'
2
2
 
3
+ import type * as FundingQuotes from '../../../internal/FundingQuotes.js'
3
4
  import * as TestApp from '../../../../test/App.js'
4
5
  import * as Funding from './funding.js'
5
6
 
6
- describe('GET /funding/routes', () => {
7
- test('returns static inventory-backed route candidates', async () => {
8
- const response = await TestApp.client().v1.funding.routes.$get(
9
- {
10
- query: {
11
- destinationToken: 'USDC.e',
12
- sourceAmount: '1000000',
13
- sourceChain: 'base',
14
- sourceToken: 'USDC',
15
- },
16
- },
17
- TestApp.auth,
18
- )
7
+ describe('GET /funding/quotes', () => {
8
+ test('returns successful live quotes with output amounts', async () => {
9
+ const app = TestApp.create({ funding: { adapters: { relay: relayAdapter() } } })
10
+ const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
19
11
  expect(response.status).toBe(200)
20
- const body = await TestApp.json(response, Funding.schema.listFundingRoutes.Response)
12
+ expect(response.headers.get('cache-control')).toBe('no-store')
13
+ const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
21
14
 
22
- expect(body.data.map((route) => route.provider.id)).toEqual(['relay', 'bridge', 'bungee'])
15
+ expect(body.data).toHaveLength(1)
23
16
  expect(body.data[0]).toMatchObject({
24
- destinationChain: { id: 'eip155:4217', name: 'Tempo' },
25
- destinationToken: { symbol: 'USDC.e' },
26
17
  id: 'base-usdc-tempo-usdce-relay',
27
- quality: { liquiditySource: 'staticInventory', tier: 'unknown' },
18
+ provider: { id: 'relay' },
19
+ quality: { liquiditySource: 'providerQuote', tier: 'liquid' },
20
+ quote: {
21
+ inputAmount: '1000000',
22
+ outputAmount: '990000',
23
+ outputAmountFormatted: '0.99',
24
+ },
28
25
  sourceAmount: { amount: '1000000', formatted: '1' },
29
- sourceChain: { id: 'eip155:8453', name: 'Base' },
30
- sourceToken: { symbol: 'USDC' },
31
- status: 'unknown',
32
26
  })
33
27
  })
34
28
 
35
- test('enriches route candidates with configured provider adapters', async () => {
29
+ test('queries only the requested provider', async () => {
30
+ let calls = 0
36
31
  const app = TestApp.create({
37
- fundingRoutes: {
32
+ funding: {
38
33
  adapters: {
39
- relay: {
40
- async probeQuote(input, signal) {
41
- expect(signal.aborted).toBe(false)
42
- expect(input.cache.bypass).toBe(true)
43
- expect(input.include).toEqual(['quotes'])
44
- expect(input.route.id).toBe('base-usdc-tempo-usdce-relay')
45
- expect(input.sourceAmount.amount).toBe('1000000')
46
- expect(input.sourceChain.id).toBe('eip155:8453')
47
- expect(input.sourceToken.symbol).toBe('USDC')
48
- return {
49
- expiresAt: '2026-01-01T00:00:30Z',
50
- limits: {
51
- instantSourceAmount: '1000000000',
52
- maxSourceAmount: '100000000000',
53
- minSourceAmount: '500000',
54
- },
55
- minimumOutputAmount: '980000',
56
- outputAmount: '990000',
57
- outputAmountFormatted: '0.99',
58
- quality: {
59
- availableVolumeUsd: '1000.00',
60
- estimatedSeconds: 4,
61
- feeBps: 12,
62
- historicalVolumeUsd24h: '100000.00',
63
- instantVolumeUsd: '1000.00',
64
- liquiditySource: 'providerQuote',
65
- score: 91,
66
- sourceDetail: 'providerEndpoint',
67
- tier: 'liquid',
68
- },
69
- sampledAt: '2026-01-01T00:00:00Z',
70
- status: 'available',
71
- }
72
- },
73
- },
34
+ relay: relayAdapter(() => {
35
+ calls += 1
36
+ }),
74
37
  },
75
38
  },
76
39
  })
40
+ const response = await app.request(quoteUrl({ ...baseUsdcQuery, provider: 'relay' }), {
41
+ headers: TestApp.auth.headers,
42
+ })
77
43
 
78
- const response = await app.request(
79
- '/v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000&provider=relay&include=quotes',
80
- { headers: { ...TestApp.auth.headers, 'Cache-Control': 'no-cache' } },
81
- )
82
44
  expect(response.status).toBe(200)
83
- const body = await TestApp.json(response, Funding.schema.listFundingRoutes.Response)
45
+ expect(calls).toBe(1)
46
+ })
84
47
 
85
- expect(body.data).toHaveLength(1)
86
- expect(body.data[0]).toMatchObject({
87
- errorCode: null,
88
- id: 'base-usdc-tempo-usdce-relay',
89
- quality: {
90
- availableVolumeUsd: '1000.00',
91
- liquiditySource: 'providerQuote',
92
- sourceDetail: 'providerEndpoint',
93
- tier: 'liquid',
94
- },
95
- quote: {
96
- expiresAt: '2026-01-01T00:00:30Z',
97
- inputAmount: '1000000',
98
- limits: {
99
- instantSourceAmount: '1000000000',
100
- maxSourceAmount: '100000000000',
101
- minSourceAmount: '500000',
102
- },
103
- minimumOutputAmount: '980000',
104
- outputAmount: '990000',
105
- outputAmountFormatted: '0.99',
106
- sampledAt: '2026-01-01T00:00:00Z',
107
- },
108
- status: 'available',
48
+ test('returns an empty list when no provider is configured', async () => {
49
+ const response = await TestApp.create().request(quoteUrl(), {
50
+ headers: TestApp.auth.headers,
109
51
  })
52
+ expect(response.status).toBe(200)
53
+ const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
54
+ expect(body.data).toEqual([])
110
55
  })
111
56
 
112
- test('converts decimal source amounts after resolving the source token', async () => {
113
- const response = await TestApp.client().v1.funding.routes.$get(
114
- {
115
- query: {
116
- provider: 'relay',
117
- sourceAmount: '1.25',
118
- sourceAmountUnits: 'decimal',
119
- sourceChain: 'eip155:8453',
120
- sourceToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
57
+ test('returns an empty list when the provider reports no quote', async () => {
58
+ const app = TestApp.create({
59
+ funding: {
60
+ adapters: {
61
+ relay: {
62
+ async probeQuote() {
63
+ return { sampledAt: '2026-01-01T00:00:00Z', status: 'unavailable' }
64
+ },
65
+ },
121
66
  },
122
67
  },
123
- TestApp.auth,
124
- )
125
- expect(response.status).toBe(200)
126
- const body = await TestApp.json(response, Funding.schema.listFundingRoutes.Response)
127
-
128
- expect(body.data).toHaveLength(1)
129
- expect(body.data[0]?.sourceAmount).toMatchObject({
130
- amount: '1250000',
131
- formatted: '1.25',
132
68
  })
69
+ const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
70
+ expect(response.status).toBe(200)
71
+ const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
72
+ expect(body.data).toEqual([])
133
73
  })
134
74
 
135
- test('does not case-fold non-EVM source token identifiers', async () => {
136
- const solanaMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
137
- const exactResponse = await TestApp.client().v1.funding.routes.$get(
138
- {
139
- query: {
140
- sourceAmount: '1000000',
141
- sourceChain: 'solana',
142
- sourceToken: solanaMint,
143
- },
144
- },
145
- TestApp.auth,
146
- )
147
- expect(exactResponse.status).toBe(200)
148
- const exactBody = await TestApp.json(exactResponse, Funding.schema.listFundingRoutes.Response)
149
- expect(exactBody.data.map((route) => route.provider.id)).toEqual(['bridge'])
150
-
151
- const lowercasedSolanaResponse = await TestApp.client().v1.funding.routes.$get(
152
- {
153
- query: {
154
- sourceAmount: '1000000',
155
- sourceChain: 'solana',
156
- sourceToken: solanaMint.toLowerCase(),
75
+ test('returns 502 when every matching provider fails', async () => {
76
+ const app = TestApp.create({
77
+ funding: {
78
+ adapters: {
79
+ relay: {
80
+ async probeQuote() {
81
+ throw new Error('upstream unavailable')
82
+ },
83
+ },
157
84
  },
158
85
  },
159
- TestApp.auth,
160
- )
161
- expect(lowercasedSolanaResponse.status).toBe(200)
162
- const lowercasedSolanaBody = await TestApp.json(
163
- lowercasedSolanaResponse,
164
- Funding.schema.listFundingRoutes.Response,
165
- )
166
- expect(lowercasedSolanaBody.data).toEqual([])
86
+ })
87
+ const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
88
+ expect(response.status).toBe(502)
89
+ const body = await TestApp.json(response, Schema.ErrorResponse)
90
+ expect(body.error.code).toBe('upstream_error')
91
+ })
167
92
 
168
- const lowercasedTronResponse = await TestApp.client().v1.funding.routes.$get(
169
- {
170
- query: {
171
- sourceAmount: '1000000',
172
- sourceChain: 'tron',
173
- sourceToken: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'.toLowerCase(),
93
+ test('rejects successful provider results without output amounts', async () => {
94
+ const app = TestApp.create({
95
+ funding: {
96
+ adapters: {
97
+ relay: {
98
+ async probeQuote() {
99
+ return { sampledAt: '2026-01-01T00:00:00Z', status: 'available' }
100
+ },
101
+ },
174
102
  },
175
103
  },
176
- TestApp.auth,
177
- )
178
- expect(lowercasedTronResponse.status).toBe(200)
179
- const lowercasedTronBody = await TestApp.json(
180
- lowercasedTronResponse,
181
- Funding.schema.listFundingRoutes.Response,
182
- )
183
- expect(lowercasedTronBody.data).toEqual([])
104
+ })
105
+ const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
106
+ expect(response.status).toBe(502)
184
107
  })
185
108
 
186
- test('returns no candidates for an unsupported source token', async () => {
187
- const response = await TestApp.client().v1.funding.routes.$get(
188
- {
189
- query: {
190
- sourceAmount: '1.234567890',
191
- sourceAmountUnits: 'decimal',
192
- sourceChain: 'base',
193
- sourceToken: 'DAI',
194
- },
195
- },
196
- TestApp.auth,
109
+ test('rejects invalid query parameters', async () => {
110
+ const response = await TestApp.create().request(
111
+ quoteUrl({ sourceAmount: '1000000', sourceChain: 'base' }),
112
+ { headers: TestApp.auth.headers },
197
113
  )
198
- expect(response.status).toBe(200)
199
- const body = await TestApp.json(response, Funding.schema.listFundingRoutes.Response)
200
- expect(body.data).toEqual([])
114
+ expect(response.status).toBe(400)
115
+ const body = await TestApp.json(response, Schema.ErrorResponse)
116
+ expect(body.error.code).toBe('query_invalid')
201
117
  })
202
118
 
203
- test('rejects amounts that cannot be represented in source decimals', async () => {
204
- const response = await TestApp.client().v1.funding.routes.$get(
205
- {
206
- query: {
207
- sourceAmount: '1.0000001',
208
- sourceAmountUnits: 'decimal',
209
- sourceChain: 'base',
210
- sourceToken: 'USDC',
211
- },
212
- },
213
- TestApp.auth,
119
+ test('rejects unsupported providers', async () => {
120
+ const response = await TestApp.create().request(
121
+ quoteUrl({ ...baseUsdcQuery, provider: 'bridge' }),
122
+ { headers: TestApp.auth.headers },
214
123
  )
215
124
  expect(response.status).toBe(400)
216
125
  const body = await TestApp.json(response, Schema.ErrorResponse)
217
126
  expect(body.error.code).toBe('query_invalid')
218
127
  })
219
128
 
220
- test('rejects unsupported include parameters', async () => {
129
+ test('rejects source chains without a live quote adapter', async () => {
221
130
  const response = await TestApp.create().request(
222
- '/v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000&include=debug',
131
+ quoteUrl({ ...baseUsdcQuery, sourceChain: 'solana' }),
223
132
  { headers: TestApp.auth.headers },
224
133
  )
225
134
  expect(response.status).toBe(400)
226
135
  const body = await TestApp.json(response, Schema.ErrorResponse)
227
136
  expect(body.error.code).toBe('query_invalid')
228
137
  })
138
+
139
+ test('does not expose the removed routes endpoint', async () => {
140
+ const response = await TestApp.create().request(
141
+ '/v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000',
142
+ { headers: TestApp.auth.headers },
143
+ )
144
+ expect(response.status).toBe(404)
145
+ })
229
146
  })
230
147
 
231
148
  describe('GET /funding/chains', () => {
232
- test('lists source chain and stablecoin inventory', async () => {
149
+ test('lists only inputs supported by quote providers', async () => {
233
150
  const response = await TestApp.client().v1.funding.chains.$get(undefined, TestApp.auth)
234
151
  expect(response.status).toBe(200)
235
152
  const body = await TestApp.json(response, Funding.schema.listFundingChains.Response)
236
153
 
237
- expect(body.data.map((chain) => chain.id)).toEqual([
238
- 'eip155:1',
239
- 'eip155:8453',
240
- 'eip155:56',
241
- 'solana:mainnet',
242
- 'tron:mainnet',
243
- ])
244
- expect(body.data.find((chain) => chain.id === 'solana:mainnet')?.tokens).toEqual(
245
- expect.arrayContaining([expect.objectContaining({ symbol: 'USDC', standard: 'SPL' })]),
154
+ expect(body.data.map((chain) => chain.id)).toEqual(['eip155:1', 'eip155:8453', 'eip155:56'])
155
+ expect(body.data.flatMap((chain) => chain.tokens.map((token) => token.symbol))).toEqual(
156
+ expect.arrayContaining(['USDC', 'USDT']),
246
157
  )
247
158
  })
248
159
  })
249
160
 
250
- describe('GET /providers', () => {
251
- test('lists providers filtered by capability', async () => {
252
- const response = await TestApp.client().v1.providers.$get(
253
- { query: { capability: 'fundingRoutes' } },
254
- TestApp.auth,
255
- )
161
+ describe('GET /funding/providers', () => {
162
+ test('lists quote providers and their configuration status', async () => {
163
+ const app = TestApp.create({ funding: { adapters: { relay: relayAdapter() } } })
164
+ const response = await app.request('/v1/funding/providers', {
165
+ headers: TestApp.auth.headers,
166
+ })
256
167
  expect(response.status).toBe(200)
257
168
  const body = await TestApp.json(response, Funding.schema.listProviders.Response)
258
169
 
259
- expect(body.data.map((provider) => provider.id)).toEqual(['relay', 'bridge', 'bungee', 'lifi'])
260
- expect(body.data.every((provider) => provider.capabilities.includes('fundingRoutes'))).toBe(
261
- true,
262
- )
263
- expect(body.data.map((provider) => provider.status)).toEqual([
264
- 'inventoryOnly',
265
- 'inventoryOnly',
266
- 'inventoryOnly',
267
- 'planned',
170
+ expect(body.data).toEqual([
171
+ {
172
+ id: 'relay',
173
+ kind: 'solverNetwork',
174
+ name: 'Relay',
175
+ requiresApiKey: false,
176
+ status: 'configured',
177
+ },
268
178
  ])
269
179
  })
270
180
 
271
- test('rejects unsupported provider capabilities', async () => {
272
- const response = await TestApp.create().request('/v1/providers?capability=settlement', {
181
+ test('does not expose the removed generic provider endpoint', async () => {
182
+ const response = await TestApp.create().request('/v1/providers', {
273
183
  headers: TestApp.auth.headers,
274
184
  })
275
- expect(response.status).toBe(400)
276
- const body = await TestApp.json(response, Schema.ErrorResponse)
277
- expect(body.error.code).toBe('query_invalid')
185
+ expect(response.status).toBe(404)
278
186
  })
279
187
  })
188
+
189
+ const baseUsdcQuery = {
190
+ sourceAmount: '1000000',
191
+ sourceChain: 'base',
192
+ sourceToken: 'USDC',
193
+ } as const
194
+
195
+ type QuoteQuery = Record<string, string>
196
+
197
+ function quoteUrl(query: QuoteQuery = baseUsdcQuery) {
198
+ return `/v1/funding/quotes?${new URLSearchParams(
199
+ Object.entries(query).map(([key, value]) => [key, String(value)]),
200
+ )}`
201
+ }
202
+
203
+ function relayAdapter(onProbe: () => void = () => {}): FundingQuotes.ProviderAdapter {
204
+ return {
205
+ async probeQuote(input, signal) {
206
+ onProbe()
207
+ expect(signal.aborted).toBe(false)
208
+ expect(input.candidate.id).toBe('base-usdc-tempo-usdce-relay')
209
+ return {
210
+ minimumOutputAmount: '980000',
211
+ outputAmount: '990000',
212
+ outputAmountFormatted: '0.99',
213
+ quality: {
214
+ estimatedSeconds: 4,
215
+ liquiditySource: 'providerQuote',
216
+ sourceDetail: 'relay:quote-v2',
217
+ tier: 'liquid',
218
+ },
219
+ sampledAt: '2026-01-01T00:00:00Z',
220
+ status: 'available',
221
+ }
222
+ },
223
+ }
224
+ }