tapimo 0.4.3 → 0.4.4

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 (77) hide show
  1. package/dist/App.d.ts +2491 -2861
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/admin/apps/verified-tokens.d.ts +57 -57
  4. package/dist/apps/data/App.d.ts +2354 -2724
  5. package/dist/apps/data/App.d.ts.map +1 -1
  6. package/dist/apps/data/FundingRouteProviders.d.ts +3 -110
  7. package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -1
  8. package/dist/apps/data/FundingRouteProviders.js +1 -302
  9. package/dist/apps/data/FundingRouteProviders.js.map +1 -1
  10. package/dist/apps/data/routes/exchanges.d.ts +455 -455
  11. package/dist/apps/data/routes/fee-amm.d.ts +92 -92
  12. package/dist/apps/data/routes/funding.d.ts +138 -730
  13. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  14. package/dist/apps/data/routes/funding.js +7 -241
  15. package/dist/apps/data/routes/funding.js.map +1 -1
  16. package/dist/apps/data/routes/tokenlist.d.ts +12 -12
  17. package/dist/apps/data/routes/transactions.d.ts +281 -281
  18. package/dist/apps/data/routes/transfers.d.ts +58 -58
  19. package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
  20. package/dist/apps/management/App.d.ts +855 -855
  21. package/dist/apps/management/routes/api-keys.d.ts +40 -40
  22. package/dist/apps/management/routes/billing.d.ts +207 -207
  23. package/dist/apps/management/routes/me.d.ts +6 -6
  24. package/dist/apps/management/routes/members.d.ts +30 -30
  25. package/dist/apps/management/routes/orgs.d.ts +92 -92
  26. package/dist/apps/management/routes/projects.d.ts +28 -28
  27. package/dist/apps/management/routes/usage.d.ts +24 -24
  28. package/dist/apps/mcp.d.ts.map +1 -1
  29. package/dist/apps/mcp.js +3 -1
  30. package/dist/apps/mcp.js.map +1 -1
  31. package/dist/cloudflare.d.ts +2 -0
  32. package/dist/cloudflare.d.ts.map +1 -1
  33. package/dist/cloudflare.js +4 -0
  34. package/dist/cloudflare.js.map +1 -1
  35. package/dist/internal/Auth.d.ts +33 -22
  36. package/dist/internal/Auth.d.ts.map +1 -1
  37. package/dist/internal/Auth.js +40 -11
  38. package/dist/internal/Auth.js.map +1 -1
  39. package/dist/internal/Errors.d.ts +6 -0
  40. package/dist/internal/Errors.d.ts.map +1 -0
  41. package/dist/internal/Errors.js +16 -0
  42. package/dist/internal/Errors.js.map +1 -0
  43. package/dist/internal/FundingRoutes.d.ts +15 -213
  44. package/dist/internal/FundingRoutes.d.ts.map +1 -1
  45. package/dist/internal/FundingRoutes.js +1 -72
  46. package/dist/internal/FundingRoutes.js.map +1 -1
  47. package/dist/internal/Store.d.ts +61 -16
  48. package/dist/internal/Store.d.ts.map +1 -1
  49. package/dist/internal/Store.js +72 -17
  50. package/dist/internal/Store.js.map +1 -1
  51. package/dist/internal/Viem.d.ts +1 -1
  52. package/dist/internal/index.d.ts +27 -0
  53. package/dist/internal/index.d.ts.map +1 -0
  54. package/dist/internal/index.js +27 -0
  55. package/dist/internal/index.js.map +1 -0
  56. package/package.json +8 -3
  57. package/src/App.test.ts +60 -4
  58. package/src/Client.test-d.ts +0 -47
  59. package/src/apps/data/FundingRouteProviders.test.ts +37 -297
  60. package/src/apps/data/FundingRouteProviders.ts +3 -435
  61. package/src/apps/data/routes/funding.test.ts +7 -109
  62. package/src/apps/data/routes/funding.ts +8 -323
  63. package/src/apps/data/routes/indexer.test.ts +2 -2
  64. package/src/apps/data/routes/webhooks.test.ts +2 -2
  65. package/src/apps/mcp.test.ts +55 -14
  66. package/src/apps/mcp.ts +3 -1
  67. package/src/cloudflare.ts +5 -0
  68. package/src/internal/Auth.test.ts +397 -16
  69. package/src/internal/Auth.ts +76 -32
  70. package/src/internal/FundingRoutes.test.ts +8 -34
  71. package/src/internal/FundingRoutes.ts +1 -174
  72. package/src/internal/Log.test.ts +4 -4
  73. package/src/internal/Store.test-d.ts +15 -0
  74. package/src/internal/Store.test.ts +153 -2
  75. package/src/internal/Store.ts +157 -41
  76. package/src/internal/index.test.ts +34 -0
  77. package/src/internal/index.ts +26 -0
@@ -1,248 +1,26 @@
1
- import * as z from 'zod/mini'
2
-
3
1
  import * as FundingRoutes from '../../internal/FundingRoutes.js'
4
2
 
5
3
  type Fetch = typeof globalThis.fetch
6
4
 
7
5
  const defaultFetch: Fetch = (input, init) => globalThis.fetch(input, init)
8
6
 
9
- const defaultAcrossBaseUrl = 'https://app.across.to/api'
10
7
  const defaultRelayBaseUrl = 'https://api.relay.link'
11
8
 
12
- namespace schema {
13
- const DecimalString = z.string().check(z.regex(/^\d+(\.\d+)?$/))
14
- const IntegerString = z.string().check(z.regex(/^\d+$/))
15
-
16
- const AcrossToken = z.object({
17
- address: z.string(),
18
- chainId: z.number().check(z.int(), z.nonnegative()),
19
- decimals: z.number().check(z.int(), z.nonnegative()),
20
- symbol: z.string(),
21
- })
22
-
23
- const AcrossFee = z.object({
24
- amount: IntegerString,
25
- amountUsd: DecimalString,
26
- pct: IntegerString,
27
- token: AcrossToken,
28
- })
29
-
30
- export const AcrossQuote = z.object({
31
- amountType: z.literal('exactInput'),
32
- expectedFillTime: z.number().check(z.int(), z.nonnegative()),
33
- expectedOutputAmount: IntegerString,
34
- fees: z.object({
35
- total: AcrossFee,
36
- totalMax: AcrossFee,
37
- }),
38
- inputAmount: IntegerString,
39
- inputToken: AcrossToken,
40
- minOutputAmount: IntegerString,
41
- outputToken: AcrossToken,
42
- quoteExpiryTimestamp: z.number().check(z.int(), z.nonnegative()),
43
- })
44
- }
45
-
46
9
  /** Creates the live funding route adapters configured for this deployment. */
47
10
  export function createAdapters(options: createAdapters.Options = {}) {
48
- return {
49
- ...(options.across ? { across: across(options.across) } : {}),
50
- ...(options.relay ? { relay: relay(options.relay) } : {}),
51
- } satisfies FundingRoutes.ProviderAdapters
11
+ return (
12
+ options.relay ? { relay: relay(options.relay) } : {}
13
+ ) satisfies FundingRoutes.ProviderAdapters
52
14
  }
53
15
 
54
16
  export declare namespace createAdapters {
55
17
  /** Provider adapter factory options. Omit a provider to keep its routes static. */
56
18
  type Options = {
57
- /** Across discovery adapter options. */
58
- across?: across.Options | false | undefined
59
19
  /** Relay quote adapter options. */
60
20
  relay?: relay.Options | false | undefined
61
21
  }
62
22
  }
63
23
 
64
- /** Creates an Across discovery adapter backed by `/suggested-fees`. */
65
- export function across(options: across.Options): FundingRoutes.ProviderAdapter {
66
- const baseUrl = normalizeBaseUrl(options.baseUrl ?? defaultAcrossBaseUrl)
67
- const fetcher = options.fetch ?? defaultFetch
68
- const now = options.now ?? (() => new Date())
69
-
70
- return {
71
- async probeQuote({ route }, signal) {
72
- const sourceChainId = providerChainId(route.sourceChain, 'across')
73
- const destinationChainId = eip155ChainId(route.destinationChain)
74
- const url = new URL(`${baseUrl}/suggested-fees`)
75
- url.search = new URLSearchParams({
76
- allowUnmatchedDecimals: 'true',
77
- amount: route.sourceAmount.amount,
78
- destinationChainId,
79
- inputToken: route.sourceToken.address,
80
- integratorId: options.integratorId,
81
- originChainId: sourceChainId,
82
- outputToken: route.destinationToken.address,
83
- }).toString()
84
-
85
- try {
86
- const body = await requestJson(url, {
87
- fetcher,
88
- headers: { Authorization: `Bearer ${options.apiKey}` },
89
- signal,
90
- })
91
- return formatAcrossResult(route, body, now())
92
- } catch (cause) {
93
- const unavailable = providerUnavailable(cause, ['AMOUNT_TOO_HIGH', 'AMOUNT_TOO_LOW'])
94
- if (!unavailable) throw cause
95
- return unavailableResult({
96
- detail: 'across:suggested-fees',
97
- message: unavailable,
98
- now: now(),
99
- source: 'providerLimit',
100
- })
101
- }
102
- },
103
- }
104
- }
105
-
106
- /** Builds the authenticated Across `/swap/approval` request for an exact-input quote. */
107
- export function createAcrossQuoteRequest(options: createAcrossQuoteRequest.Options) {
108
- assertAcrossIntegratorId(options.integratorId)
109
- const baseUrl = normalizeBaseUrl(options.baseUrl ?? defaultAcrossBaseUrl)
110
- const sourceChainId = providerChainId(options.route.sourceChain, 'across')
111
- const destinationChainId = eip155ChainId(options.route.destinationChain)
112
- const url = new URL(`${baseUrl}/swap/approval`)
113
- url.search = new URLSearchParams({
114
- amount: options.route.sourceAmount.amount,
115
- depositor: options.depositor,
116
- destinationChainId,
117
- inputToken: options.route.sourceToken.address,
118
- integratorId: options.integratorId,
119
- originChainId: sourceChainId,
120
- outputToken: options.route.destinationToken.address,
121
- skipOriginTxEstimation: 'true',
122
- strictTradeType: 'true',
123
- tradeType: 'exactInput',
124
- }).toString()
125
- if (options.recipient !== undefined) url.searchParams.set('recipient', options.recipient)
126
- return { headers: { Authorization: `Bearer ${options.apiKey}` }, url }
127
- }
128
-
129
- export declare namespace createAcrossQuoteRequest {
130
- /** Inputs required to construct an Across exact-input quote request. */
131
- type Options = {
132
- /** Across API key sent as a bearer token. */
133
- apiKey: string
134
- /** Optional Across API base URL. */
135
- baseUrl?: string | undefined
136
- /** Address that would submit the source transaction. */
137
- depositor: string
138
- /** Across integrator id sent as the required query parameter. */
139
- integratorId: string
140
- /** Optional destination recipient. */
141
- recipient?: string | undefined
142
- /** Curated route selected for the quote. */
143
- route: FundingRoutes.RouteCandidate
144
- }
145
- }
146
-
147
- /** Creates an explicit Across quote adapter backed by `/swap/approval`. */
148
- export function acrossQuote(options: acrossQuote.Options): FundingRoutes.ProviderQuote {
149
- assertAcrossIntegratorId(options.integratorId)
150
-
151
- return async ({ depositor, recipient, route }, signal) => {
152
- const request = createAcrossQuoteRequest({
153
- apiKey: options.apiKey,
154
- baseUrl: options.baseUrl,
155
- depositor,
156
- integratorId: options.integratorId,
157
- recipient,
158
- route,
159
- })
160
-
161
- try {
162
- const body = await requestJson(request.url, {
163
- fetcher: defaultFetch,
164
- headers: request.headers,
165
- signal,
166
- })
167
- return parseAcrossQuote({ body, now: new Date(), route })
168
- } catch (cause) {
169
- if (!(cause instanceof ProviderResponseError)) throw cause
170
- throw mapAcrossQuoteError({ body: cause.body, status: cause.status })
171
- }
172
- }
173
- }
174
-
175
- export declare namespace acrossQuote {
176
- /** Across Swap API adapter options. */
177
- type Options = {
178
- /** Across API key sent as a bearer token. */
179
- apiKey: string
180
- /** Optional Across API base URL. */
181
- baseUrl?: string | undefined
182
- /** Across integrator id sent as the required query parameter. */
183
- integratorId: string
184
- }
185
- }
186
-
187
- /** Returns bounded Across quote failure details suitable for structured request logs. */
188
- export function acrossQuoteFailure(options: acrossQuoteFailure.Options): acrossQuoteFailure.Result {
189
- const { cause } = options
190
- const base = { id: 'across', operation: 'swap/approval' } as const
191
- if (cause instanceof ProviderResponseError) {
192
- const code = providerErrorCode(cause.body)
193
- return {
194
- ...(code === undefined ? {} : { code }),
195
- failure: 'http' as const,
196
- ...base,
197
- status: cause.status,
198
- }
199
- }
200
- if (cause instanceof ProviderPayloadError) return { failure: 'payload', ...base }
201
- if (cause instanceof TypeError) return { failure: 'network', ...base }
202
- if (cause instanceof Error) return { failure: 'unknown', ...base }
203
- return undefined
204
- }
205
-
206
- export declare namespace acrossQuoteFailure {
207
- /** Inputs used to classify an Across quote failure. */
208
- type Options = {
209
- /** Error thrown by the Across quote boundary. */
210
- cause: unknown
211
- }
212
-
213
- /** Bounded diagnostic fields, or undefined when the thrown value is not an error. */
214
- type Result =
215
- | {
216
- /** Stable provider error code, when one is safe to retain. */
217
- code?: string | undefined
218
- /** Failure boundary that rejected the provider request. */
219
- failure: 'http' | 'network' | 'payload' | 'unknown'
220
- /** Provider identifier. */
221
- id: 'across'
222
- /** Across operation identifier. */
223
- operation: 'swap/approval'
224
- /** Upstream HTTP status, when a response was received. */
225
- status?: number | undefined
226
- }
227
- | undefined
228
- }
229
-
230
- export declare namespace across {
231
- /** Across API adapter options. */
232
- type Options = {
233
- /** Override for tests or non-production Across environments. */
234
- baseUrl?: string | undefined
235
- /** Fetch implementation override for tests. */
236
- fetch?: Fetch | undefined
237
- /** Across API key sent as a bearer token. */
238
- apiKey: string
239
- /** Across integrator id sent as the required query parameter. */
240
- integratorId: string
241
- /** Clock override for deterministic tests. */
242
- now?: (() => Date) | undefined
243
- }
244
- }
245
-
246
24
  /** Creates a Relay quote adapter backed by `/chains/liquidity` and `/quote/v2`. */
247
25
  export function relay(options: relay.Options): FundingRoutes.ProviderAdapter {
248
26
  const baseUrl = normalizeBaseUrl(options.baseUrl ?? defaultRelayBaseUrl)
@@ -314,140 +92,6 @@ export declare namespace relay {
314
92
  }
315
93
  }
316
94
 
317
- /** Maps an Across HTTP error into the stable funding-provider error contract. */
318
- export function mapAcrossQuoteError(options: mapAcrossQuoteError.Options) {
319
- if (options.status === 429) return new FundingRoutes.ProviderRateLimitError()
320
- const cause = new ProviderResponseError(options.status, options.body)
321
- const unavailable = providerUnavailable(cause, [
322
- 'AMOUNT_TOO_HIGH',
323
- 'AMOUNT_TOO_LOW',
324
- 'INSUFFICIENT_LIQUIDITY',
325
- 'NO_QUOTES',
326
- 'NO_ROUTE',
327
- 'UNSUPPORTED_ROUTE',
328
- ])
329
- if (unavailable) return new FundingRoutes.ProviderQuoteUnavailableError()
330
- return cause
331
- }
332
-
333
- export declare namespace mapAcrossQuoteError {
334
- /** Across HTTP error response to classify. */
335
- type Options = {
336
- /** Parsed response body returned by Across. */
337
- body: unknown
338
- /** Across HTTP response status. */
339
- status: number
340
- }
341
- }
342
-
343
- /** Validates and normalizes an Across quote payload against its curated route. */
344
- export function parseAcrossQuote(
345
- options: parseAcrossQuote.Options,
346
- ): FundingRoutes.ProviderQuoteResult {
347
- const parsed = schema.AcrossQuote.safeParse(options.body)
348
- if (!parsed.success) throw new ProviderPayloadError()
349
- const response = parsed.data
350
- const total = response.fees.total
351
- const totalMax = response.fees.totalMax
352
- if (
353
- response.inputAmount !== options.route.sourceAmount.amount ||
354
- response.inputToken.address.toLowerCase() !== options.route.sourceToken.address.toLowerCase() ||
355
- response.inputToken.chainId !== Number(eip155ChainId(options.route.sourceChain)) ||
356
- response.inputToken.decimals !== options.route.sourceToken.decimals ||
357
- response.outputToken.address.toLowerCase() !==
358
- options.route.destinationToken.address.toLowerCase() ||
359
- response.outputToken.chainId !== Number(eip155ChainId(options.route.destinationChain)) ||
360
- response.outputToken.decimals !== options.route.destinationToken.decimals ||
361
- BigInt(response.expectedOutputAmount) <= 0n ||
362
- BigInt(response.minOutputAmount) <= 0n ||
363
- BigInt(response.minOutputAmount) > BigInt(response.expectedOutputAmount) ||
364
- response.quoteExpiryTimestamp * 1_000 <= options.now.getTime() ||
365
- !matchesFeeToken(total.token, options.route) ||
366
- !matchesFeeToken(totalMax.token, options.route) ||
367
- BigInt(totalMax.amount) < BigInt(total.amount) ||
368
- BigInt(totalMax.pct) < BigInt(total.pct)
369
- )
370
- throw new ProviderPayloadError()
371
- return {
372
- estimatedSeconds: response.expectedFillTime,
373
- expiresAt: isoFromUnixSeconds(String(response.quoteExpiryTimestamp)),
374
- fees: {
375
- total: formatAcrossFee(response.fees.total),
376
- totalMax: formatAcrossFee(response.fees.totalMax),
377
- },
378
- minimumOutputAmount: response.minOutputAmount,
379
- outputAmount: response.expectedOutputAmount,
380
- sampledAt: options.now.toISOString(),
381
- }
382
- }
383
-
384
- export declare namespace parseAcrossQuote {
385
- /** Inputs required to validate and normalize an Across quote payload. */
386
- type Options = {
387
- /** Untrusted JSON body returned by Across. */
388
- body: unknown
389
- /** Timestamp when Tempo sampled the response. */
390
- now: Date
391
- /** Curated route the response must match. */
392
- route: FundingRoutes.RouteCandidate
393
- }
394
- }
395
-
396
- function formatAcrossResult(
397
- route: FundingRoutes.RouteCandidate,
398
- body: unknown,
399
- now: Date,
400
- ): FundingRoutes.QuoteProbeResult {
401
- const response = record(body)
402
- const timestamp = integerString(response['timestamp'])
403
- const sampledAt = timestamp ? isoFromUnixSeconds(timestamp) : now.toISOString()
404
- const limits = acrossLimits(response['limits'])
405
- const amountStatus = statusFromLimits(route.sourceAmount.amount, limits)
406
- const status = response['isAmountTooLow'] === true ? 'unavailable' : amountStatus.status
407
- const tier = response['isAmountTooLow'] === true ? 'unavailable' : amountStatus.tier
408
- const outputAmount = decimalString(response['outputAmount'])
409
-
410
- return {
411
- expiresAt: timestamp ? isoFromUnixSeconds((BigInt(timestamp) + 600n).toString()) : null,
412
- limits,
413
- outputAmount,
414
- outputAmountFormatted: outputAmount
415
- ? decimalFromBaseUnits(outputAmount, route.destinationToken.decimals)
416
- : null,
417
- quality: {
418
- availableVolumeUsd: stablecoinUsdCapacity(route, limits?.maxSourceAmount),
419
- estimatedSeconds: nonnegativeInteger(response['estimatedFillTimeSec']),
420
- feeBps: scaledPctToBps(
421
- nestedString(response['totalRelayFee'], 'pct') ?? decimalString(response['relayFeePct']),
422
- ),
423
- historicalVolumeUsd24h: null,
424
- instantVolumeUsd: stablecoinUsdCapacity(route, limits?.instantSourceAmount),
425
- liquiditySource: 'providerLimit',
426
- score: scoreFor(tier),
427
- sourceDetail: 'across:suggested-fees',
428
- tier,
429
- },
430
- sampledAt,
431
- status,
432
- }
433
- }
434
-
435
- function formatAcrossFee(
436
- fee: z.output<typeof schema.AcrossQuote>['fees']['total'],
437
- ): FundingRoutes.ProviderQuoteFee {
438
- return {
439
- amount: fee.amount,
440
- amountUsd: fee.amountUsd,
441
- bps: scaledPctToBps(fee.pct),
442
- token: {
443
- address: fee.token.address,
444
- chainId: `eip155:${fee.token.chainId}`,
445
- decimals: fee.token.decimals,
446
- symbol: fee.token.symbol,
447
- },
448
- }
449
- }
450
-
451
95
  function formatRelayResult(
452
96
  route: FundingRoutes.RouteCandidate,
453
97
  quote: unknown,
@@ -519,31 +163,6 @@ function unavailableResult(options: {
519
163
  }
520
164
  }
521
165
 
522
- function acrossLimits(input: unknown): FundingRoutes.QuoteProbeLimits | null {
523
- const limits = record(input)
524
- const minSourceAmount = decimalString(limits['minDeposit'])
525
- const maxSourceAmount = decimalString(limits['maxDeposit'])
526
- const instantSourceAmount =
527
- decimalString(limits['maxDepositInstant']) ?? decimalString(limits['recommendedDepositInstant'])
528
- return minSourceAmount || maxSourceAmount || instantSourceAmount
529
- ? { instantSourceAmount, maxSourceAmount, minSourceAmount }
530
- : null
531
- }
532
-
533
- function statusFromLimits(
534
- amount: string,
535
- limits: FundingRoutes.QuoteProbeLimits | null,
536
- ): { status: FundingRoutes.RouteStatus; tier: FundingRoutes.QualityTier } {
537
- if (!limits) return { status: 'available', tier: 'liquid' }
538
- if (limits.minSourceAmount && compareIntegerStrings(amount, limits.minSourceAmount) < 0)
539
- return { status: 'unavailable', tier: 'unavailable' }
540
- if (limits.maxSourceAmount && compareIntegerStrings(amount, limits.maxSourceAmount) > 0)
541
- return { status: 'unavailable', tier: 'unavailable' }
542
- if (limits.instantSourceAmount && compareIntegerStrings(amount, limits.instantSourceAmount) > 0)
543
- return { status: 'thin', tier: 'thin' }
544
- return { status: 'available', tier: 'liquid' }
545
- }
546
-
547
166
  function relayLiquidity(input: unknown, route: FundingRoutes.RouteCandidate) {
548
167
  const response = record(input)
549
168
  const rows = Array.isArray(response['liquidity']) ? response['liquidity'] : []
@@ -612,14 +231,6 @@ class ProviderResponseError extends Error {
612
231
  }
613
232
  }
614
233
 
615
- class ProviderPayloadError extends Error {
616
- override name = 'FundingRouteProviders.ProviderPayloadError'
617
- }
618
-
619
- class ProviderConfigurationError extends Error {
620
- override name = 'FundingRouteProviders.ProviderConfigurationError'
621
- }
622
-
623
234
  function providerChainId(
624
235
  routeChain: FundingRoutes.RouteCandidate['sourceChain'],
625
236
  provider: string,
@@ -641,21 +252,6 @@ class UnsupportedProviderRouteError extends Error {
641
252
  override name = 'FundingRouteProviders.UnsupportedProviderRouteError'
642
253
  }
643
254
 
644
- function assertAcrossIntegratorId(integratorId: string) {
645
- if (!/^0x[\da-fA-F]{4}$/.test(integratorId)) throw new ProviderConfigurationError()
646
- }
647
-
648
- function matchesFeeToken(
649
- token: z.output<typeof schema.AcrossQuote>['fees']['total']['token'],
650
- route: FundingRoutes.RouteCandidate,
651
- ) {
652
- return (
653
- token.address.toLowerCase() === route.sourceToken.address.toLowerCase() &&
654
- token.chainId === Number(eip155ChainId(route.sourceChain)) &&
655
- token.decimals === route.sourceToken.decimals
656
- )
657
- }
658
-
659
255
  function normalizeBaseUrl(input: string) {
660
256
  return input.replace(/\/+$/, '')
661
257
  }
@@ -691,10 +287,6 @@ function integerString(input: unknown) {
691
287
  return undefined
692
288
  }
693
289
 
694
- function nestedString(input: unknown, key: string) {
695
- return decimalString(record(input)[key])
696
- }
697
-
698
290
  function nonnegativeInteger(input: unknown) {
699
291
  if (typeof input === 'number' && Number.isSafeInteger(input) && input >= 0) return input
700
292
  if (typeof input === 'string' && /^\d+$/.test(input)) return Number(input)
@@ -712,30 +304,6 @@ function compareIntegerStrings(left: string, right: string) {
712
304
  return BigInt(left) === BigInt(right) ? 0 : BigInt(left) > BigInt(right) ? 1 : -1
713
305
  }
714
306
 
715
- function isoFromUnixSeconds(input: string) {
716
- return new Date(Number(input) * 1_000).toISOString()
717
- }
718
-
719
- function stablecoinUsdCapacity(
720
- route: FundingRoutes.RouteCandidate,
721
- baseUnits: string | null | undefined,
722
- ) {
723
- if (!baseUnits || !isDollarStablecoin(route.sourceToken.symbol)) return null
724
- return decimalFromBaseUnits(baseUnits, route.sourceToken.decimals)
725
- }
726
-
727
- function isDollarStablecoin(symbol: string) {
728
- return ['DAI', 'FRXUSD', 'PYUSD', 'USDC', 'USDC.E', 'USDT', 'USDT0'].includes(
729
- symbol.toUpperCase(),
730
- )
731
- }
732
-
733
- function scaledPctToBps(input: string | undefined) {
734
- if (!input || !/^\d+$/.test(input)) return null
735
- const denominator = 1_000_000_000_000_000_000n
736
- return Number((BigInt(input) * 10_000n + denominator / 2n) / denominator)
737
- }
738
-
739
307
  function scoreFor(tier: FundingRoutes.QualityTier) {
740
308
  if (tier === 'liquid') return 90
741
309
  if (tier === 'thin') return 65
@@ -1,8 +1,6 @@
1
1
  import { Schema } from 'tapimo'
2
2
 
3
3
  import * as TestApp from '../../../../test/App.js'
4
- import type * as Log from '../../../internal/Log.js'
5
- import * as FundingRouteProviders from '../FundingRouteProviders.js'
6
4
  import * as Funding from './funding.js'
7
5
 
8
6
  describe('GET /funding/routes', () => {
@@ -21,16 +19,11 @@ describe('GET /funding/routes', () => {
21
19
  expect(response.status).toBe(200)
22
20
  const body = await TestApp.json(response, Funding.schema.listFundingRoutes.Response)
23
21
 
24
- expect(body.data.map((route) => route.provider.id)).toEqual([
25
- 'across',
26
- 'relay',
27
- 'bridge',
28
- 'bungee',
29
- ])
22
+ expect(body.data.map((route) => route.provider.id)).toEqual(['relay', 'bridge', 'bungee'])
30
23
  expect(body.data[0]).toMatchObject({
31
24
  destinationChain: { id: 'eip155:4217', name: 'Tempo' },
32
25
  destinationToken: { symbol: 'USDC.e' },
33
- id: 'base-usdc-tempo-usdce-across',
26
+ id: 'base-usdc-tempo-usdce-relay',
34
27
  quality: { liquiditySource: 'staticInventory', tier: 'unknown' },
35
28
  sourceAmount: { amount: '1000000', formatted: '1' },
36
29
  sourceChain: { id: 'eip155:8453', name: 'Base' },
@@ -43,12 +36,12 @@ describe('GET /funding/routes', () => {
43
36
  const app = TestApp.create({
44
37
  fundingRoutes: {
45
38
  adapters: {
46
- across: {
39
+ relay: {
47
40
  async probeQuote(input, signal) {
48
41
  expect(signal.aborted).toBe(false)
49
42
  expect(input.cache.bypass).toBe(true)
50
43
  expect(input.include).toEqual(['quotes'])
51
- expect(input.route.id).toBe('base-usdc-tempo-usdce-across')
44
+ expect(input.route.id).toBe('base-usdc-tempo-usdce-relay')
52
45
  expect(input.sourceAmount.amount).toBe('1000000')
53
46
  expect(input.sourceChain.id).toBe('eip155:8453')
54
47
  expect(input.sourceToken.symbol).toBe('USDC')
@@ -83,7 +76,7 @@ describe('GET /funding/routes', () => {
83
76
  })
84
77
 
85
78
  const response = await app.request(
86
- '/v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000&provider=across&include=quotes',
79
+ '/v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000&provider=relay&include=quotes',
87
80
  { headers: { ...TestApp.auth.headers, 'Cache-Control': 'no-cache' } },
88
81
  )
89
82
  expect(response.status).toBe(200)
@@ -92,7 +85,7 @@ describe('GET /funding/routes', () => {
92
85
  expect(body.data).toHaveLength(1)
93
86
  expect(body.data[0]).toMatchObject({
94
87
  errorCode: null,
95
- id: 'base-usdc-tempo-usdce-across',
88
+ id: 'base-usdc-tempo-usdce-relay',
96
89
  quality: {
97
90
  availableVolumeUsd: '1000.00',
98
91
  liquiditySource: 'providerQuote',
@@ -235,84 +228,6 @@ describe('GET /funding/routes', () => {
235
228
  })
236
229
  })
237
230
 
238
- describe('POST /funding/routes/quote', () => {
239
- test('rejects invalid depositor addresses', async () => {
240
- const response = await TestApp.create().request('/v1/funding/routes/quote', {
241
- body: JSON.stringify({
242
- depositor: 'invalid',
243
- provider: 'across',
244
- sourceAmount: '1000000',
245
- sourceChain: 'base',
246
- sourceToken: 'USDC',
247
- }),
248
- headers: { ...TestApp.auth.headers, 'content-type': 'application/json' },
249
- method: 'POST',
250
- })
251
-
252
- expect(response.status).toBe(400)
253
- expect(response.headers.get('cache-control')).toBe('no-store')
254
- expect(await response.json()).toMatchObject({ error: { code: 'body_invalid' } })
255
- })
256
-
257
- test('returns a stable error when Across quoting is not configured', async () => {
258
- const response = await TestApp.create().request('/v1/funding/routes/quote', {
259
- body: JSON.stringify(quoteRequest()),
260
- headers: { ...TestApp.auth.headers, 'content-type': 'application/json' },
261
- method: 'POST',
262
- })
263
-
264
- expect(response.status).toBe(501)
265
- expect(response.headers.get('cache-control')).toBe('no-store')
266
- expect(await response.json()).toMatchObject({ error: { code: 'provider_unconfigured' } })
267
- })
268
-
269
- test('records bounded Across failure details in the request log', async () => {
270
- const entries: Log.Entry[] = []
271
- const response = await TestApp.create({
272
- fundingRoutes: {
273
- async acrossQuote() {
274
- throw FundingRouteProviders.mapAcrossQuoteError({
275
- body: { code: 'AUTH_FAILED', detail: 'not logged' },
276
- status: 401,
277
- })
278
- },
279
- },
280
- logger: (entry) => void entries.push(entry),
281
- }).request('/v1/funding/routes/quote', {
282
- body: JSON.stringify(quoteRequest()),
283
- headers: { ...TestApp.auth.headers, 'content-type': 'application/json' },
284
- method: 'POST',
285
- })
286
-
287
- expect(response.status).toBe(502)
288
- expect(entries[0]).toMatchObject({
289
- errorCode: 'provider_error',
290
- provider: {
291
- code: 'AUTH_FAILED',
292
- failure: 'http',
293
- id: 'across',
294
- operation: 'swap/approval',
295
- status: 401,
296
- },
297
- })
298
- expect(JSON.stringify(entries[0])).not.toContain('not logged')
299
- })
300
-
301
- test('publishes the normalized quote contract in OpenAPI', async () => {
302
- const response = await TestApp.create().request('/openapi.json')
303
- const document = (await response.json()) as {
304
- paths: Record<string, { post?: { operationId?: string; responses?: object } }>
305
- }
306
- const operation = document.paths['/v1/funding/routes/quote']?.post
307
-
308
- expect(operation).toMatchObject({
309
- operationId: 'quoteFundingRoute',
310
- responses: { 200: expect.any(Object), 400: expect.any(Object), 502: expect.any(Object) },
311
- })
312
- expect(JSON.stringify(operation)).not.toMatch(/approvalTxns|checks|swapTx/)
313
- })
314
- })
315
-
316
231
  describe('GET /funding/chains', () => {
317
232
  test('lists source chain and stablecoin inventory', async () => {
318
233
  const response = await TestApp.client().v1.funding.chains.$get(undefined, TestApp.auth)
@@ -332,16 +247,6 @@ describe('GET /funding/chains', () => {
332
247
  })
333
248
  })
334
249
 
335
- function quoteRequest() {
336
- return {
337
- depositor: '0x1111111111111111111111111111111111111111',
338
- provider: 'across',
339
- sourceAmount: '1000000',
340
- sourceChain: 'base',
341
- sourceToken: 'USDC',
342
- }
343
- }
344
-
345
250
  describe('GET /providers', () => {
346
251
  test('lists providers filtered by capability', async () => {
347
252
  const response = await TestApp.client().v1.providers.$get(
@@ -351,13 +256,7 @@ describe('GET /providers', () => {
351
256
  expect(response.status).toBe(200)
352
257
  const body = await TestApp.json(response, Funding.schema.listProviders.Response)
353
258
 
354
- expect(body.data.map((provider) => provider.id)).toEqual([
355
- 'across',
356
- 'relay',
357
- 'bridge',
358
- 'bungee',
359
- 'lifi',
360
- ])
259
+ expect(body.data.map((provider) => provider.id)).toEqual(['relay', 'bridge', 'bungee', 'lifi'])
361
260
  expect(body.data.every((provider) => provider.capabilities.includes('fundingRoutes'))).toBe(
362
261
  true,
363
262
  )
@@ -365,7 +264,6 @@ describe('GET /providers', () => {
365
264
  'inventoryOnly',
366
265
  'inventoryOnly',
367
266
  'inventoryOnly',
368
- 'inventoryOnly',
369
267
  'planned',
370
268
  ])
371
269
  })