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
package/src/internal/Auth.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { findTargetHandler } from 'hono/utils/handler'
|
|
|
9
9
|
import { decodeJwt } from 'jose'
|
|
10
10
|
import { Credential, type Method } from 'mppx'
|
|
11
11
|
import { Mppx, tempo } from 'mppx/hono'
|
|
12
|
+
import { Addresses } from 'viem/tempo'
|
|
12
13
|
import * as z from 'zod/mini'
|
|
13
14
|
|
|
14
15
|
import type * as App from '../App.js'
|
|
@@ -30,6 +31,7 @@ import * as Users from '../db/tables/users.js'
|
|
|
30
31
|
import * as Viem from './Viem.js'
|
|
31
32
|
|
|
32
33
|
const policySymbol = Symbol('tempo-api.auth.policy')
|
|
34
|
+
const defaultMppRateLimit = { perMinute: 100 } satisfies RateLimit.Limit
|
|
33
35
|
|
|
34
36
|
/** Auth context consumed by Tempo API handlers. */
|
|
35
37
|
export type Context = {
|
|
@@ -47,7 +49,7 @@ export type Context = {
|
|
|
47
49
|
}
|
|
48
50
|
| undefined
|
|
49
51
|
/** MPP payment configuration. */
|
|
50
|
-
mpp?:
|
|
52
|
+
mpp?: MppHandler | undefined
|
|
51
53
|
/**
|
|
52
54
|
* Configured default anonymous public quota. Used as the throttle ceiling for
|
|
53
55
|
* sandbox keys without active billing on routes that expose no public lane
|
|
@@ -89,22 +91,27 @@ export type Payment = {
|
|
|
89
91
|
type: 'mpp'
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
/** MPP
|
|
93
|
-
export
|
|
94
|
-
/**
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
/** MPP route-policy types. Distinct from the SIWE {@link Session} capability. */
|
|
95
|
+
export declare namespace Mpp {
|
|
96
|
+
/** MPP Session request options used by route access policies. */
|
|
97
|
+
type Session = Method.RequestDefaults<ReturnType<typeof tempo.session>> & {
|
|
98
|
+
/** Optional human-readable payment description. */
|
|
99
|
+
description?: string | undefined
|
|
100
|
+
/** Optional challenge expiration timestamp or date. */
|
|
101
|
+
expires?: Date | string | undefined
|
|
102
|
+
/** Optional server-defined correlation data. */
|
|
103
|
+
meta?: Record<string, string> | undefined
|
|
104
|
+
/** Optional route/resource scope bound to the challenge. */
|
|
105
|
+
scope?: string | undefined
|
|
106
|
+
}
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
/** MPP payment handler consumed by auth policies. */
|
|
105
|
-
type
|
|
106
|
-
/**
|
|
107
|
-
|
|
110
|
+
type MppHandler = {
|
|
111
|
+
/** Paid-request quota per access principal. */
|
|
112
|
+
rateLimit?: RateLimit.Limit | undefined
|
|
113
|
+
/** Creates a Hono middleware for a Tempo Session payment. */
|
|
114
|
+
session(options: Mpp.Session): MiddlewareHandler
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
/**
|
|
@@ -127,11 +134,13 @@ export type PolicyOverride = {
|
|
|
127
134
|
}
|
|
128
135
|
| boolean
|
|
129
136
|
| undefined
|
|
130
|
-
/** MPP paid lane: object to override the
|
|
137
|
+
/** MPP paid lane: object to override the Session request, or boolean to enable/disable. */
|
|
131
138
|
mpp?:
|
|
132
139
|
| {
|
|
133
|
-
/**
|
|
134
|
-
|
|
140
|
+
/** Paid-request quota override for this route (a protective per-route cap). */
|
|
141
|
+
rateLimit?: RateLimit.Limit | undefined
|
|
142
|
+
/** Tempo Session request override for this route. */
|
|
143
|
+
session?: Mpp.Session | undefined
|
|
135
144
|
}
|
|
136
145
|
| boolean
|
|
137
146
|
| undefined
|
|
@@ -465,7 +474,11 @@ export function middleware<
|
|
|
465
474
|
endpoint extends string = string,
|
|
466
475
|
environment extends Environment = Environment,
|
|
467
476
|
>(options: middleware.Options<endpoint>): MiddlewareHandler<environment> {
|
|
468
|
-
const {
|
|
477
|
+
const {
|
|
478
|
+
rateLimit: mppRateLimit = defaultMppRateLimit,
|
|
479
|
+
session: session_ = {},
|
|
480
|
+
...mpp
|
|
481
|
+
} = options.mpp === false ? {} : (options.mpp ?? {})
|
|
469
482
|
const defaults = laneDefaults(options)
|
|
470
483
|
// Default-deny: no lanes open until a route grants them via `policy`.
|
|
471
484
|
// Otherwise the `super_admin` principal bypasses lane checks entirely.
|
|
@@ -503,11 +516,23 @@ export function middleware<
|
|
|
503
516
|
...(options.mpp === false
|
|
504
517
|
? {}
|
|
505
518
|
: {
|
|
506
|
-
mpp:
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
519
|
+
mpp: {
|
|
520
|
+
rateLimit: mppRateLimit,
|
|
521
|
+
session: Mppx.create({
|
|
522
|
+
...mpp,
|
|
523
|
+
// Defaults only; global `session` options may override, including
|
|
524
|
+
// enabling same-route HEAD bootstrap.
|
|
525
|
+
methods: [
|
|
526
|
+
tempo.session({
|
|
527
|
+
amount: '0.0001',
|
|
528
|
+
bootstrap: false,
|
|
529
|
+
currency: Addresses.pathUsd,
|
|
530
|
+
unitType: 'request',
|
|
531
|
+
...session_,
|
|
532
|
+
}),
|
|
533
|
+
],
|
|
534
|
+
}).session,
|
|
535
|
+
},
|
|
511
536
|
}),
|
|
512
537
|
rateLimit: RateLimit.memory(options.rateLimit),
|
|
513
538
|
...(options.session ? { session: options.session } : {}),
|
|
@@ -600,8 +625,10 @@ export declare namespace middleware {
|
|
|
600
625
|
|
|
601
626
|
/** MPP payment options used by auth middleware. */
|
|
602
627
|
type MppOptions = Omit<NonNullable<Parameters<typeof Mppx.create>[0]>, 'methods'> & {
|
|
603
|
-
/**
|
|
604
|
-
|
|
628
|
+
/** Paid-request quota per access principal. Defaults to 100 per minute. */
|
|
629
|
+
rateLimit?: RateLimit.Limit | undefined
|
|
630
|
+
/** Global Tempo Session options passed to the MPP method instantiator. */
|
|
631
|
+
session?: NonNullable<Parameters<typeof tempo.session>[0]> | undefined
|
|
605
632
|
}
|
|
606
633
|
}
|
|
607
634
|
|
|
@@ -1043,7 +1070,7 @@ function laneDefaults(options: middleware.Options): LaneDefaults {
|
|
|
1043
1070
|
// catalog scopes via `Auth.policy`.
|
|
1044
1071
|
scopes: apiKey?.scopes ?? [Scope.wildcard],
|
|
1045
1072
|
},
|
|
1046
|
-
mpp: {
|
|
1073
|
+
mpp: { session: {} },
|
|
1047
1074
|
public: { rateLimit: public_?.rateLimit ?? { perMinute: 60 } },
|
|
1048
1075
|
session: true,
|
|
1049
1076
|
}
|
|
@@ -1072,8 +1099,8 @@ function resolvePolicy(
|
|
|
1072
1099
|
// The OpenAPI summary/description rides along as the payment-challenge
|
|
1073
1100
|
// description. It is metadata, not a policy override, so it only applies when
|
|
1074
1101
|
// the paid lane is actually enabled and has no explicit description already.
|
|
1075
|
-
if (mpp && description !== undefined && mpp.
|
|
1076
|
-
mpp = {
|
|
1102
|
+
if (mpp && description !== undefined && mpp.session.description === undefined)
|
|
1103
|
+
mpp = { ...mpp, session: { ...mpp.session, description } }
|
|
1077
1104
|
|
|
1078
1105
|
// A lane-less policy stays enforced for super admin; only an explicit
|
|
1079
1106
|
// `false` override opts a route out of auth entirely.
|
|
@@ -1109,7 +1136,10 @@ function resolveMppLane(
|
|
|
1109
1136
|
if (override === false) return undefined
|
|
1110
1137
|
if (override === true) return current ?? default_
|
|
1111
1138
|
const base = current ?? default_
|
|
1112
|
-
return {
|
|
1139
|
+
return {
|
|
1140
|
+
rateLimit: override.rateLimit ?? base.rateLimit,
|
|
1141
|
+
session: { ...base.session, ...override.session },
|
|
1142
|
+
}
|
|
1113
1143
|
}
|
|
1114
1144
|
|
|
1115
1145
|
function resolvePublicLane(
|
|
@@ -1483,8 +1513,10 @@ export declare namespace require {
|
|
|
1483
1513
|
/** MPP payment access policy. */
|
|
1484
1514
|
mpp?:
|
|
1485
1515
|
| {
|
|
1486
|
-
/**
|
|
1487
|
-
|
|
1516
|
+
/** Paid-request quota override for this route (a protective per-route cap). */
|
|
1517
|
+
rateLimit?: RateLimit.Limit | undefined
|
|
1518
|
+
/** Tempo Session request policy. */
|
|
1519
|
+
session: Mpp.Session
|
|
1488
1520
|
}
|
|
1489
1521
|
| undefined
|
|
1490
1522
|
/** Public access policy. */
|
|
@@ -1729,7 +1761,19 @@ async function requirePayment<environment extends Environment>(
|
|
|
1729
1761
|
next: () => Promise<void>,
|
|
1730
1762
|
options: PaymentAccess,
|
|
1731
1763
|
) {
|
|
1732
|
-
|
|
1764
|
+
if (hasPaymentCredential(c)) {
|
|
1765
|
+
const result = await consumeRateLimit(options.auth, {
|
|
1766
|
+
key: `mpp:${options.principal.type}:${options.principal.id}`,
|
|
1767
|
+
limit: options.policy.mpp?.rateLimit ?? options.auth.mpp?.rateLimit ?? defaultMppRateLimit,
|
|
1768
|
+
})
|
|
1769
|
+
if (result) {
|
|
1770
|
+
setRateLimitHeaders(c, result)
|
|
1771
|
+
c.header('RateLimit-Scope', 'mpp')
|
|
1772
|
+
if (!result.allowed) return rateLimitExceeded(c, result)
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
const middleware = options.auth.mpp?.session(options.policy.mpp?.session ?? {})
|
|
1733
1777
|
if (!middleware)
|
|
1734
1778
|
return jsonError(c, {
|
|
1735
1779
|
code: 'payment_required',
|
|
@@ -2,16 +2,7 @@ import * as FundingRoutes from './FundingRoutes.js'
|
|
|
2
2
|
|
|
3
3
|
const baseUsdcRequest = {
|
|
4
4
|
limit: 10,
|
|
5
|
-
provider: '
|
|
6
|
-
sourceAmount: '1000000',
|
|
7
|
-
sourceAmountUnits: 'baseUnits',
|
|
8
|
-
sourceChain: 'base',
|
|
9
|
-
sourceToken: 'USDC',
|
|
10
|
-
} as const
|
|
11
|
-
|
|
12
|
-
const baseAcrossQuoteRequest = {
|
|
13
|
-
depositor: '0x1111111111111111111111111111111111111111',
|
|
14
|
-
provider: 'across',
|
|
5
|
+
provider: 'relay',
|
|
15
6
|
sourceAmount: '1000000',
|
|
16
7
|
sourceAmountUnits: 'baseUnits',
|
|
17
8
|
sourceChain: 'base',
|
|
@@ -24,7 +15,7 @@ describe('listRoutes', () => {
|
|
|
24
15
|
const routes = await FundingRoutes.listRoutes({
|
|
25
16
|
...baseUsdcRequest,
|
|
26
17
|
adapters: {
|
|
27
|
-
|
|
18
|
+
relay: {
|
|
28
19
|
async probeQuote(input, signal) {
|
|
29
20
|
seen = input
|
|
30
21
|
expect(signal.aborted).toBe(false)
|
|
@@ -62,14 +53,14 @@ describe('listRoutes', () => {
|
|
|
62
53
|
},
|
|
63
54
|
})
|
|
64
55
|
|
|
65
|
-
expect(seen?.route.id).toBe('base-usdc-tempo-usdce-
|
|
56
|
+
expect(seen?.route.id).toBe('base-usdc-tempo-usdce-relay')
|
|
66
57
|
expect(seen?.sourceAmount.amount).toBe('1000000')
|
|
67
58
|
expect(seen?.sourceChain.id).toBe('eip155:8453')
|
|
68
59
|
expect(seen?.sourceToken.symbol).toBe('USDC')
|
|
69
60
|
expect(routes).toHaveLength(1)
|
|
70
61
|
expect(routes[0]).toMatchObject({
|
|
71
62
|
errorCode: null,
|
|
72
|
-
id: 'base-usdc-tempo-usdce-
|
|
63
|
+
id: 'base-usdc-tempo-usdce-relay',
|
|
73
64
|
quality: {
|
|
74
65
|
availableVolumeUsd: '1000.00',
|
|
75
66
|
estimatedSeconds: 4,
|
|
@@ -103,7 +94,7 @@ describe('listRoutes', () => {
|
|
|
103
94
|
const routes = await FundingRoutes.listRoutes({
|
|
104
95
|
...baseUsdcRequest,
|
|
105
96
|
adapters: {
|
|
106
|
-
|
|
97
|
+
bridge: {
|
|
107
98
|
async probeQuote() {
|
|
108
99
|
calls += 1
|
|
109
100
|
throw new Error('unexpected call')
|
|
@@ -128,7 +119,7 @@ describe('listRoutes', () => {
|
|
|
128
119
|
const routes = await FundingRoutes.listRoutes({
|
|
129
120
|
...baseUsdcRequest,
|
|
130
121
|
adapters: {
|
|
131
|
-
|
|
122
|
+
relay: {
|
|
132
123
|
async probeQuote() {
|
|
133
124
|
throw new Error('upstream unavailable')
|
|
134
125
|
},
|
|
@@ -154,7 +145,7 @@ describe('listRoutes', () => {
|
|
|
154
145
|
...baseUsdcRequest,
|
|
155
146
|
adapterTimeoutMs: 1,
|
|
156
147
|
adapters: {
|
|
157
|
-
|
|
148
|
+
relay: {
|
|
158
149
|
probeQuote(_input, signal) {
|
|
159
150
|
signal.addEventListener('abort', () => {
|
|
160
151
|
aborted = true
|
|
@@ -183,7 +174,7 @@ describe('listRoutes', () => {
|
|
|
183
174
|
const routes = await FundingRoutes.listRoutes({
|
|
184
175
|
...baseUsdcRequest,
|
|
185
176
|
adapters: {
|
|
186
|
-
|
|
177
|
+
relay: {
|
|
187
178
|
async probeQuote(input) {
|
|
188
179
|
seen = input
|
|
189
180
|
return {
|
|
@@ -209,23 +200,6 @@ describe('listRoutes', () => {
|
|
|
209
200
|
})
|
|
210
201
|
})
|
|
211
202
|
|
|
212
|
-
describe('quoteRoute', () => {
|
|
213
|
-
test('rejects requests without a configured quote capability', async () => {
|
|
214
|
-
await expect(FundingRoutes.quoteRoute(baseAcrossQuoteRequest)).rejects.toBeInstanceOf(
|
|
215
|
-
FundingRoutes.ProviderNotConfiguredError,
|
|
216
|
-
)
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
test('rejects requests outside curated inventory', async () => {
|
|
220
|
-
await expect(
|
|
221
|
-
FundingRoutes.quoteRoute({
|
|
222
|
-
...baseAcrossQuoteRequest,
|
|
223
|
-
sourceToken: 'USDT',
|
|
224
|
-
}),
|
|
225
|
-
).rejects.toBeInstanceOf(FundingRoutes.FundingRouteNotFoundError)
|
|
226
|
-
})
|
|
227
|
-
})
|
|
228
|
-
|
|
229
203
|
describe('parseInclude', () => {
|
|
230
204
|
test('parses comma-separated include values', () => {
|
|
231
205
|
expect(FundingRoutes.parseInclude('quotes,quotes')).toEqual(['quotes'])
|
|
@@ -18,7 +18,6 @@ const sourceChains = [
|
|
|
18
18
|
kind: 'evm',
|
|
19
19
|
name: 'Ethereum',
|
|
20
20
|
providerIds: {
|
|
21
|
-
across: '1',
|
|
22
21
|
bridge: 'ethereum',
|
|
23
22
|
bungee: '1',
|
|
24
23
|
lifi: '1',
|
|
@@ -75,7 +74,6 @@ const sourceChains = [
|
|
|
75
74
|
kind: 'evm',
|
|
76
75
|
name: 'Base',
|
|
77
76
|
providerIds: {
|
|
78
|
-
across: '8453',
|
|
79
77
|
bridge: 'base',
|
|
80
78
|
bungee: '8453',
|
|
81
79
|
lifi: '8453',
|
|
@@ -116,7 +114,6 @@ const sourceChains = [
|
|
|
116
114
|
kind: 'evm',
|
|
117
115
|
name: 'BNB Smart Chain',
|
|
118
116
|
providerIds: {
|
|
119
|
-
across: '56',
|
|
120
117
|
bungee: '56',
|
|
121
118
|
lifi: '56',
|
|
122
119
|
relay: '56',
|
|
@@ -241,14 +238,6 @@ const destinationTokens = [
|
|
|
241
238
|
|
|
242
239
|
/** Providers exposed by the generic provider catalog. */
|
|
243
240
|
const providers = [
|
|
244
|
-
{
|
|
245
|
-
capabilities: ['fundingRoutes'],
|
|
246
|
-
id: 'across',
|
|
247
|
-
kind: 'intentBridge',
|
|
248
|
-
name: 'Across',
|
|
249
|
-
requiresApiKey: true,
|
|
250
|
-
status: 'inventoryOnly',
|
|
251
|
-
},
|
|
252
241
|
{
|
|
253
242
|
capabilities: ['fundingRoutes'],
|
|
254
243
|
id: 'relay',
|
|
@@ -413,64 +402,6 @@ export type QuoteProbeResult = {
|
|
|
413
402
|
}
|
|
414
403
|
}
|
|
415
404
|
|
|
416
|
-
/** Token reference attached to a normalized provider fee. */
|
|
417
|
-
export type ProviderQuoteFeeToken = {
|
|
418
|
-
/** Token contract address on its chain. */
|
|
419
|
-
address: string
|
|
420
|
-
/** CAIP-2 chain id for the fee token. */
|
|
421
|
-
chainId: string
|
|
422
|
-
/** Number of decimal places used by the fee token. */
|
|
423
|
-
decimals: number
|
|
424
|
-
/** Fee token ticker symbol. */
|
|
425
|
-
symbol: string
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/** Normalized fee amount returned by a provider quote. */
|
|
429
|
-
export type ProviderQuoteFee = {
|
|
430
|
-
/** Fee amount in the fee token's base units. */
|
|
431
|
-
amount: string
|
|
432
|
-
/** Provider-estimated USD value of the fee. */
|
|
433
|
-
amountUsd: string | null
|
|
434
|
-
/** Fee as basis points of the quoted input, when available. */
|
|
435
|
-
bps: number | null
|
|
436
|
-
/** Token in which the fee amount is denominated. */
|
|
437
|
-
token: ProviderQuoteFeeToken
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/** Input passed to a provider for one explicit, depositor-specific quote. */
|
|
441
|
-
export type ProviderQuoteInput = {
|
|
442
|
-
/** Address that would submit the funding transaction. */
|
|
443
|
-
depositor: string
|
|
444
|
-
/** Optional destination recipient; providers default it to the depositor. */
|
|
445
|
-
recipient?: string | undefined
|
|
446
|
-
/** Curated route selected by the caller. */
|
|
447
|
-
route: RouteCandidate
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/** Dynamic data returned by an explicit provider quote. */
|
|
451
|
-
export type ProviderQuoteResult = {
|
|
452
|
-
/** Estimated settlement time in seconds. */
|
|
453
|
-
estimatedSeconds: number | null
|
|
454
|
-
/** Provider quote expiry timestamp. */
|
|
455
|
-
expiresAt: string
|
|
456
|
-
/** Current and maximum fee summaries. */
|
|
457
|
-
fees: {
|
|
458
|
-
/** Current estimated total fee. */
|
|
459
|
-
total: ProviderQuoteFee
|
|
460
|
-
/** Maximum total fee allowed by the quote. */
|
|
461
|
-
totalMax: ProviderQuoteFee
|
|
462
|
-
}
|
|
463
|
-
/** Slippage-adjusted minimum destination amount in base units. */
|
|
464
|
-
minimumOutputAmount: string
|
|
465
|
-
/** Expected destination amount in base units. */
|
|
466
|
-
outputAmount: string
|
|
467
|
-
/** Timestamp when Tempo sampled the quote. */
|
|
468
|
-
sampledAt: string
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/** A normalized explicit funding quote returned to callers. */
|
|
472
|
-
export type FundingQuote = ReturnType<typeof formatFundingQuote>
|
|
473
|
-
|
|
474
405
|
/** Adapter used to enrich a static route candidate with live provider information. */
|
|
475
406
|
export type ProviderAdapter = {
|
|
476
407
|
/** Probes provider-backed quote information for one static candidate. */
|
|
@@ -480,15 +411,8 @@ export type ProviderAdapter = {
|
|
|
480
411
|
/** Provider adapter registry, keyed by provider id. Missing adapters leave routes static. */
|
|
481
412
|
export type ProviderAdapters = Partial<Record<ProviderId, ProviderAdapter>>
|
|
482
413
|
|
|
483
|
-
/** Gets a depositor-specific quote without exposing provider execution payloads. */
|
|
484
|
-
export type ProviderQuote = (
|
|
485
|
-
input: ProviderQuoteInput,
|
|
486
|
-
signal: AbortSignal,
|
|
487
|
-
) => Promise<ProviderQuoteResult>
|
|
488
|
-
|
|
489
414
|
/** Static provider support rows used until live quote adapters are configured. */
|
|
490
415
|
const inventory: readonly InventoryEntry[] = [
|
|
491
|
-
{ destinationToken: 'usdce', provider: 'across', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDC'] }, // prettier-ignore
|
|
492
416
|
{ destinationToken: 'usdce', provider: 'relay', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDC', 'USDT'] }, // prettier-ignore
|
|
493
417
|
{ destinationToken: 'usdce', provider: 'bridge', sourceChains: ['ethereum', 'base', 'solana'], sourceTokens: ['USDC'] }, // prettier-ignore
|
|
494
418
|
{ destinationToken: 'usdce', provider: 'bridge', sourceChains: ['ethereum', 'solana', 'tron'], sourceTokens: ['USDT'] }, // prettier-ignore
|
|
@@ -503,7 +427,6 @@ type InventoryEntry = {
|
|
|
503
427
|
}
|
|
504
428
|
|
|
505
429
|
const defaultAdapterTimeoutMs = 750
|
|
506
|
-
const defaultQuoteTimeoutMs = 5_000
|
|
507
430
|
const includes = ['quotes'] as const
|
|
508
431
|
|
|
509
432
|
/** Lists normalized chains and source tokens that can fund Tempo. */
|
|
@@ -522,10 +445,7 @@ export function listProviders(options: listProviders.Options = {}) {
|
|
|
522
445
|
)
|
|
523
446
|
.map((provider) => ({
|
|
524
447
|
...formatProvider(provider),
|
|
525
|
-
status:
|
|
526
|
-
options.adapters?.[provider.id] || options.configuredProviders?.includes(provider.id)
|
|
527
|
-
? ('configured' as const)
|
|
528
|
-
: provider.status,
|
|
448
|
+
status: options.adapters?.[provider.id] ? ('configured' as const) : provider.status,
|
|
529
449
|
}))
|
|
530
450
|
}
|
|
531
451
|
|
|
@@ -534,8 +454,6 @@ export declare namespace listProviders {
|
|
|
534
454
|
type Options = {
|
|
535
455
|
/** Provider adapters configured for this deployment. */
|
|
536
456
|
adapters?: ProviderAdapters | undefined
|
|
537
|
-
/** Providers configured through capabilities outside discovery adapters. */
|
|
538
|
-
configuredProviders?: readonly ProviderId[] | undefined
|
|
539
457
|
/** Provider capability to require. */
|
|
540
458
|
capability?: Capability | undefined
|
|
541
459
|
}
|
|
@@ -581,55 +499,6 @@ export async function listRoutes(options: listRoutes.Options) {
|
|
|
581
499
|
)
|
|
582
500
|
}
|
|
583
501
|
|
|
584
|
-
/** Gets a depositor-specific quote for one curated funding route. */
|
|
585
|
-
export async function quoteRoute(options: quoteRoute.Options): Promise<FundingQuote> {
|
|
586
|
-
const [route] = listStaticRoutes({
|
|
587
|
-
destinationToken: options.destinationToken,
|
|
588
|
-
limit: 1,
|
|
589
|
-
provider: options.provider,
|
|
590
|
-
sourceAmount: options.sourceAmount,
|
|
591
|
-
sourceAmountUnits: options.sourceAmountUnits,
|
|
592
|
-
sourceChain: options.sourceChain,
|
|
593
|
-
sourceToken: options.sourceToken,
|
|
594
|
-
})
|
|
595
|
-
if (!route) throw new FundingRouteNotFoundError()
|
|
596
|
-
|
|
597
|
-
const quote = options.quote
|
|
598
|
-
if (!quote) throw new ProviderNotConfiguredError()
|
|
599
|
-
|
|
600
|
-
return formatFundingQuote(
|
|
601
|
-
route,
|
|
602
|
-
await runWithTimeout(
|
|
603
|
-
(signal) =>
|
|
604
|
-
quote(
|
|
605
|
-
{
|
|
606
|
-
depositor: options.depositor,
|
|
607
|
-
...(options.recipient === undefined ? {} : { recipient: options.recipient }),
|
|
608
|
-
route,
|
|
609
|
-
},
|
|
610
|
-
signal,
|
|
611
|
-
),
|
|
612
|
-
options.quoteTimeoutMs ?? defaultQuoteTimeoutMs,
|
|
613
|
-
),
|
|
614
|
-
)
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
export declare namespace quoteRoute {
|
|
618
|
-
/** Options for resolving and quoting one curated funding route. */
|
|
619
|
-
type Options = Omit<listStaticRoutes.Options, 'limit' | 'provider'> & {
|
|
620
|
-
/** Address that would submit the funding transaction. */
|
|
621
|
-
depositor: string
|
|
622
|
-
/** Configured provider quote capability. */
|
|
623
|
-
quote?: ProviderQuote | undefined
|
|
624
|
-
/** Provider selected for the quote. */
|
|
625
|
-
provider: 'across'
|
|
626
|
-
/** Maximum time allowed for the live provider request. */
|
|
627
|
-
quoteTimeoutMs?: number | undefined
|
|
628
|
-
/** Optional destination recipient; defaults to the depositor. */
|
|
629
|
-
recipient?: string | undefined
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
|
|
633
502
|
/** Lists static funding route candidates for a normalized caller request. */
|
|
634
503
|
export function listStaticRoutes(options: listStaticRoutes.Options) {
|
|
635
504
|
const sourceChain = resolveSourceChain(options.sourceChain)
|
|
@@ -881,28 +750,6 @@ function formatRouteQuote(route: RouteCandidate, quote: QuoteProbeResult) {
|
|
|
881
750
|
} as const
|
|
882
751
|
}
|
|
883
752
|
|
|
884
|
-
function formatFundingQuote(route: RouteCandidate, quote: ProviderQuoteResult) {
|
|
885
|
-
return {
|
|
886
|
-
destinationChain: route.destinationChain,
|
|
887
|
-
destinationToken: route.destinationToken,
|
|
888
|
-
estimatedSeconds: quote.estimatedSeconds,
|
|
889
|
-
expiresAt: quote.expiresAt,
|
|
890
|
-
fees: quote.fees,
|
|
891
|
-
inputAmount: route.sourceAmount.amount,
|
|
892
|
-
minimumOutputAmount: quote.minimumOutputAmount,
|
|
893
|
-
outputAmount: quote.outputAmount,
|
|
894
|
-
outputAmountFormatted: decimalFromBaseUnits(
|
|
895
|
-
quote.outputAmount,
|
|
896
|
-
route.destinationToken.decimals,
|
|
897
|
-
),
|
|
898
|
-
provider: route.provider,
|
|
899
|
-
routeId: route.id,
|
|
900
|
-
sampledAt: quote.sampledAt,
|
|
901
|
-
sourceChain: route.sourceChain,
|
|
902
|
-
sourceToken: route.sourceToken,
|
|
903
|
-
} as const
|
|
904
|
-
}
|
|
905
|
-
|
|
906
753
|
function markRouteError(route: RouteCandidate, errorCode: string) {
|
|
907
754
|
return {
|
|
908
755
|
...route,
|
|
@@ -1026,31 +873,11 @@ export class InvalidAmountError extends Error {
|
|
|
1026
873
|
override name = 'FundingRoutes.InvalidAmountError'
|
|
1027
874
|
}
|
|
1028
875
|
|
|
1029
|
-
/** Error thrown when no curated route matches an explicit quote request. */
|
|
1030
|
-
export class FundingRouteNotFoundError extends Error {
|
|
1031
|
-
override name = 'FundingRoutes.FundingRouteNotFoundError'
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
876
|
/** Error thrown internally when a provider adapter exceeds its route quote timeout. */
|
|
1035
877
|
export class ProviderAdapterTimeoutError extends Error {
|
|
1036
878
|
override name = 'FundingRoutes.ProviderAdapterTimeoutError'
|
|
1037
879
|
}
|
|
1038
880
|
|
|
1039
|
-
/** Error thrown when a requested provider quote is temporarily unavailable. */
|
|
1040
|
-
export class ProviderQuoteUnavailableError extends Error {
|
|
1041
|
-
override name = 'FundingRoutes.ProviderQuoteUnavailableError'
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
/** Error thrown when a provider rate-limits a quote request. */
|
|
1045
|
-
export class ProviderRateLimitError extends Error {
|
|
1046
|
-
override name = 'FundingRoutes.ProviderRateLimitError'
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
/** Error thrown when a route provider lacks explicit quote capability. */
|
|
1050
|
-
export class ProviderNotConfiguredError extends Error {
|
|
1051
|
-
override name = 'FundingRoutes.ProviderNotConfiguredError'
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
881
|
/** Error thrown when the caller filters to an unknown provider. */
|
|
1055
882
|
export class UnsupportedProviderError extends Error {
|
|
1056
883
|
override name = 'FundingRoutes.UnsupportedProviderError'
|
package/src/internal/Log.test.ts
CHANGED
|
@@ -145,8 +145,8 @@ describe('middleware', () => {
|
|
|
145
145
|
c.set('providerFailure', {
|
|
146
146
|
code: 'UNAUTHORIZED',
|
|
147
147
|
failure: 'http',
|
|
148
|
-
id: '
|
|
149
|
-
operation: '
|
|
148
|
+
id: 'relay',
|
|
149
|
+
operation: 'quote/v2',
|
|
150
150
|
status: 401,
|
|
151
151
|
})
|
|
152
152
|
return Response.error(c, {
|
|
@@ -167,8 +167,8 @@ describe('middleware', () => {
|
|
|
167
167
|
"provider": {
|
|
168
168
|
"code": "UNAUTHORIZED",
|
|
169
169
|
"failure": "http",
|
|
170
|
-
"id": "
|
|
171
|
-
"operation": "
|
|
170
|
+
"id": "relay",
|
|
171
|
+
"operation": "quote/v2",
|
|
172
172
|
"status": 401,
|
|
173
173
|
},
|
|
174
174
|
"route": "/provider",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Store as MppStore } from 'mppx'
|
|
2
|
+
import { Store } from 'tapimo'
|
|
3
|
+
|
|
4
|
+
declare const namespace: Store.durableObject.Namespace
|
|
5
|
+
|
|
6
|
+
type Items = {
|
|
7
|
+
channel: { spent: bigint }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('durableObject', () => {
|
|
11
|
+
test('matches typed atomic stores', () => {
|
|
12
|
+
expectTypeOf(Store.durableObject(namespace)).toExtend<MppStore.AtomicStore<Items>>()
|
|
13
|
+
expectTypeOf(Store.durableObject<Items>(namespace)).toEqualTypeOf<Store.AtomicState<Items>>()
|
|
14
|
+
})
|
|
15
|
+
})
|