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.
- package/dist/App.d.ts +2491 -2861
- package/dist/App.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +57 -57
- package/dist/apps/data/App.d.ts +2354 -2724
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/FundingRouteProviders.d.ts +3 -110
- package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -1
- package/dist/apps/data/FundingRouteProviders.js +1 -302
- package/dist/apps/data/FundingRouteProviders.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +455 -455
- package/dist/apps/data/routes/fee-amm.d.ts +92 -92
- package/dist/apps/data/routes/funding.d.ts +138 -730
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +7 -241
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +12 -12
- package/dist/apps/data/routes/transactions.d.ts +281 -281
- package/dist/apps/data/routes/transfers.d.ts +58 -58
- package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
- package/dist/apps/management/App.d.ts +855 -855
- package/dist/apps/management/routes/api-keys.d.ts +40 -40
- package/dist/apps/management/routes/billing.d.ts +207 -207
- package/dist/apps/management/routes/me.d.ts +6 -6
- package/dist/apps/management/routes/members.d.ts +30 -30
- package/dist/apps/management/routes/orgs.d.ts +92 -92
- package/dist/apps/management/routes/projects.d.ts +28 -28
- package/dist/apps/management/routes/usage.d.ts +24 -24
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +3 -1
- package/dist/apps/mcp.js.map +1 -1
- package/dist/cloudflare.d.ts +2 -0
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +4 -0
- package/dist/cloudflare.js.map +1 -1
- package/dist/internal/Auth.d.ts +33 -22
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +40 -11
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Errors.d.ts +6 -0
- package/dist/internal/Errors.d.ts.map +1 -0
- package/dist/internal/Errors.js +16 -0
- package/dist/internal/Errors.js.map +1 -0
- package/dist/internal/FundingRoutes.d.ts +15 -213
- package/dist/internal/FundingRoutes.d.ts.map +1 -1
- package/dist/internal/FundingRoutes.js +1 -72
- package/dist/internal/FundingRoutes.js.map +1 -1
- package/dist/internal/Store.d.ts +61 -16
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +72 -17
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Viem.d.ts +1 -1
- package/dist/internal/index.d.ts +27 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +27 -0
- package/dist/internal/index.js.map +1 -0
- package/package.json +8 -3
- package/src/App.test.ts +60 -4
- package/src/Client.test-d.ts +0 -47
- package/src/apps/data/FundingRouteProviders.test.ts +37 -297
- package/src/apps/data/FundingRouteProviders.ts +3 -435
- package/src/apps/data/routes/funding.test.ts +7 -109
- package/src/apps/data/routes/funding.ts +8 -323
- package/src/apps/data/routes/indexer.test.ts +2 -2
- package/src/apps/data/routes/webhooks.test.ts +2 -2
- package/src/apps/mcp.test.ts +55 -14
- package/src/apps/mcp.ts +3 -1
- package/src/cloudflare.ts +5 -0
- package/src/internal/Auth.test.ts +397 -16
- package/src/internal/Auth.ts +76 -32
- package/src/internal/FundingRoutes.test.ts +8 -34
- package/src/internal/FundingRoutes.ts +1 -174
- package/src/internal/Log.test.ts +4 -4
- package/src/internal/Store.test-d.ts +15 -0
- package/src/internal/Store.test.ts +153 -2
- package/src/internal/Store.ts +157 -41
- package/src/internal/index.test.ts +34 -0
- package/src/internal/index.ts +26 -0
|
@@ -8,7 +8,6 @@ import * as FundingRoutes from '../../../internal/FundingRoutes.js'
|
|
|
8
8
|
import * as OpenApi from '../../../internal/OpenApi.js'
|
|
9
9
|
import * as Response from '../../../internal/Response.js'
|
|
10
10
|
import * as Schema from '../../../internal/Schema.js'
|
|
11
|
-
import * as FundingRouteProviders from '../FundingRouteProviders.js'
|
|
12
11
|
|
|
13
12
|
/** Zod schemas owned by the funding routes resource. */
|
|
14
13
|
export namespace schema {
|
|
@@ -42,7 +41,7 @@ export namespace schema {
|
|
|
42
41
|
.optional(z.record(z.string(), z.string()))
|
|
43
42
|
.check(
|
|
44
43
|
z.describe('Provider-specific chain ids Tempo uses when querying route providers.'),
|
|
45
|
-
z.meta({ examples: [{
|
|
44
|
+
z.meta({ examples: [{ bridge: 'base', relay: '8453' }] }),
|
|
46
45
|
),
|
|
47
46
|
}),
|
|
48
47
|
'A normalized chain reference for funding routes.',
|
|
@@ -123,7 +122,7 @@ export namespace schema {
|
|
|
123
122
|
/** Provider summary embedded in a route candidate. */
|
|
124
123
|
export const ProviderRef = Schema.describe(
|
|
125
124
|
z.object({
|
|
126
|
-
id: z.string().check(z.describe('Stable provider id.'), z.meta({ examples: ['
|
|
125
|
+
id: z.string().check(z.describe('Stable provider id.'), z.meta({ examples: ['relay'] })),
|
|
127
126
|
kind: z
|
|
128
127
|
.string()
|
|
129
128
|
.check(
|
|
@@ -132,7 +131,7 @@ export namespace schema {
|
|
|
132
131
|
),
|
|
133
132
|
name: z
|
|
134
133
|
.string()
|
|
135
|
-
.check(z.describe('Human-readable provider name.'), z.meta({ examples: ['
|
|
134
|
+
.check(z.describe('Human-readable provider name.'), z.meta({ examples: ['Relay'] })),
|
|
136
135
|
requiresApiKey: z
|
|
137
136
|
.boolean()
|
|
138
137
|
.check(
|
|
@@ -269,111 +268,6 @@ export namespace schema {
|
|
|
269
268
|
'Provider-derived quote, limit, and freshness data for a route candidate.',
|
|
270
269
|
)
|
|
271
270
|
|
|
272
|
-
/** Token reference attached to a normalized quote fee. */
|
|
273
|
-
export const QuoteFeeToken = Schema.describe(
|
|
274
|
-
z.object({
|
|
275
|
-
address: z
|
|
276
|
-
.string()
|
|
277
|
-
.check(
|
|
278
|
-
z.describe('Contract address of the fee token.'),
|
|
279
|
-
z.meta({ examples: ['0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'] }),
|
|
280
|
-
),
|
|
281
|
-
chainId: z
|
|
282
|
-
.string()
|
|
283
|
-
.check(z.describe('CAIP-2 chain id of the fee token.'), z.meta({ examples: ['eip155:8453'] })), // prettier-ignore
|
|
284
|
-
decimals: z
|
|
285
|
-
.number()
|
|
286
|
-
.check(
|
|
287
|
-
z.int(),
|
|
288
|
-
z.nonnegative(),
|
|
289
|
-
z.describe('Number of decimal places used by the fee token.'),
|
|
290
|
-
z.meta({ examples: [6] }),
|
|
291
|
-
),
|
|
292
|
-
symbol: z
|
|
293
|
-
.string()
|
|
294
|
-
.check(z.describe('Ticker symbol of the fee token.'), z.meta({ examples: ['USDC'] })),
|
|
295
|
-
}),
|
|
296
|
-
'Token in which a normalized quote fee is denominated.',
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
/** Normalized fee summary for an explicit funding quote. */
|
|
300
|
-
export const QuoteFee = Schema.describe(
|
|
301
|
-
z.object({
|
|
302
|
-
amount: Schema.DecimalString.check(
|
|
303
|
-
z.describe("Fee amount in the fee token's smallest unit."),
|
|
304
|
-
z.meta({ examples: ['1200'] }),
|
|
305
|
-
),
|
|
306
|
-
amountUsd: z
|
|
307
|
-
.nullable(z.string().check(z.regex(/^\d+(\.\d+)?$/)))
|
|
308
|
-
.check(
|
|
309
|
-
z.describe('Provider-estimated USD value of the fee.'),
|
|
310
|
-
z.meta({ examples: ['0.0012'] }),
|
|
311
|
-
),
|
|
312
|
-
bps: z
|
|
313
|
-
.nullable(z.number().check(z.int(), z.nonnegative()))
|
|
314
|
-
.check(z.describe('Fee as basis points of the quoted input.'), z.meta({ examples: [12] })),
|
|
315
|
-
token: QuoteFeeToken,
|
|
316
|
-
}),
|
|
317
|
-
'Provider-neutral fee amount and denomination.',
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
/** Explicit depositor-specific quote for one curated funding route. */
|
|
321
|
-
export const FundingQuote = Schema.describe(
|
|
322
|
-
z.object({
|
|
323
|
-
destinationChain: Chain.check(z.describe('Destination chain for the quoted route.')),
|
|
324
|
-
destinationToken: Token.check(z.describe('Token received on the destination chain.')),
|
|
325
|
-
estimatedSeconds: z
|
|
326
|
-
.nullable(z.number().check(z.int(), z.nonnegative()))
|
|
327
|
-
.check(z.describe('Estimated settlement time in seconds.'), z.meta({ examples: [2] })),
|
|
328
|
-
expiresAt: z.iso
|
|
329
|
-
.datetime()
|
|
330
|
-
.check(
|
|
331
|
-
z.describe('Timestamp after which the quote must not be used.'),
|
|
332
|
-
z.meta({ examples: ['2026-01-01T00:01:00Z'] }),
|
|
333
|
-
),
|
|
334
|
-
fees: z
|
|
335
|
-
.object({
|
|
336
|
-
total: QuoteFee.check(z.describe('Current estimated total fee.')),
|
|
337
|
-
totalMax: QuoteFee.check(z.describe('Maximum total fee allowed by the quote.')),
|
|
338
|
-
})
|
|
339
|
-
.check(z.describe('Current and maximum provider fee summaries.')),
|
|
340
|
-
inputAmount: Schema.DecimalString.check(
|
|
341
|
-
z.describe('Exact source amount quoted in base units.'),
|
|
342
|
-
z.meta({ examples: ['1000000'] }),
|
|
343
|
-
),
|
|
344
|
-
minimumOutputAmount: Schema.DecimalString.check(
|
|
345
|
-
z.describe('Slippage-adjusted minimum destination amount in base units.'),
|
|
346
|
-
z.meta({ examples: ['988000'] }),
|
|
347
|
-
),
|
|
348
|
-
outputAmount: Schema.DecimalString.check(
|
|
349
|
-
z.describe('Expected destination amount in base units.'),
|
|
350
|
-
z.meta({ examples: ['990000'] }),
|
|
351
|
-
),
|
|
352
|
-
outputAmountFormatted: z
|
|
353
|
-
.string()
|
|
354
|
-
.check(
|
|
355
|
-
z.describe('Expected destination amount rendered with token decimals.'),
|
|
356
|
-
z.meta({ examples: ['0.99'] }),
|
|
357
|
-
),
|
|
358
|
-
provider: ProviderRef.check(z.describe('Provider that produced the quote.')),
|
|
359
|
-
routeId: z
|
|
360
|
-
.string()
|
|
361
|
-
.check(
|
|
362
|
-
z.describe('Stable id of the curated route that was quoted.'),
|
|
363
|
-
z.meta({ examples: ['base-usdc-tempo-usdce-across'] }),
|
|
364
|
-
),
|
|
365
|
-
sampledAt: z.iso
|
|
366
|
-
.datetime()
|
|
367
|
-
.check(
|
|
368
|
-
z.describe('Timestamp when Tempo sampled the provider quote.'),
|
|
369
|
-
z.meta({ examples: ['2026-01-01T00:00:00Z'] }),
|
|
370
|
-
),
|
|
371
|
-
sourceChain: Chain.check(z.describe('Source chain for the quoted route.')),
|
|
372
|
-
sourceToken: Token.check(z.describe('Token spent on the source chain.')),
|
|
373
|
-
}),
|
|
374
|
-
'Normalized quote information without provider execution payloads.',
|
|
375
|
-
)
|
|
376
|
-
|
|
377
271
|
/** Static or live funding route candidate. */
|
|
378
272
|
export const RouteCandidate = Schema.describe(
|
|
379
273
|
z.object({
|
|
@@ -389,7 +283,7 @@ export namespace schema {
|
|
|
389
283
|
.string()
|
|
390
284
|
.check(
|
|
391
285
|
z.describe('Stable deterministic route candidate id.'),
|
|
392
|
-
z.meta({ examples: ['base-usdc-tempo-usdce-
|
|
286
|
+
z.meta({ examples: ['base-usdc-tempo-usdce-relay'] }),
|
|
393
287
|
),
|
|
394
288
|
provider: ProviderRef,
|
|
395
289
|
quality: Quality,
|
|
@@ -475,9 +369,9 @@ export namespace schema {
|
|
|
475
369
|
.optional(z.string())
|
|
476
370
|
.check(
|
|
477
371
|
z.describe(
|
|
478
|
-
'Only return candidates from this provider id, such as `
|
|
372
|
+
'Only return candidates from this provider id, such as `relay`, `bridge`, or `bungee`. Planned providers can return an empty list.',
|
|
479
373
|
),
|
|
480
|
-
z.meta({ examples: ['
|
|
374
|
+
z.meta({ examples: ['relay', 'bridge'] }),
|
|
481
375
|
),
|
|
482
376
|
sourceAmount: z
|
|
483
377
|
.string()
|
|
@@ -528,63 +422,6 @@ export namespace schema {
|
|
|
528
422
|
)
|
|
529
423
|
}
|
|
530
424
|
|
|
531
|
-
/** Schemas for the quoteFundingRoute operation. */
|
|
532
|
-
export namespace quoteFundingRoute {
|
|
533
|
-
/** JSON body for an explicit funding route quote. */
|
|
534
|
-
export const Body = z
|
|
535
|
-
.strictObject({
|
|
536
|
-
depositor: Schema.Address.check(
|
|
537
|
-
z.describe('Address that would submit the funding transaction.'),
|
|
538
|
-
),
|
|
539
|
-
destinationToken: z
|
|
540
|
-
.optional(z.string())
|
|
541
|
-
.check(
|
|
542
|
-
z.describe('Tempo destination token symbol, address, or token key.'),
|
|
543
|
-
z.meta({ examples: ['USDC.e'] }),
|
|
544
|
-
),
|
|
545
|
-
provider: z
|
|
546
|
-
.literal('across')
|
|
547
|
-
.check(
|
|
548
|
-
z.describe('Provider id selected from funding route discovery.'),
|
|
549
|
-
z.meta({ examples: ['across'] }),
|
|
550
|
-
),
|
|
551
|
-
recipient: z
|
|
552
|
-
.optional(Schema.Address)
|
|
553
|
-
.check(z.describe('Optional destination recipient; defaults to the depositor.')),
|
|
554
|
-
sourceAmount: z
|
|
555
|
-
.string()
|
|
556
|
-
.check(
|
|
557
|
-
z.minLength(1),
|
|
558
|
-
z.describe('Exact source amount to quote.'),
|
|
559
|
-
z.meta({ examples: ['1000000'] }),
|
|
560
|
-
),
|
|
561
|
-
sourceAmountUnits: z
|
|
562
|
-
._default(z.enum(['baseUnits', 'decimal']), 'baseUnits')
|
|
563
|
-
.check(
|
|
564
|
-
z.describe('Whether `sourceAmount` uses base units or decimal notation.'),
|
|
565
|
-
z.meta({ examples: ['baseUnits'] }),
|
|
566
|
-
),
|
|
567
|
-
sourceChain: z
|
|
568
|
-
.string()
|
|
569
|
-
.check(
|
|
570
|
-
z.minLength(1),
|
|
571
|
-
z.describe('Source chain alias or CAIP-2 id.'),
|
|
572
|
-
z.meta({ examples: ['base'] }),
|
|
573
|
-
),
|
|
574
|
-
sourceToken: z
|
|
575
|
-
.string()
|
|
576
|
-
.check(
|
|
577
|
-
z.minLength(1),
|
|
578
|
-
z.describe('Source token symbol, address, mint, or token key.'),
|
|
579
|
-
z.meta({ examples: ['USDC'] }),
|
|
580
|
-
),
|
|
581
|
-
})
|
|
582
|
-
.check(z.describe('Parameters for a depositor-specific exact-input funding quote.'))
|
|
583
|
-
|
|
584
|
-
/** Normalized funding quote without provider execution payloads. */
|
|
585
|
-
export const Response = FundingQuote
|
|
586
|
-
}
|
|
587
|
-
|
|
588
425
|
/** Schemas for the listFundingChains operation. */
|
|
589
426
|
export namespace listFundingChains {
|
|
590
427
|
/** Non-paginated source-chain inventory. */
|
|
@@ -626,12 +463,12 @@ export namespace schema {
|
|
|
626
463
|
const fundingRouteResponseExamples = {
|
|
627
464
|
baseUsdc: {
|
|
628
465
|
description:
|
|
629
|
-
'`GET /v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000&provider=
|
|
466
|
+
'`GET /v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000&provider=relay`',
|
|
630
467
|
summary: 'Base USDC',
|
|
631
468
|
value: {
|
|
632
469
|
data: FundingRoutes.listStaticRoutes({
|
|
633
470
|
limit: 10,
|
|
634
|
-
provider: '
|
|
471
|
+
provider: 'relay',
|
|
635
472
|
sourceAmount: '1000000',
|
|
636
473
|
sourceAmountUnits: 'baseUnits',
|
|
637
474
|
sourceChain: 'base',
|
|
@@ -681,56 +518,6 @@ const providersResponseExample = {
|
|
|
681
518
|
data: FundingRoutes.listProviders(),
|
|
682
519
|
} satisfies z.output<typeof schema.listProviders.Response>
|
|
683
520
|
|
|
684
|
-
const [fundingQuoteRouteExample] = FundingRoutes.listStaticRoutes({
|
|
685
|
-
destinationToken: 'USDC.e',
|
|
686
|
-
limit: 1,
|
|
687
|
-
provider: 'across',
|
|
688
|
-
sourceAmount: '1000000',
|
|
689
|
-
sourceAmountUnits: 'baseUnits',
|
|
690
|
-
sourceChain: 'base',
|
|
691
|
-
sourceToken: 'USDC',
|
|
692
|
-
})
|
|
693
|
-
|
|
694
|
-
const fundingQuoteResponseExample = {
|
|
695
|
-
destinationChain: fundingQuoteRouteExample!.destinationChain,
|
|
696
|
-
destinationToken: fundingQuoteRouteExample!.destinationToken,
|
|
697
|
-
estimatedSeconds: 2,
|
|
698
|
-
expiresAt: '2026-01-01T00:01:00Z',
|
|
699
|
-
fees: {
|
|
700
|
-
total: {
|
|
701
|
-
amount: '1200',
|
|
702
|
-
amountUsd: '0.0012',
|
|
703
|
-
bps: 12,
|
|
704
|
-
token: {
|
|
705
|
-
address: fundingQuoteRouteExample!.sourceToken.address,
|
|
706
|
-
chainId: fundingQuoteRouteExample!.sourceChain.id,
|
|
707
|
-
decimals: fundingQuoteRouteExample!.sourceToken.decimals,
|
|
708
|
-
symbol: fundingQuoteRouteExample!.sourceToken.symbol,
|
|
709
|
-
},
|
|
710
|
-
},
|
|
711
|
-
totalMax: {
|
|
712
|
-
amount: '1800',
|
|
713
|
-
amountUsd: '0.0018',
|
|
714
|
-
bps: 18,
|
|
715
|
-
token: {
|
|
716
|
-
address: fundingQuoteRouteExample!.sourceToken.address,
|
|
717
|
-
chainId: fundingQuoteRouteExample!.sourceChain.id,
|
|
718
|
-
decimals: fundingQuoteRouteExample!.sourceToken.decimals,
|
|
719
|
-
symbol: fundingQuoteRouteExample!.sourceToken.symbol,
|
|
720
|
-
},
|
|
721
|
-
},
|
|
722
|
-
},
|
|
723
|
-
inputAmount: '1000000',
|
|
724
|
-
minimumOutputAmount: '988000',
|
|
725
|
-
outputAmount: '990000',
|
|
726
|
-
outputAmountFormatted: '0.99',
|
|
727
|
-
provider: fundingQuoteRouteExample!.provider,
|
|
728
|
-
routeId: fundingQuoteRouteExample!.id,
|
|
729
|
-
sampledAt: '2026-01-01T00:00:00Z',
|
|
730
|
-
sourceChain: fundingQuoteRouteExample!.sourceChain,
|
|
731
|
-
sourceToken: fundingQuoteRouteExample!.sourceToken,
|
|
732
|
-
} satisfies z.output<typeof schema.quoteFundingRoute.Response>
|
|
733
|
-
|
|
734
521
|
/** Mounts funding route discovery and generic provider inventory endpoints. */
|
|
735
522
|
export function funding(options: funding.Options = {}) {
|
|
736
523
|
return new Hono<App.Environment>()
|
|
@@ -798,103 +585,6 @@ export function funding(options: funding.Options = {}) {
|
|
|
798
585
|
}
|
|
799
586
|
},
|
|
800
587
|
)
|
|
801
|
-
.post(
|
|
802
|
-
'/v1/funding/routes/quote',
|
|
803
|
-
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
804
|
-
OpenApi.validate('json', schema.quoteFundingRoute.Body, {
|
|
805
|
-
code: 'body_invalid',
|
|
806
|
-
message: 'Invalid quote parameters',
|
|
807
|
-
}),
|
|
808
|
-
OpenApi.describeRoute({
|
|
809
|
-
description:
|
|
810
|
-
'Gets a depositor-specific exact-input quote for one curated funding route. The response excludes provider transaction calldata, approvals, and wallet balance details.',
|
|
811
|
-
operationId: 'quoteFundingRoute',
|
|
812
|
-
responses: OpenApi.responses({
|
|
813
|
-
errors: {
|
|
814
|
-
400: { codes: ['body_invalid'] },
|
|
815
|
-
404: { codes: ['route_not_found', 'quote_unavailable'] },
|
|
816
|
-
501: { codes: ['provider_unconfigured'] },
|
|
817
|
-
502: { codes: ['provider_error', 'provider_rate_limited', 'provider_timeout'] },
|
|
818
|
-
},
|
|
819
|
-
success: {
|
|
820
|
-
description: 'Normalized route quote without provider execution payloads.',
|
|
821
|
-
example: fundingQuoteResponseExample,
|
|
822
|
-
schema: schema.quoteFundingRoute.Response,
|
|
823
|
-
},
|
|
824
|
-
}),
|
|
825
|
-
summary: 'Quote funding route',
|
|
826
|
-
tags: ['Funding'],
|
|
827
|
-
}),
|
|
828
|
-
async (c) => {
|
|
829
|
-
if (Auth.narrowAccess) return Auth.paidAccessError(c)
|
|
830
|
-
if (OpenApi.narrowValidation)
|
|
831
|
-
return OpenApi.validationError(c, {
|
|
832
|
-
code: 'body_invalid',
|
|
833
|
-
message: 'Invalid quote parameters',
|
|
834
|
-
})
|
|
835
|
-
|
|
836
|
-
const body = c.req.valid('json')
|
|
837
|
-
try {
|
|
838
|
-
const quote = await FundingRoutes.quoteRoute({
|
|
839
|
-
depositor: body.depositor,
|
|
840
|
-
destinationToken: body.destinationToken,
|
|
841
|
-
provider: body.provider,
|
|
842
|
-
quote: options.acrossQuote,
|
|
843
|
-
quoteTimeoutMs: options.quoteTimeoutMs,
|
|
844
|
-
recipient: body.recipient,
|
|
845
|
-
sourceAmount: body.sourceAmount,
|
|
846
|
-
sourceAmountUnits: body.sourceAmountUnits,
|
|
847
|
-
sourceChain: body.sourceChain,
|
|
848
|
-
sourceToken: body.sourceToken,
|
|
849
|
-
})
|
|
850
|
-
c.header('Cache-Control', 'no-store')
|
|
851
|
-
return c.json(Response.validated(schema.quoteFundingRoute.Response, quote), 200)
|
|
852
|
-
} catch (cause) {
|
|
853
|
-
const message = requestErrorMessage(cause)
|
|
854
|
-
if (message) return Response.error(c, { code: 'body_invalid', message, status: 400 })
|
|
855
|
-
if (cause instanceof FundingRoutes.FundingRouteNotFoundError)
|
|
856
|
-
return Response.error(c, {
|
|
857
|
-
code: 'route_not_found',
|
|
858
|
-
message: 'No curated funding route matches the request.',
|
|
859
|
-
status: 404,
|
|
860
|
-
})
|
|
861
|
-
if (cause instanceof FundingRoutes.ProviderQuoteUnavailableError)
|
|
862
|
-
return Response.error(c, {
|
|
863
|
-
code: 'quote_unavailable',
|
|
864
|
-
message: 'The provider cannot quote this route.',
|
|
865
|
-
status: 404,
|
|
866
|
-
})
|
|
867
|
-
if (cause instanceof FundingRoutes.ProviderNotConfiguredError)
|
|
868
|
-
return Response.error(c, {
|
|
869
|
-
code: 'provider_unconfigured',
|
|
870
|
-
message: 'The provider quote integration is not configured.',
|
|
871
|
-
status: 501,
|
|
872
|
-
})
|
|
873
|
-
if (cause instanceof FundingRoutes.ProviderRateLimitError)
|
|
874
|
-
return Response.error(c, {
|
|
875
|
-
code: 'provider_rate_limited',
|
|
876
|
-
message: 'The provider temporarily rate-limited this quote.',
|
|
877
|
-
status: 502,
|
|
878
|
-
})
|
|
879
|
-
if (cause instanceof FundingRoutes.ProviderAdapterTimeoutError)
|
|
880
|
-
return Response.error(c, {
|
|
881
|
-
code: 'provider_timeout',
|
|
882
|
-
message: 'The provider quote timed out.',
|
|
883
|
-
status: 502,
|
|
884
|
-
})
|
|
885
|
-
if (cause instanceof Error) {
|
|
886
|
-
const providerFailure = FundingRouteProviders.acrossQuoteFailure({ cause })
|
|
887
|
-
if (providerFailure) c.set('providerFailure', providerFailure)
|
|
888
|
-
return Response.error(c, {
|
|
889
|
-
code: 'provider_error',
|
|
890
|
-
message: 'The provider could not complete the quote.',
|
|
891
|
-
status: 502,
|
|
892
|
-
})
|
|
893
|
-
}
|
|
894
|
-
throw cause
|
|
895
|
-
}
|
|
896
|
-
},
|
|
897
|
-
)
|
|
898
588
|
.get(
|
|
899
589
|
'/v1/funding/chains',
|
|
900
590
|
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
@@ -968,7 +658,6 @@ export function funding(options: funding.Options = {}) {
|
|
|
968
658
|
data: FundingRoutes.listProviders({
|
|
969
659
|
adapters: options.adapters,
|
|
970
660
|
capability: query.capability,
|
|
971
|
-
configuredProviders: options.acrossQuote ? ['across'] : [],
|
|
972
661
|
}),
|
|
973
662
|
}),
|
|
974
663
|
200,
|
|
@@ -980,14 +669,10 @@ export function funding(options: funding.Options = {}) {
|
|
|
980
669
|
export declare namespace funding {
|
|
981
670
|
/** Options for funding route discovery. */
|
|
982
671
|
type Options = {
|
|
983
|
-
/** Across explicit quote capability. */
|
|
984
|
-
acrossQuote?: FundingRoutes.ProviderQuote | undefined
|
|
985
672
|
/** Provider adapters used to enrich static inventory with live data. */
|
|
986
673
|
adapters?: FundingRoutes.ProviderAdapters | undefined
|
|
987
674
|
/** Per-provider quote timeout in milliseconds. */
|
|
988
675
|
adapterTimeoutMs?: number | undefined
|
|
989
|
-
/** Timeout for depositor-specific provider quotes. */
|
|
990
|
-
quoteTimeoutMs?: number | undefined
|
|
991
676
|
}
|
|
992
677
|
}
|
|
993
678
|
|
|
@@ -131,14 +131,14 @@ describe('GET /indexer/query', () => {
|
|
|
131
131
|
const app = TestApp.create({
|
|
132
132
|
rateLimit: { store: rateLimitStore },
|
|
133
133
|
mpp: {
|
|
134
|
-
|
|
134
|
+
secretKey: 'secret_test_key_0123456789abcdef',
|
|
135
|
+
session: {
|
|
135
136
|
chainId: Tempo.chain.id,
|
|
136
137
|
currency: Tempo.currency,
|
|
137
138
|
decimals: 6,
|
|
138
139
|
getClient: () => Tempo.client,
|
|
139
140
|
recipient: Tempo.accounts[2].address,
|
|
140
141
|
},
|
|
141
|
-
secretKey: 'secret_test_key_0123456789abcdef',
|
|
142
142
|
},
|
|
143
143
|
})
|
|
144
144
|
await exhaustQuota(RateLimit.memory({ store: rateLimitStore }), 'public:anonymous', {
|
|
@@ -1100,14 +1100,14 @@ describe('MPP access (disabled for now — API-key-only)', () => {
|
|
|
1100
1100
|
const app = TestApp.create({
|
|
1101
1101
|
auth: { keys: [] },
|
|
1102
1102
|
mpp: {
|
|
1103
|
-
|
|
1103
|
+
secretKey: 'secret_test_key_0123456789abcdef',
|
|
1104
|
+
session: {
|
|
1104
1105
|
chainId: Tempo.chain.id,
|
|
1105
1106
|
currency: Tempo.currency,
|
|
1106
1107
|
decimals: 6,
|
|
1107
1108
|
getClient: () => Tempo.client,
|
|
1108
1109
|
recipient: Tempo.accounts[2].address,
|
|
1109
1110
|
},
|
|
1110
|
-
secretKey: 'secret_test_key_0123456789abcdef',
|
|
1111
1111
|
},
|
|
1112
1112
|
})
|
|
1113
1113
|
|
package/src/apps/mcp.test.ts
CHANGED
|
@@ -91,6 +91,46 @@ async function toolsCall(
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/** Searches every page of the progressive tool catalog. */
|
|
95
|
+
async function toolsSearch(host: Host) {
|
|
96
|
+
const tools: { annotations?: { readOnlyHint?: boolean }; name: string }[] = []
|
|
97
|
+
let offset = 0
|
|
98
|
+
while (true) {
|
|
99
|
+
const result = resultJson(await toolsCall(host, 'search_tools', { limit: 20, offset })) as {
|
|
100
|
+
nextOffset?: number
|
|
101
|
+
tools: typeof tools
|
|
102
|
+
}
|
|
103
|
+
tools.push(...result.tools)
|
|
104
|
+
if (result.nextOffset === undefined) return tools
|
|
105
|
+
offset = result.nextOffset
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Loads the complete schema and metadata for one catalog tool. */
|
|
110
|
+
async function toolDetails(host: Host, name: string) {
|
|
111
|
+
return resultJson(await toolsCall(host, 'get_tool_details', { name })) as {
|
|
112
|
+
annotations?: { readOnlyHint?: boolean }
|
|
113
|
+
inputSchema: { properties?: Record<string, { description?: string }> }
|
|
114
|
+
name: string
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Inspects and executes a generated tool through its matching read or write gate. */
|
|
119
|
+
async function generatedToolCall(
|
|
120
|
+
host: Host,
|
|
121
|
+
name: string,
|
|
122
|
+
args: Record<string, unknown>,
|
|
123
|
+
headers: Record<string, string> = {},
|
|
124
|
+
) {
|
|
125
|
+
const details = await toolDetails(host, name)
|
|
126
|
+
return toolsCall(
|
|
127
|
+
host,
|
|
128
|
+
details.annotations?.readOnlyHint === true ? 'call_read_tool' : 'call_write_tool',
|
|
129
|
+
{ arguments: args, name },
|
|
130
|
+
headers,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
94
134
|
/** Parses a tool result: structured content when present, else the JSON text item. */
|
|
95
135
|
function resultJson(result: Awaited<ReturnType<typeof toolsCall>>) {
|
|
96
136
|
return result.structuredContent ?? JSON.parse(result.content[0]!.text)
|
|
@@ -108,7 +148,13 @@ test('scopes tools to the data domain', async () => {
|
|
|
108
148
|
expect(Object.keys(spec.paths).some((path) => path.startsWith('/v1/orgs'))).toBe(true)
|
|
109
149
|
|
|
110
150
|
await initialize(host)
|
|
111
|
-
|
|
151
|
+
expect(await toolsList(host)).toEqual([
|
|
152
|
+
'search_tools',
|
|
153
|
+
'get_tool_details',
|
|
154
|
+
'call_read_tool',
|
|
155
|
+
'call_write_tool',
|
|
156
|
+
])
|
|
157
|
+
const tools = (await toolsSearch(host)).map((tool) => tool.name)
|
|
112
158
|
|
|
113
159
|
expect(tools).toContain('v1_tokens_get')
|
|
114
160
|
expect(tools).toContain('docs_search')
|
|
@@ -125,13 +171,8 @@ test('keeps tool input schemas compact', async () => {
|
|
|
125
171
|
const { host } = createHost()
|
|
126
172
|
await initialize(host)
|
|
127
173
|
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
name: string
|
|
131
|
-
inputSchema: { properties?: Record<string, { description?: string }> }
|
|
132
|
-
}[]
|
|
133
|
-
const tool = tools.find((entry) => entry.name === 'v1_tokens_get')
|
|
134
|
-
const properties = tool?.inputSchema.properties ?? {}
|
|
174
|
+
const tool = await toolDetails(host, 'v1_tokens_get')
|
|
175
|
+
const properties = tool.inputSchema.properties ?? {}
|
|
135
176
|
|
|
136
177
|
// Credential inputs stay off the schema; auth rides the forwarded header.
|
|
137
178
|
expect(properties['authorization']).toBeUndefined()
|
|
@@ -142,7 +183,7 @@ test('keeps tool input schemas compact', async () => {
|
|
|
142
183
|
'Keyset cursor from a previous response `nextCursor`; omit for the first page.',
|
|
143
184
|
)
|
|
144
185
|
// `compact` strips examples from generated schemas.
|
|
145
|
-
expect(JSON.stringify(tool
|
|
186
|
+
expect(JSON.stringify(tool.inputSchema)).not.toContain('"examples"')
|
|
146
187
|
})
|
|
147
188
|
|
|
148
189
|
test('executes generated tools against the composed app', async () => {
|
|
@@ -152,7 +193,7 @@ test('executes generated tools against the composed app', async () => {
|
|
|
152
193
|
const { host } = createHost({ app: TestApp.create({ db }) })
|
|
153
194
|
|
|
154
195
|
await initialize(host)
|
|
155
|
-
const result = await
|
|
196
|
+
const result = await generatedToolCall(host, 'v1_tokens_get', { verified: true })
|
|
156
197
|
|
|
157
198
|
expect(result.isError).toBeFalsy()
|
|
158
199
|
const body = resultJson(result) as { data: { verified: boolean }[]; nextCursor: unknown }
|
|
@@ -164,10 +205,10 @@ test('forwards the caller Authorization header to authenticated tools', async ()
|
|
|
164
205
|
const { host } = createHost({ app: TestApp.create({ auth: { keys: [reader] } }) })
|
|
165
206
|
await initialize(host)
|
|
166
207
|
|
|
167
|
-
const denied = await
|
|
208
|
+
const denied = await generatedToolCall(host, 'v1_webhooks_get', {})
|
|
168
209
|
expect(denied.isError).toBe(true)
|
|
169
210
|
|
|
170
|
-
const allowed = await
|
|
211
|
+
const allowed = await generatedToolCall(
|
|
171
212
|
host,
|
|
172
213
|
'v1_webhooks_get',
|
|
173
214
|
{},
|
|
@@ -181,7 +222,7 @@ test('proxies docs tool calls to the remote MCP source', async () => {
|
|
|
181
222
|
const { host } = createHost({ docs: docsStub() })
|
|
182
223
|
await initialize(host)
|
|
183
224
|
|
|
184
|
-
const result = await
|
|
225
|
+
const result = await generatedToolCall(host, 'docs_search', { query: 'fees' })
|
|
185
226
|
|
|
186
227
|
expect(result.isError).toBeFalsy()
|
|
187
228
|
expect(resultJson(result)).toMatchObject({ hits: ['fees'] })
|
|
@@ -210,7 +251,7 @@ test('documents POST /mcp and keeps it open under auth enforcement', async () =>
|
|
|
210
251
|
// Documenting the route makes it auth-enforced; the public lane keeps it
|
|
211
252
|
// anonymously reachable, and the endpoint never surfaces itself as a tool.
|
|
212
253
|
await initialize(app)
|
|
213
|
-
const tools = await
|
|
254
|
+
const tools = (await toolsSearch(app)).map((tool) => tool.name)
|
|
214
255
|
expect(tools.length).toBeGreaterThan(0)
|
|
215
256
|
expect(tools.some((name) => name.startsWith('mcp'))).toBe(false)
|
|
216
257
|
|
package/src/apps/mcp.ts
CHANGED
|
@@ -147,7 +147,9 @@ export function mcp(options: mcp.Options) {
|
|
|
147
147
|
example: {
|
|
148
148
|
jsonrpc: '2.0',
|
|
149
149
|
id: 1,
|
|
150
|
-
result: {
|
|
150
|
+
result: {
|
|
151
|
+
tools: [{ name: 'search_tools', description: 'Search available tools' }],
|
|
152
|
+
},
|
|
151
153
|
},
|
|
152
154
|
schema: OpenApi.resolver(schema.Response),
|
|
153
155
|
},
|
package/src/cloudflare.ts
CHANGED
|
@@ -26,6 +26,11 @@ export class DurableObject extends CloudflareDurableObject {
|
|
|
26
26
|
await this.#store.delete(key)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/** Deletes a key only when its value matches `expected`. */
|
|
30
|
+
async deleteIf(key: string, expected: null | string) {
|
|
31
|
+
return this.#store.deleteIf!(key, expected)
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
/** Gets a string value. Returns `null` for missing or expired keys. */
|
|
30
35
|
async get(key: string) {
|
|
31
36
|
return this.#store.get(key)
|