tapimo 0.3.0 → 0.4.1
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/README.md +5 -5
- package/dist/App.d.ts +31 -9
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +3 -3
- package/dist/App.js.map +1 -1
- package/dist/admin/App.d.ts +116 -116
- package/dist/apps/data/App.d.ts +30 -5
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +21 -4
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingRouteProviders.d.ts +54 -0
- package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -0
- package/dist/apps/data/FundingRouteProviders.js +355 -0
- package/dist/apps/data/FundingRouteProviders.js.map +1 -0
- package/dist/apps/data/index.d.ts +1 -0
- package/dist/apps/data/index.d.ts.map +1 -1
- package/dist/apps/data/index.js +1 -0
- package/dist/apps/data/index.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +45 -45
- package/dist/apps/data/routes/coingecko.d.ts +176 -176
- package/dist/apps/data/routes/funding.d.ts +79 -1
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +68 -8
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +5 -5
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/transactions.d.ts +281 -281
- package/dist/apps/data/routes/transfers.d.ts +58 -58
- package/dist/apps/docs/App.d.ts +1 -1
- package/dist/apps/docs/App.js +1 -1
- package/dist/apps/index.d.ts +2 -2
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +2 -2
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/{platform → management}/App.d.ts +11 -11
- package/dist/apps/{platform → management}/App.d.ts.map +1 -1
- package/dist/apps/{platform → management}/App.js +7 -7
- package/dist/apps/management/App.js.map +1 -0
- package/dist/apps/management/Billing.d.ts.map +1 -0
- package/dist/apps/management/Billing.js.map +1 -0
- package/dist/apps/management/index.d.ts +3 -0
- package/dist/apps/management/index.d.ts.map +1 -0
- package/dist/apps/management/index.js +3 -0
- package/dist/apps/management/index.js.map +1 -0
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -0
- package/dist/apps/{platform → management}/routes/api-keys.js +1 -1
- package/dist/apps/management/routes/api-keys.js.map +1 -0
- package/dist/apps/{platform → management}/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js.map +1 -0
- package/dist/apps/{platform → management}/routes/invitations.d.ts +1 -1
- package/dist/apps/management/routes/invitations.d.ts.map +1 -0
- package/dist/apps/{platform → management}/routes/invitations.js +1 -1
- package/dist/apps/management/routes/invitations.js.map +1 -0
- package/dist/apps/{platform → management}/routes/me.d.ts +17 -17
- package/dist/apps/management/routes/me.d.ts.map +1 -0
- package/dist/apps/management/routes/me.js.map +1 -0
- package/dist/apps/management/routes/members.d.ts.map +1 -0
- package/dist/apps/management/routes/members.js.map +1 -0
- package/dist/apps/management/routes/orgs.d.ts.map +1 -0
- package/dist/apps/management/routes/orgs.js.map +1 -0
- package/dist/apps/{platform → management}/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js.map +1 -0
- package/dist/apps/{platform → management}/routes/scopes.d.ts +11 -11
- package/dist/apps/management/routes/scopes.d.ts.map +1 -0
- package/dist/apps/management/routes/scopes.js.map +1 -0
- package/dist/apps/management/routes/usage.d.ts.map +1 -0
- package/dist/apps/management/routes/usage.js.map +1 -0
- package/dist/apps/mcp.js +1 -1
- package/dist/apps/relay/App.js +1 -1
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/handlers/relay.d.ts +13 -10
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +46 -16
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.js +1 -1
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/EdgeCache.js +1 -1
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/FundingRoutes.d.ts +748 -16
- package/dist/internal/FundingRoutes.d.ts.map +1 -1
- package/dist/internal/FundingRoutes.js +166 -31
- package/dist/internal/FundingRoutes.js.map +1 -1
- package/package.json +5 -5
- package/src/App.ts +10 -10
- package/src/Client.test-d.ts +4 -4
- package/src/apps/composition.test.ts +26 -13
- package/src/apps/data/App.ts +27 -4
- package/src/apps/data/FundingRouteProviders.test.ts +281 -0
- package/src/apps/data/FundingRouteProviders.ts +459 -0
- package/src/apps/data/index.ts +1 -0
- package/src/apps/data/routes/funding.test.ts +102 -2
- package/src/apps/data/routes/funding.ts +122 -8
- package/src/apps/data/routes/rpc.test.ts +33 -0
- package/src/apps/docs/App.ts +1 -1
- package/src/apps/index.ts +2 -2
- package/src/apps/{platform → management}/App.ts +14 -14
- package/src/apps/{platform → management}/index.ts +1 -1
- package/src/apps/{platform → management}/routes/api-keys.ts +1 -1
- package/src/apps/{platform → management}/routes/invitations.ts +1 -1
- package/src/apps/mcp.test.ts +1 -1
- package/src/apps/mcp.ts +1 -1
- package/src/apps/relay/App.test.ts +1 -1
- package/src/apps/relay/App.ts +1 -1
- package/src/handlers/relay.test-d.ts +8 -0
- package/src/handlers/relay.test.ts +2 -2
- package/src/handlers/relay.ts +62 -22
- package/src/internal/Auth.ts +1 -1
- package/src/internal/EdgeCache.ts +1 -1
- package/src/internal/FundingRoutes.test.ts +213 -0
- package/src/internal/FundingRoutes.ts +347 -32
- package/dist/apps/platform/App.js.map +0 -1
- package/dist/apps/platform/Billing.d.ts.map +0 -1
- package/dist/apps/platform/Billing.js.map +0 -1
- package/dist/apps/platform/index.d.ts +0 -3
- package/dist/apps/platform/index.d.ts.map +0 -1
- package/dist/apps/platform/index.js +0 -3
- package/dist/apps/platform/index.js.map +0 -1
- package/dist/apps/platform/routes/api-keys.d.ts.map +0 -1
- package/dist/apps/platform/routes/api-keys.js.map +0 -1
- package/dist/apps/platform/routes/billing.js.map +0 -1
- package/dist/apps/platform/routes/invitations.d.ts.map +0 -1
- package/dist/apps/platform/routes/invitations.js.map +0 -1
- package/dist/apps/platform/routes/me.d.ts.map +0 -1
- package/dist/apps/platform/routes/me.js.map +0 -1
- package/dist/apps/platform/routes/members.d.ts.map +0 -1
- package/dist/apps/platform/routes/members.js.map +0 -1
- package/dist/apps/platform/routes/orgs.d.ts.map +0 -1
- package/dist/apps/platform/routes/orgs.js.map +0 -1
- package/dist/apps/platform/routes/projects.js.map +0 -1
- package/dist/apps/platform/routes/scopes.d.ts.map +0 -1
- package/dist/apps/platform/routes/scopes.js.map +0 -1
- package/dist/apps/platform/routes/usage.d.ts.map +0 -1
- package/dist/apps/platform/routes/usage.js.map +0 -1
- /package/dist/apps/{platform → management}/Billing.d.ts +0 -0
- /package/dist/apps/{platform → management}/Billing.js +0 -0
- /package/dist/apps/{platform → management}/routes/api-keys.d.ts +0 -0
- /package/dist/apps/{platform → management}/routes/billing.d.ts +0 -0
- /package/dist/apps/{platform → management}/routes/billing.js +0 -0
- /package/dist/apps/{platform → management}/routes/me.js +0 -0
- /package/dist/apps/{platform → management}/routes/members.d.ts +0 -0
- /package/dist/apps/{platform → management}/routes/members.js +0 -0
- /package/dist/apps/{platform → management}/routes/orgs.d.ts +0 -0
- /package/dist/apps/{platform → management}/routes/orgs.js +0 -0
- /package/dist/apps/{platform → management}/routes/projects.d.ts +0 -0
- /package/dist/apps/{platform → management}/routes/projects.js +0 -0
- /package/dist/apps/{platform → management}/routes/scopes.js +0 -0
- /package/dist/apps/{platform → management}/routes/usage.d.ts +0 -0
- /package/dist/apps/{platform → management}/routes/usage.js +0 -0
- /package/src/apps/{platform → management}/Billing.test.ts +0 -0
- /package/src/apps/{platform → management}/Billing.ts +0 -0
- /package/src/apps/{platform → management}/routes/api-keys.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/billing.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/billing.ts +0 -0
- /package/src/apps/{platform → management}/routes/invitations.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/me.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/me.ts +0 -0
- /package/src/apps/{platform → management}/routes/members.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/members.ts +0 -0
- /package/src/apps/{platform → management}/routes/orgs.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/orgs.ts +0 -0
- /package/src/apps/{platform → management}/routes/projects.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/projects.ts +0 -0
- /package/src/apps/{platform → management}/routes/scopes.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/scopes.ts +0 -0
- /package/src/apps/{platform → management}/routes/usage.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/usage.ts +0 -0
|
@@ -74,7 +74,7 @@ export function middleware<environment extends Env>(
|
|
|
74
74
|
return async (c, next) => {
|
|
75
75
|
const { store } = options
|
|
76
76
|
// Pay the store lookup only when a matched handler is marked cacheable
|
|
77
|
-
// (mounted `Cache.response`); other GETs (authed
|
|
77
|
+
// (mounted `Cache.response`); other GETs (authed management routes, docs,
|
|
78
78
|
// 404s) can never hit, so the read would be a wasted round trip.
|
|
79
79
|
const eligible =
|
|
80
80
|
store !== undefined &&
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import * as FundingRoutes from './FundingRoutes.js'
|
|
2
|
+
|
|
3
|
+
const baseUsdcRequest = {
|
|
4
|
+
limit: 10,
|
|
5
|
+
provider: 'across',
|
|
6
|
+
sourceAmount: '1000000',
|
|
7
|
+
sourceAmountUnits: 'baseUnits',
|
|
8
|
+
sourceChain: 'base',
|
|
9
|
+
sourceToken: 'USDC',
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
describe('listRoutes', () => {
|
|
13
|
+
test('enriches static candidates with adapter quote probes', async () => {
|
|
14
|
+
let seen: FundingRoutes.QuoteProbeInput | undefined
|
|
15
|
+
const routes = await FundingRoutes.listRoutes({
|
|
16
|
+
...baseUsdcRequest,
|
|
17
|
+
adapters: {
|
|
18
|
+
across: {
|
|
19
|
+
async probeQuote(input, signal) {
|
|
20
|
+
seen = input
|
|
21
|
+
expect(signal.aborted).toBe(false)
|
|
22
|
+
expect(input.cache.bypass).toBe(false)
|
|
23
|
+
expect(input.destinationChain.id).toBe('eip155:4217')
|
|
24
|
+
expect(input.destinationToken.symbol).toBe('USDC.e')
|
|
25
|
+
expect(input.include).toEqual([])
|
|
26
|
+
expect(input.request.sourceChain).toBe('base')
|
|
27
|
+
return {
|
|
28
|
+
expiresAt: '2026-01-01T00:00:30Z',
|
|
29
|
+
limits: {
|
|
30
|
+
instantSourceAmount: '1000000000',
|
|
31
|
+
maxSourceAmount: '100000000000',
|
|
32
|
+
minSourceAmount: '500000',
|
|
33
|
+
},
|
|
34
|
+
minimumOutputAmount: '980000',
|
|
35
|
+
outputAmount: '990000',
|
|
36
|
+
outputAmountFormatted: '0.99',
|
|
37
|
+
quality: {
|
|
38
|
+
availableVolumeUsd: '1000.00',
|
|
39
|
+
estimatedSeconds: 4,
|
|
40
|
+
feeBps: 12,
|
|
41
|
+
historicalVolumeUsd24h: '100000.00',
|
|
42
|
+
instantVolumeUsd: '1000.00',
|
|
43
|
+
liquiditySource: 'providerQuote',
|
|
44
|
+
score: 91,
|
|
45
|
+
sourceDetail: 'providerEndpoint',
|
|
46
|
+
tier: 'liquid',
|
|
47
|
+
},
|
|
48
|
+
sampledAt: '2026-01-01T00:00:00Z',
|
|
49
|
+
status: 'available',
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
expect(seen?.route.id).toBe('base-usdc-tempo-usdce-across')
|
|
57
|
+
expect(seen?.sourceAmount.amount).toBe('1000000')
|
|
58
|
+
expect(seen?.sourceChain.id).toBe('eip155:8453')
|
|
59
|
+
expect(seen?.sourceToken.symbol).toBe('USDC')
|
|
60
|
+
expect(routes).toHaveLength(1)
|
|
61
|
+
expect(routes[0]).toMatchObject({
|
|
62
|
+
errorCode: null,
|
|
63
|
+
id: 'base-usdc-tempo-usdce-across',
|
|
64
|
+
quality: {
|
|
65
|
+
availableVolumeUsd: '1000.00',
|
|
66
|
+
estimatedSeconds: 4,
|
|
67
|
+
feeBps: 12,
|
|
68
|
+
historicalVolumeUsd24h: '100000.00',
|
|
69
|
+
instantVolumeUsd: '1000.00',
|
|
70
|
+
liquiditySource: 'providerQuote',
|
|
71
|
+
score: 91,
|
|
72
|
+
sourceDetail: 'providerEndpoint',
|
|
73
|
+
tier: 'liquid',
|
|
74
|
+
},
|
|
75
|
+
quote: {
|
|
76
|
+
expiresAt: '2026-01-01T00:00:30Z',
|
|
77
|
+
inputAmount: '1000000',
|
|
78
|
+
limits: {
|
|
79
|
+
instantSourceAmount: '1000000000',
|
|
80
|
+
maxSourceAmount: '100000000000',
|
|
81
|
+
minSourceAmount: '500000',
|
|
82
|
+
},
|
|
83
|
+
minimumOutputAmount: '980000',
|
|
84
|
+
outputAmount: '990000',
|
|
85
|
+
outputAmountFormatted: '0.99',
|
|
86
|
+
sampledAt: '2026-01-01T00:00:00Z',
|
|
87
|
+
},
|
|
88
|
+
status: 'available',
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('leaves candidates static when their provider has no adapter', async () => {
|
|
93
|
+
let calls = 0
|
|
94
|
+
const routes = await FundingRoutes.listRoutes({
|
|
95
|
+
...baseUsdcRequest,
|
|
96
|
+
adapters: {
|
|
97
|
+
relay: {
|
|
98
|
+
async probeQuote() {
|
|
99
|
+
calls += 1
|
|
100
|
+
throw new Error('unexpected call')
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
expect(calls).toBe(0)
|
|
107
|
+
expect(routes[0]).toMatchObject({
|
|
108
|
+
quality: {
|
|
109
|
+
liquiditySource: 'staticInventory',
|
|
110
|
+
sourceDetail: null,
|
|
111
|
+
tier: 'unknown',
|
|
112
|
+
},
|
|
113
|
+
quote: null,
|
|
114
|
+
status: 'unknown',
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('marks candidates as errors when an adapter throws', async () => {
|
|
119
|
+
const routes = await FundingRoutes.listRoutes({
|
|
120
|
+
...baseUsdcRequest,
|
|
121
|
+
adapters: {
|
|
122
|
+
across: {
|
|
123
|
+
async probeQuote() {
|
|
124
|
+
throw new Error('upstream unavailable')
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
expect(routes[0]).toMatchObject({
|
|
131
|
+
errorCode: 'provider_error',
|
|
132
|
+
quality: {
|
|
133
|
+
liquiditySource: 'providerError',
|
|
134
|
+
sourceDetail: null,
|
|
135
|
+
tier: 'error',
|
|
136
|
+
},
|
|
137
|
+
quote: null,
|
|
138
|
+
status: 'error',
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('aborts slow adapters and marks candidates as timed-out errors', async () => {
|
|
143
|
+
let aborted = false
|
|
144
|
+
const routes = await FundingRoutes.listRoutes({
|
|
145
|
+
...baseUsdcRequest,
|
|
146
|
+
adapterTimeoutMs: 1,
|
|
147
|
+
adapters: {
|
|
148
|
+
across: {
|
|
149
|
+
probeQuote(_input, signal) {
|
|
150
|
+
signal.addEventListener('abort', () => {
|
|
151
|
+
aborted = true
|
|
152
|
+
})
|
|
153
|
+
return new Promise<FundingRoutes.QuoteProbeResult>(() => {})
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
expect(aborted).toBe(true)
|
|
160
|
+
expect(routes[0]).toMatchObject({
|
|
161
|
+
errorCode: 'provider_timeout',
|
|
162
|
+
quality: {
|
|
163
|
+
liquiditySource: 'providerError',
|
|
164
|
+
sourceDetail: null,
|
|
165
|
+
tier: 'error',
|
|
166
|
+
},
|
|
167
|
+
quote: null,
|
|
168
|
+
status: 'error',
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('passes include and cache policy through to adapters', async () => {
|
|
173
|
+
let seen: FundingRoutes.QuoteProbeInput | undefined
|
|
174
|
+
const routes = await FundingRoutes.listRoutes({
|
|
175
|
+
...baseUsdcRequest,
|
|
176
|
+
adapters: {
|
|
177
|
+
across: {
|
|
178
|
+
async probeQuote(input) {
|
|
179
|
+
seen = input
|
|
180
|
+
return {
|
|
181
|
+
errorCode: 'provider_unavailable',
|
|
182
|
+
sampledAt: '2026-01-01T00:00:00Z',
|
|
183
|
+
status: 'error',
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
cache: { bypass: true },
|
|
189
|
+
include: ['quotes'],
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
expect(seen?.cache.bypass).toBe(true)
|
|
193
|
+
expect(seen?.include).toEqual(['quotes'])
|
|
194
|
+
expect(seen?.request.include).toEqual(['quotes'])
|
|
195
|
+
expect(routes[0]).toMatchObject({
|
|
196
|
+
errorCode: 'provider_unavailable',
|
|
197
|
+
quote: null,
|
|
198
|
+
status: 'error',
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
describe('parseInclude', () => {
|
|
204
|
+
test('parses comma-separated include values', () => {
|
|
205
|
+
expect(FundingRoutes.parseInclude('quotes,quotes')).toEqual(['quotes'])
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test('rejects unsupported include values', () => {
|
|
209
|
+
expect(() => FundingRoutes.parseInclude('quotes,debug')).toThrow(
|
|
210
|
+
FundingRoutes.UnsupportedIncludeError,
|
|
211
|
+
)
|
|
212
|
+
})
|
|
213
|
+
})
|
|
@@ -288,6 +288,138 @@ type DestinationTokenInput = (typeof destinationTokens)[number]
|
|
|
288
288
|
type ProviderInput = (typeof providers)[number]
|
|
289
289
|
type SourceChainInput = (typeof sourceChains)[number]
|
|
290
290
|
|
|
291
|
+
/** Provider ids accepted by the funding route inventory. */
|
|
292
|
+
export type ProviderId = ProviderInput['id']
|
|
293
|
+
|
|
294
|
+
/** Provider-specific route status for the requested amount. */
|
|
295
|
+
export type RouteStatus = 'available' | 'thin' | 'unknown' | 'unavailable' | 'error'
|
|
296
|
+
|
|
297
|
+
/** Machine-readable route quality tier. */
|
|
298
|
+
export type QualityTier = 'liquid' | 'thin' | 'unknown' | 'unavailable' | 'error'
|
|
299
|
+
|
|
300
|
+
/** Response expansions accepted by funding route discovery. */
|
|
301
|
+
export type Include = 'quotes'
|
|
302
|
+
|
|
303
|
+
/** Normalized chain reference passed to provider adapters. */
|
|
304
|
+
export type ChainRef = ReturnType<typeof formatChain> | typeof destinationChain
|
|
305
|
+
|
|
306
|
+
/** Normalized token amount passed to provider adapters. */
|
|
307
|
+
export type TokenAmount = ReturnType<typeof resolveSourceAmount>
|
|
308
|
+
|
|
309
|
+
/** Normalized token reference passed to provider adapters. */
|
|
310
|
+
export type TokenRef = ReturnType<typeof formatDestinationToken> | ReturnType<typeof formatSourceToken>
|
|
311
|
+
|
|
312
|
+
/** A normalized funding route candidate returned by route discovery. */
|
|
313
|
+
export type RouteCandidate = ReturnType<typeof formatRouteCandidate>
|
|
314
|
+
|
|
315
|
+
/** Cache policy for a single funding route discovery request. */
|
|
316
|
+
export type CachePolicy = {
|
|
317
|
+
/** Whether provider adapters should bypass their own quote or inventory caches. */
|
|
318
|
+
bypass: boolean
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Normalized caller request passed to provider adapters. */
|
|
322
|
+
export type QuoteRequest = {
|
|
323
|
+
/** Optional Tempo destination token symbol, address, or token key. */
|
|
324
|
+
destinationToken?: string | undefined
|
|
325
|
+
/** Response expansions requested by the caller. */
|
|
326
|
+
include: readonly Include[]
|
|
327
|
+
/** Maximum number of route candidates requested by the caller. */
|
|
328
|
+
limit: number
|
|
329
|
+
/** Optional provider id filter. */
|
|
330
|
+
provider?: string | undefined
|
|
331
|
+
/** Source amount as supplied by the caller. */
|
|
332
|
+
sourceAmount: string
|
|
333
|
+
/** Unit used by {@link sourceAmount}. */
|
|
334
|
+
sourceAmountUnits: SourceAmountUnits
|
|
335
|
+
/** Chain alias or CAIP-2 source chain id. */
|
|
336
|
+
sourceChain: string
|
|
337
|
+
/** Source token symbol, contract address, mint, or Tempo token key. */
|
|
338
|
+
sourceToken: string
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Input passed to a provider adapter for one static route candidate probe. */
|
|
342
|
+
export type QuoteProbeInput = {
|
|
343
|
+
/** Cache policy for provider-side inventory, quote, and liquidity lookups. */
|
|
344
|
+
cache: CachePolicy
|
|
345
|
+
/** Destination chain, always Tempo mainnet in V1. */
|
|
346
|
+
destinationChain: ChainRef
|
|
347
|
+
/** Tempo token expected from this route. */
|
|
348
|
+
destinationToken: TokenRef
|
|
349
|
+
/** Response expansions requested by the caller. */
|
|
350
|
+
include: readonly Include[]
|
|
351
|
+
/** Normalized caller request that produced this route candidate. */
|
|
352
|
+
request: QuoteRequest
|
|
353
|
+
/** Static candidate being enriched with live provider information. */
|
|
354
|
+
route: RouteCandidate
|
|
355
|
+
/** Source amount in base units and display form. */
|
|
356
|
+
sourceAmount: TokenAmount
|
|
357
|
+
/** Chain where the caller currently holds funds. */
|
|
358
|
+
sourceChain: ChainRef
|
|
359
|
+
/** Token the caller currently holds. */
|
|
360
|
+
sourceToken: TokenRef
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Provider-specific source amount limits, expressed in source base units. */
|
|
364
|
+
export type QuoteProbeLimits = {
|
|
365
|
+
/** Provider-reported instant source amount capacity. */
|
|
366
|
+
instantSourceAmount?: string | null | undefined
|
|
367
|
+
/** Provider-reported maximum source amount. */
|
|
368
|
+
maxSourceAmount?: string | null | undefined
|
|
369
|
+
/** Provider-reported minimum source amount. */
|
|
370
|
+
minSourceAmount?: string | null | undefined
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** Quote probe data returned by a provider adapter. */
|
|
374
|
+
export type QuoteProbeResult = {
|
|
375
|
+
/** Stable route error code when status is `error`. */
|
|
376
|
+
errorCode?: string | null | undefined
|
|
377
|
+
/** Provider quote expiry timestamp. */
|
|
378
|
+
expiresAt?: string | null | undefined
|
|
379
|
+
/** Provider-reported source amount limits. */
|
|
380
|
+
limits?: QuoteProbeLimits | null | undefined
|
|
381
|
+
/** Slippage-adjusted minimum destination amount in base units. */
|
|
382
|
+
minimumOutputAmount?: string | null | undefined
|
|
383
|
+
/** Expected destination amount in base units. */
|
|
384
|
+
outputAmount?: string | null | undefined
|
|
385
|
+
/** Expected destination amount rendered with destination token decimals. */
|
|
386
|
+
outputAmountFormatted?: string | null | undefined
|
|
387
|
+
/** Timestamp for the live quote or route probe. */
|
|
388
|
+
sampledAt: string
|
|
389
|
+
/** Provider-specific route status for the requested amount. */
|
|
390
|
+
status: RouteStatus
|
|
391
|
+
/** Provider-backed route quality metadata. */
|
|
392
|
+
quality?: {
|
|
393
|
+
/** Available USD capacity, when available. */
|
|
394
|
+
availableVolumeUsd?: string | null | undefined
|
|
395
|
+
/** Estimated settlement time in seconds, when known. */
|
|
396
|
+
estimatedSeconds?: number | null | undefined
|
|
397
|
+
/** Estimated fee in basis points, when known. */
|
|
398
|
+
feeBps?: number | null | undefined
|
|
399
|
+
/** 24h routed volume in USD, when available. */
|
|
400
|
+
historicalVolumeUsd24h?: string | null | undefined
|
|
401
|
+
/** Instant USD capacity, when available. */
|
|
402
|
+
instantVolumeUsd?: string | null | undefined
|
|
403
|
+
/** Where the live liquidity or support signal came from. */
|
|
404
|
+
liquiditySource: string
|
|
405
|
+
/** Explainable route score from 0 to 100, when enough signals are available. */
|
|
406
|
+
score?: number | null | undefined
|
|
407
|
+
/** Provider-specific quote source or endpoint detail. */
|
|
408
|
+
sourceDetail?: string | null | undefined
|
|
409
|
+
/** Machine-readable route quality tier. */
|
|
410
|
+
tier?: QualityTier | undefined
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** Adapter used to enrich a static route candidate with live provider information. */
|
|
415
|
+
export type ProviderAdapter = {
|
|
416
|
+
/** Probes provider-backed quote information for one static candidate. */
|
|
417
|
+
probeQuote(input: QuoteProbeInput, signal: AbortSignal): Promise<QuoteProbeResult>
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Provider adapter registry, keyed by provider id. Missing adapters leave routes static. */
|
|
421
|
+
export type ProviderAdapters = Partial<Record<ProviderId, ProviderAdapter>>
|
|
422
|
+
|
|
291
423
|
/** Static provider support rows used until live quote adapters are configured. */
|
|
292
424
|
const inventory: readonly InventoryEntry[] = [
|
|
293
425
|
{ destinationToken: 'usdce', provider: 'across', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDC'] }, // prettier-ignore
|
|
@@ -304,6 +436,9 @@ type InventoryEntry = {
|
|
|
304
436
|
sourceTokens: readonly string[]
|
|
305
437
|
}
|
|
306
438
|
|
|
439
|
+
const defaultAdapterTimeoutMs = 750
|
|
440
|
+
const includes = ['quotes'] as const
|
|
441
|
+
|
|
307
442
|
/** Lists normalized chains and source tokens that can fund Tempo. */
|
|
308
443
|
export function listChains() {
|
|
309
444
|
return sourceChains.map((chain) => ({
|
|
@@ -318,19 +453,61 @@ export function listProviders(options: listProviders.Options = {}) {
|
|
|
318
453
|
.filter((provider) =>
|
|
319
454
|
options.capability === undefined ? true : provider.capabilities.includes(options.capability),
|
|
320
455
|
)
|
|
321
|
-
.map(
|
|
456
|
+
.map((provider) => ({
|
|
457
|
+
...formatProvider(provider),
|
|
458
|
+
status: options.adapters?.[provider.id] ? ('configured' as const) : provider.status,
|
|
459
|
+
}))
|
|
322
460
|
}
|
|
323
461
|
|
|
324
462
|
export declare namespace listProviders {
|
|
325
463
|
/** Options for listing providers. */
|
|
326
464
|
type Options = {
|
|
465
|
+
/** Provider adapters configured for this deployment. */
|
|
466
|
+
adapters?: ProviderAdapters | undefined
|
|
327
467
|
/** Provider capability to require. */
|
|
328
468
|
capability?: Capability | undefined
|
|
329
469
|
}
|
|
330
470
|
}
|
|
331
471
|
|
|
472
|
+
/** Parses the comma-separated `include` query parameter. */
|
|
473
|
+
export function parseInclude(input: string | undefined) {
|
|
474
|
+
if (input === undefined || input.trim() === '') return []
|
|
475
|
+
const parsed = input.split(',').map((part) => part.trim()).filter(Boolean)
|
|
476
|
+
for (const include of parsed)
|
|
477
|
+
if (!(includes as readonly string[]).includes(include)) throw new UnsupportedIncludeError()
|
|
478
|
+
return [...new Set(parsed)] as readonly Include[]
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** Lists funding route candidates, enriching static inventory when adapters are configured. */
|
|
482
|
+
export async function listRoutes(options: listRoutes.Options) {
|
|
483
|
+
const routes = listStaticRoutes(options)
|
|
484
|
+
const adapterEntries = Object.entries(options.adapters ?? {})
|
|
485
|
+
if (adapterEntries.length === 0) return routes
|
|
486
|
+
|
|
487
|
+
return Promise.all(
|
|
488
|
+
routes.map((route) =>
|
|
489
|
+
enrichRoute(route, {
|
|
490
|
+
adapters: options.adapters ?? {},
|
|
491
|
+
cache: options.cache ?? { bypass: false },
|
|
492
|
+
include: options.include ?? [],
|
|
493
|
+
request: {
|
|
494
|
+
destinationToken: options.destinationToken,
|
|
495
|
+
include: options.include ?? [],
|
|
496
|
+
limit: options.limit,
|
|
497
|
+
provider: options.provider,
|
|
498
|
+
sourceAmount: options.sourceAmount,
|
|
499
|
+
sourceAmountUnits: options.sourceAmountUnits,
|
|
500
|
+
sourceChain: options.sourceChain,
|
|
501
|
+
sourceToken: options.sourceToken,
|
|
502
|
+
},
|
|
503
|
+
timeoutMs: options.adapterTimeoutMs ?? defaultAdapterTimeoutMs,
|
|
504
|
+
}),
|
|
505
|
+
),
|
|
506
|
+
)
|
|
507
|
+
}
|
|
508
|
+
|
|
332
509
|
/** Lists static funding route candidates for a normalized caller request. */
|
|
333
|
-
export function
|
|
510
|
+
export function listStaticRoutes(options: listStaticRoutes.Options) {
|
|
334
511
|
const sourceChain = resolveSourceChain(options.sourceChain)
|
|
335
512
|
const sourceToken = resolveSourceToken(sourceChain, options.sourceToken)
|
|
336
513
|
const destinationToken = resolveDestinationToken(options.destinationToken)
|
|
@@ -353,39 +530,19 @@ export function listRoutes(options: listRoutes.Options) {
|
|
|
353
530
|
.filter((entry) => entry.destinationToken === destinationToken.slug)
|
|
354
531
|
.filter((entry) => provider === undefined || entry.provider === provider.id)
|
|
355
532
|
.slice(0, options.limit)
|
|
356
|
-
.map((entry) =>
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
destinationToken: formatDestinationToken(destinationToken),
|
|
361
|
-
id: [
|
|
362
|
-
sourceChain.slug,
|
|
363
|
-
sourceToken.symbol.toLowerCase(),
|
|
364
|
-
'tempo',
|
|
365
|
-
destinationToken.slug,
|
|
366
|
-
entryProvider.id,
|
|
367
|
-
].join('-'),
|
|
368
|
-
provider: formatProviderRef(entryProvider),
|
|
369
|
-
quality: {
|
|
370
|
-
availableVolumeUsd: null,
|
|
371
|
-
estimatedSeconds: null,
|
|
372
|
-
feeBps: null,
|
|
373
|
-
historicalVolumeUsd24h: null,
|
|
374
|
-
instantVolumeUsd: null,
|
|
375
|
-
liquiditySource: 'staticInventory',
|
|
376
|
-
score: null,
|
|
377
|
-
tier: 'unknown' as const,
|
|
378
|
-
},
|
|
533
|
+
.map((entry) =>
|
|
534
|
+
formatRouteCandidate({
|
|
535
|
+
destinationToken,
|
|
536
|
+
provider: providers.find((candidate) => candidate.id === entry.provider)!,
|
|
379
537
|
sourceAmount,
|
|
380
|
-
sourceChain
|
|
381
|
-
sourceToken
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
})
|
|
538
|
+
sourceChain,
|
|
539
|
+
sourceToken,
|
|
540
|
+
}),
|
|
541
|
+
)
|
|
385
542
|
}
|
|
386
543
|
|
|
387
|
-
export declare namespace
|
|
388
|
-
/** Options for listing funding routes. */
|
|
544
|
+
export declare namespace listStaticRoutes {
|
|
545
|
+
/** Options for listing static funding routes. */
|
|
389
546
|
type Options = {
|
|
390
547
|
/** Optional Tempo destination token symbol, address, or token key. */
|
|
391
548
|
destinationToken?: string | undefined
|
|
@@ -404,6 +561,20 @@ export declare namespace listRoutes {
|
|
|
404
561
|
}
|
|
405
562
|
}
|
|
406
563
|
|
|
564
|
+
export declare namespace listRoutes {
|
|
565
|
+
/** Options for listing funding routes. */
|
|
566
|
+
type Options = listStaticRoutes.Options & {
|
|
567
|
+
/** Provider adapters used to enrich static inventory with live data. */
|
|
568
|
+
adapters?: ProviderAdapters | undefined
|
|
569
|
+
/** Per-provider quote timeout in milliseconds. */
|
|
570
|
+
adapterTimeoutMs?: number | undefined
|
|
571
|
+
/** Cache policy for provider-side inventory, quote, and liquidity lookups. */
|
|
572
|
+
cache?: CachePolicy | undefined
|
|
573
|
+
/** Response expansions requested by the caller. */
|
|
574
|
+
include?: readonly Include[] | undefined
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
407
578
|
/** Provider capability identifiers accepted by the generic provider catalog. */
|
|
408
579
|
export type Capability = 'fundingRoutes'
|
|
409
580
|
|
|
@@ -423,6 +594,51 @@ function decimalFromBaseUnits(baseUnits: string, decimals: number) {
|
|
|
423
594
|
return fraction ? `${whole}.${fraction}` : whole
|
|
424
595
|
}
|
|
425
596
|
|
|
597
|
+
async function enrichRoute(
|
|
598
|
+
route: RouteCandidate,
|
|
599
|
+
options: {
|
|
600
|
+
adapters: ProviderAdapters
|
|
601
|
+
cache: CachePolicy
|
|
602
|
+
include: readonly Include[]
|
|
603
|
+
request: QuoteRequest
|
|
604
|
+
timeoutMs: number
|
|
605
|
+
},
|
|
606
|
+
) {
|
|
607
|
+
const adapter = options.adapters[route.provider.id as ProviderId]
|
|
608
|
+
if (!adapter) return route
|
|
609
|
+
|
|
610
|
+
try {
|
|
611
|
+
const quote = await quoteWithTimeout(
|
|
612
|
+
adapter,
|
|
613
|
+
{
|
|
614
|
+
cache: options.cache,
|
|
615
|
+
destinationChain: route.destinationChain,
|
|
616
|
+
destinationToken: route.destinationToken,
|
|
617
|
+
include: options.include,
|
|
618
|
+
request: options.request,
|
|
619
|
+
route,
|
|
620
|
+
sourceAmount: route.sourceAmount,
|
|
621
|
+
sourceChain: route.sourceChain,
|
|
622
|
+
sourceToken: route.sourceToken,
|
|
623
|
+
},
|
|
624
|
+
options.timeoutMs,
|
|
625
|
+
)
|
|
626
|
+
return {
|
|
627
|
+
...route,
|
|
628
|
+
errorCode: quote.errorCode ?? null,
|
|
629
|
+
quality: {
|
|
630
|
+
...route.quality,
|
|
631
|
+
...(quote.status === 'error' ? { tier: 'error' as const } : {}),
|
|
632
|
+
...quote.quality,
|
|
633
|
+
},
|
|
634
|
+
quote: quote.status === 'error' ? null : formatRouteQuote(route, quote),
|
|
635
|
+
status: quote.status,
|
|
636
|
+
}
|
|
637
|
+
} catch (cause) {
|
|
638
|
+
return markRouteError(route, cause instanceof ProviderAdapterTimeoutError ? 'provider_timeout' : 'provider_error') // prettier-ignore
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
426
642
|
function formatChain(chain: SourceChainInput) {
|
|
427
643
|
const { addressFormat, id, kind, name, providerIds } = chain
|
|
428
644
|
return { addressFormat, id, kind, name, providerIds }
|
|
@@ -451,6 +667,44 @@ function formatProviderRef(provider: ProviderInput) {
|
|
|
451
667
|
return { id, kind, name, requiresApiKey }
|
|
452
668
|
}
|
|
453
669
|
|
|
670
|
+
function formatRouteCandidate(options: {
|
|
671
|
+
destinationToken: DestinationTokenInput
|
|
672
|
+
provider: ProviderInput
|
|
673
|
+
sourceAmount: ReturnType<typeof resolveSourceAmount>
|
|
674
|
+
sourceChain: SourceChainInput
|
|
675
|
+
sourceToken: TokenInput
|
|
676
|
+
}) {
|
|
677
|
+
return {
|
|
678
|
+
destinationChain,
|
|
679
|
+
destinationToken: formatDestinationToken(options.destinationToken),
|
|
680
|
+
errorCode: null,
|
|
681
|
+
id: [
|
|
682
|
+
options.sourceChain.slug,
|
|
683
|
+
options.sourceToken.symbol.toLowerCase(),
|
|
684
|
+
'tempo',
|
|
685
|
+
options.destinationToken.slug,
|
|
686
|
+
options.provider.id,
|
|
687
|
+
].join('-'),
|
|
688
|
+
provider: formatProviderRef(options.provider),
|
|
689
|
+
quality: {
|
|
690
|
+
availableVolumeUsd: null,
|
|
691
|
+
estimatedSeconds: null,
|
|
692
|
+
feeBps: null,
|
|
693
|
+
historicalVolumeUsd24h: null,
|
|
694
|
+
instantVolumeUsd: null,
|
|
695
|
+
liquiditySource: 'staticInventory',
|
|
696
|
+
score: null,
|
|
697
|
+
sourceDetail: null,
|
|
698
|
+
tier: 'unknown',
|
|
699
|
+
},
|
|
700
|
+
quote: null,
|
|
701
|
+
sourceAmount: options.sourceAmount,
|
|
702
|
+
sourceChain: formatChain(options.sourceChain),
|
|
703
|
+
sourceToken: formatSourceToken(options.sourceChain, options.sourceToken),
|
|
704
|
+
status: 'unknown',
|
|
705
|
+
} as const
|
|
706
|
+
}
|
|
707
|
+
|
|
454
708
|
function formatSourceToken(chain: SourceChainInput, token: TokenInput) {
|
|
455
709
|
return {
|
|
456
710
|
address: token.address,
|
|
@@ -482,6 +736,57 @@ function parseDecimalUnits(value: string, decimals: number) {
|
|
|
482
736
|
return baseUnits.toString()
|
|
483
737
|
}
|
|
484
738
|
|
|
739
|
+
function formatRouteQuote(route: RouteCandidate, quote: QuoteProbeResult) {
|
|
740
|
+
return {
|
|
741
|
+
expiresAt: quote.expiresAt ?? null,
|
|
742
|
+
inputAmount: route.sourceAmount.amount,
|
|
743
|
+
limits: quote.limits
|
|
744
|
+
? {
|
|
745
|
+
instantSourceAmount: quote.limits.instantSourceAmount ?? null,
|
|
746
|
+
maxSourceAmount: quote.limits.maxSourceAmount ?? null,
|
|
747
|
+
minSourceAmount: quote.limits.minSourceAmount ?? null,
|
|
748
|
+
}
|
|
749
|
+
: null,
|
|
750
|
+
minimumOutputAmount: quote.minimumOutputAmount ?? null,
|
|
751
|
+
outputAmount: quote.outputAmount ?? null,
|
|
752
|
+
outputAmountFormatted: quote.outputAmountFormatted ?? null,
|
|
753
|
+
sampledAt: quote.sampledAt,
|
|
754
|
+
} as const
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function markRouteError(route: RouteCandidate, errorCode: string) {
|
|
758
|
+
return {
|
|
759
|
+
...route,
|
|
760
|
+
errorCode,
|
|
761
|
+
quality: {
|
|
762
|
+
...route.quality,
|
|
763
|
+
liquiditySource: 'providerError',
|
|
764
|
+
score: null,
|
|
765
|
+
sourceDetail: null,
|
|
766
|
+
tier: 'error' as const,
|
|
767
|
+
},
|
|
768
|
+
quote: null,
|
|
769
|
+
status: 'error' as const,
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function quoteWithTimeout(adapter: ProviderAdapter, input: QuoteProbeInput, timeoutMs: number) {
|
|
774
|
+
const controller = new AbortController()
|
|
775
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs)
|
|
776
|
+
return Promise.race([
|
|
777
|
+
adapter.probeQuote(input, controller.signal),
|
|
778
|
+
new Promise<never>((_, reject) => {
|
|
779
|
+
if (controller.signal.aborted) {
|
|
780
|
+
reject(new ProviderAdapterTimeoutError())
|
|
781
|
+
return
|
|
782
|
+
}
|
|
783
|
+
controller.signal.addEventListener('abort', () => reject(new ProviderAdapterTimeoutError()), {
|
|
784
|
+
once: true,
|
|
785
|
+
})
|
|
786
|
+
}),
|
|
787
|
+
]).finally(() => clearTimeout(timeout))
|
|
788
|
+
}
|
|
789
|
+
|
|
485
790
|
function resolveDestinationToken(input: string | undefined) {
|
|
486
791
|
if (input === undefined) return destinationTokens.find((token) => token.slug === defaultDestinationToken)! // prettier-ignore
|
|
487
792
|
const normalized = normalizeInput(input)
|
|
@@ -572,11 +877,21 @@ export class InvalidAmountError extends Error {
|
|
|
572
877
|
override name = 'FundingRoutes.InvalidAmountError'
|
|
573
878
|
}
|
|
574
879
|
|
|
880
|
+
/** Error thrown internally when a provider adapter exceeds its route quote timeout. */
|
|
881
|
+
export class ProviderAdapterTimeoutError extends Error {
|
|
882
|
+
override name = 'FundingRoutes.ProviderAdapterTimeoutError'
|
|
883
|
+
}
|
|
884
|
+
|
|
575
885
|
/** Error thrown when the caller filters to an unknown provider. */
|
|
576
886
|
export class UnsupportedProviderError extends Error {
|
|
577
887
|
override name = 'FundingRoutes.UnsupportedProviderError'
|
|
578
888
|
}
|
|
579
889
|
|
|
890
|
+
/** Error thrown when the caller requests an unsupported include expansion. */
|
|
891
|
+
export class UnsupportedIncludeError extends Error {
|
|
892
|
+
override name = 'FundingRoutes.UnsupportedIncludeError'
|
|
893
|
+
}
|
|
894
|
+
|
|
580
895
|
/** Error thrown when the caller supplies an unsupported source chain. */
|
|
581
896
|
export class UnsupportedSourceChainError extends Error {
|
|
582
897
|
override name = 'FundingRoutes.UnsupportedSourceChainError'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"App.js","sourceRoot":"","sources":["../../../src/apps/platform/App.ts"],"names":[],"mappings":"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"}
|