tapimo 0.4.4 → 0.5.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/dist/App.d.ts +4701 -3416
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +24 -2
- package/dist/App.js.map +1 -1
- package/dist/Assets.js +3 -3
- package/dist/Assets.js.map +1 -1
- package/dist/Scope.d.ts +18 -1
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +23 -3
- package/dist/Scope.js.map +1 -1
- package/dist/Webhooks.d.ts +1 -1
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +5 -10
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +6 -34
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +3 -3
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +4 -1
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/scopes.d.ts +6 -34
- package/dist/admin/apps/scopes.d.ts.map +1 -1
- package/dist/admin/apps/scopes.js +2 -2
- package/dist/admin/apps/scopes.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +57 -57
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +33 -33
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/data/App.d.ts +4574 -4277
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +155 -77
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts +109 -0
- package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -0
- package/dist/apps/data/FundingQuoteProviders.js +464 -0
- package/dist/apps/data/FundingQuoteProviders.js.map +1 -0
- package/dist/apps/data/index.d.ts +1 -1
- package/dist/apps/data/index.js +1 -1
- package/dist/apps/data/routes/activities.d.ts +26 -26
- package/dist/apps/data/routes/addresses.d.ts +38 -38
- package/dist/apps/data/routes/balances.d.ts +45 -45
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +67 -149
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +2 -3
- package/dist/apps/data/routes/blocks.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.js +3 -5
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +177 -176
- package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
- package/dist/apps/data/routes/coingecko.js +3 -8
- package/dist/apps/data/routes/coingecko.js.map +1 -1
- package/dist/apps/data/routes/funding.d.ts +131 -228
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +152 -219
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +34 -34
- package/dist/apps/data/routes/indexer.d.ts.map +1 -1
- package/dist/apps/data/routes/indexer.js +9 -2
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +228 -228
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +86 -16
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +12 -12
- package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
- package/dist/apps/data/routes/webhooks.d.ts +340 -0
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +13 -1
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/index.d.ts +1 -1
- package/dist/apps/index.js +1 -1
- package/dist/apps/management/App.d.ts +1833 -851
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +4 -0
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +41 -41
- package/dist/apps/management/routes/api-keys.js +2 -2
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +48 -48
- package/dist/apps/management/routes/invitations.d.ts +132 -132
- package/dist/apps/management/routes/invite-links.d.ts +1100 -0
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -0
- package/dist/apps/management/routes/invite-links.js +411 -0
- package/dist/apps/management/routes/invite-links.js.map +1 -0
- package/dist/apps/management/routes/members.d.ts +41 -41
- package/dist/apps/management/routes/orgs.d.ts +92 -92
- package/dist/apps/management/routes/projects.d.ts +9 -9
- package/dist/apps/management/routes/scopes.d.ts.map +1 -1
- package/dist/apps/management/routes/scopes.js +2 -2
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +24 -24
- package/dist/apps/mcp.js +2 -2
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/relay/App.d.ts +2 -2
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +44 -6
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/db/Db.d.ts +3 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +57 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +31 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +31 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/inviteLinks.d.ts +42 -0
- package/dist/db/tables/inviteLinks.d.ts.map +1 -0
- package/dist/db/tables/inviteLinks.js +148 -0
- package/dist/db/tables/inviteLinks.js.map +1 -0
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +2 -0
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +6 -2
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +13 -3
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +9 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +138 -10
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +3 -1
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +17 -2
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/{FundingRoutes.d.ts → FundingQuotes.d.ts} +240 -369
- package/dist/internal/FundingQuotes.d.ts.map +1 -0
- package/dist/internal/{FundingRoutes.js → FundingQuotes.js} +144 -183
- package/dist/internal/FundingQuotes.js.map +1 -0
- package/dist/internal/Log.d.ts +18 -12
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +2 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/Tidx.d.ts +9 -3
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +13 -7
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Viem.d.ts +17 -2
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +30 -3
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal/index.js +1 -1
- package/package.json +2 -2
- package/src/ApiKeys.test.ts +25 -0
- package/src/App.test-d.ts +2 -0
- package/src/App.test.ts +13 -2
- package/src/App.ts +37 -5
- package/src/Assets.ts +3 -3
- package/src/Client.test-d.ts +21 -11
- package/src/Scope.test.ts +1 -0
- package/src/Scope.ts +32 -4
- package/src/Webhooks.ts +5 -10
- package/src/admin/apps/api-keys.test.ts +27 -0
- package/src/admin/apps/api-keys.ts +8 -1
- package/src/admin/apps/scopes.test.ts +7 -2
- package/src/admin/apps/scopes.ts +2 -2
- package/src/admin/ui/src/components/Dialog.tsx +8 -2
- package/src/admin/ui/src/lib/scopes.test.ts +19 -0
- package/src/admin/ui/src/lib/scopes.ts +12 -0
- package/src/admin/ui/src/routes/api-keys.tsx +128 -27
- package/src/admin/ui.generated.ts +33 -33
- package/src/apps/composition.test.ts +77 -0
- package/src/apps/data/App.test.ts +354 -0
- package/src/apps/data/App.ts +178 -91
- package/src/apps/data/FundingQuoteProviders.test.ts +577 -0
- package/src/apps/data/FundingQuoteProviders.ts +633 -0
- package/src/apps/data/index.ts +1 -1
- package/src/apps/data/routes/balances.test.ts +64 -18
- package/src/apps/data/routes/balances.ts +76 -177
- package/src/apps/data/routes/blocks.ts +3 -5
- package/src/apps/data/routes/coingecko.ts +3 -8
- package/src/apps/data/routes/funding.test.ts +304 -192
- package/src/apps/data/routes/funding.ts +166 -286
- package/src/apps/data/routes/indexer.test.ts +173 -15
- package/src/apps/data/routes/indexer.ts +8 -2
- package/src/apps/data/routes/rpc.test.ts +219 -0
- package/src/apps/data/routes/rpc.ts +92 -17
- package/src/apps/data/routes/webhooks.test.ts +21 -0
- package/src/apps/data/routes/webhooks.ts +13 -1
- package/src/apps/index.ts +1 -1
- package/src/apps/management/App.ts +4 -0
- package/src/apps/management/Billing.test.ts +2 -2
- package/src/apps/management/routes/api-keys.test.ts +47 -3
- package/src/apps/management/routes/api-keys.ts +2 -2
- package/src/apps/management/routes/invite-links.test.ts +385 -0
- package/src/apps/management/routes/invite-links.ts +570 -0
- package/src/apps/management/routes/scopes.test.ts +3 -2
- package/src/apps/management/routes/scopes.ts +4 -2
- package/src/apps/mcp.test.ts +18 -0
- package/src/apps/mcp.ts +2 -2
- package/src/apps/relay/App.test.ts +328 -7
- package/src/apps/relay/App.ts +42 -6
- package/src/apps/relay/Sponsorships.test.ts +2 -2
- package/src/db/Db.test.ts +5 -5
- package/src/db/Db.ts +65 -0
- package/src/db/Schema.ts +39 -0
- package/src/db/tables/billingSettings.test.ts +2 -2
- package/src/db/tables/inviteLinks.ts +187 -0
- package/src/db/tables/organizations.test.ts +2 -2
- package/src/db/tables/organizations.ts +2 -0
- package/src/db/tables/sponsoredTransactions.test.ts +19 -2
- package/src/db/tables/sponsoredTransactions.ts +21 -14
- package/src/db/tables/stripeCustomers.test.ts +2 -2
- package/src/handlers/relay.ts +16 -4
- package/src/internal/Auth.test.ts +132 -22
- package/src/internal/Auth.ts +181 -14
- package/src/internal/EdgeCache.test.ts +24 -3
- package/src/internal/EdgeCache.ts +18 -2
- package/src/internal/FundingQuotes.test.ts +268 -0
- package/src/internal/{FundingRoutes.ts → FundingQuotes.ts} +216 -277
- package/src/internal/Log.ts +21 -14
- package/src/internal/Tidx.test.ts +48 -0
- package/src/internal/Tidx.ts +21 -7
- package/src/internal/Viem.test.ts +46 -0
- package/src/internal/Viem.ts +48 -6
- package/src/internal/index.test.ts +1 -1
- package/src/internal/index.ts +1 -1
- package/dist/apps/data/FundingRouteProviders.d.ts +0 -36
- package/dist/apps/data/FundingRouteProviders.d.ts.map +0 -1
- package/dist/apps/data/FundingRouteProviders.js +0 -245
- package/dist/apps/data/FundingRouteProviders.js.map +0 -1
- package/dist/internal/FundingRoutes.d.ts.map +0 -1
- package/dist/internal/FundingRoutes.js.map +0 -1
- package/src/apps/data/FundingRouteProviders.test.ts +0 -225
- package/src/apps/data/FundingRouteProviders.ts +0 -312
- package/src/internal/FundingRoutes.test.ts +0 -213
|
@@ -1,235 +1,256 @@
|
|
|
1
1
|
import { Schema } from 'tapimo'
|
|
2
2
|
|
|
3
|
+
import type * as FundingQuotes from '../../../internal/FundingQuotes.js'
|
|
4
|
+
import type * as Log from '../../../internal/Log.js'
|
|
3
5
|
import * as TestApp from '../../../../test/App.js'
|
|
4
6
|
import * as Funding from './funding.js'
|
|
5
7
|
|
|
6
|
-
describe('GET /funding/
|
|
7
|
-
test('returns
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
query: {
|
|
11
|
-
destinationToken: 'USDC.e',
|
|
12
|
-
sourceAmount: '1000000',
|
|
13
|
-
sourceChain: 'base',
|
|
14
|
-
sourceToken: 'USDC',
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
TestApp.auth,
|
|
18
|
-
)
|
|
8
|
+
describe('GET /funding/quotes', () => {
|
|
9
|
+
test('returns successful live quotes with output amounts', async () => {
|
|
10
|
+
const app = TestApp.create({ funding: { adapters: { relay: relayAdapter() } } })
|
|
11
|
+
const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
|
|
19
12
|
expect(response.status).toBe(200)
|
|
20
|
-
|
|
13
|
+
expect(response.headers.get('cache-control')).toBe('no-store')
|
|
14
|
+
const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
|
|
21
15
|
|
|
22
|
-
expect(body.data
|
|
16
|
+
expect(body.data).toHaveLength(1)
|
|
23
17
|
expect(body.data[0]).toMatchObject({
|
|
24
|
-
destinationChain: { id: 'eip155:4217', name: 'Tempo' },
|
|
25
|
-
destinationToken: { symbol: 'USDC.e' },
|
|
26
18
|
id: 'base-usdc-tempo-usdce-relay',
|
|
27
|
-
|
|
19
|
+
provider: { id: 'relay' },
|
|
20
|
+
quality: { liquiditySource: 'providerQuote', tier: 'liquid' },
|
|
21
|
+
quote: {
|
|
22
|
+
inputAmount: '1000000',
|
|
23
|
+
outputAmount: '990000',
|
|
24
|
+
outputAmountFormatted: '0.99',
|
|
25
|
+
},
|
|
28
26
|
sourceAmount: { amount: '1000000', formatted: '1' },
|
|
29
|
-
sourceChain: { id: 'eip155:8453', name: 'Base' },
|
|
30
|
-
sourceToken: { symbol: 'USDC' },
|
|
31
|
-
status: 'unknown',
|
|
32
27
|
})
|
|
33
28
|
})
|
|
34
29
|
|
|
35
|
-
test('
|
|
30
|
+
test('queries only the requested provider', async () => {
|
|
31
|
+
let acrossCalls = 0
|
|
32
|
+
let relayCalls = 0
|
|
36
33
|
const app = TestApp.create({
|
|
37
|
-
|
|
34
|
+
funding: {
|
|
38
35
|
adapters: {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expect(input.sourceAmount.amount).toBe('1000000')
|
|
46
|
-
expect(input.sourceChain.id).toBe('eip155:8453')
|
|
47
|
-
expect(input.sourceToken.symbol).toBe('USDC')
|
|
48
|
-
return {
|
|
49
|
-
expiresAt: '2026-01-01T00:00:30Z',
|
|
50
|
-
limits: {
|
|
51
|
-
instantSourceAmount: '1000000000',
|
|
52
|
-
maxSourceAmount: '100000000000',
|
|
53
|
-
minSourceAmount: '500000',
|
|
54
|
-
},
|
|
55
|
-
minimumOutputAmount: '980000',
|
|
56
|
-
outputAmount: '990000',
|
|
57
|
-
outputAmountFormatted: '0.99',
|
|
58
|
-
quality: {
|
|
59
|
-
availableVolumeUsd: '1000.00',
|
|
60
|
-
estimatedSeconds: 4,
|
|
61
|
-
feeBps: 12,
|
|
62
|
-
historicalVolumeUsd24h: '100000.00',
|
|
63
|
-
instantVolumeUsd: '1000.00',
|
|
64
|
-
liquiditySource: 'providerQuote',
|
|
65
|
-
score: 91,
|
|
66
|
-
sourceDetail: 'providerEndpoint',
|
|
67
|
-
tier: 'liquid',
|
|
68
|
-
},
|
|
69
|
-
sampledAt: '2026-01-01T00:00:00Z',
|
|
70
|
-
status: 'available',
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
},
|
|
36
|
+
across: acrossAdapter(() => {
|
|
37
|
+
acrossCalls += 1
|
|
38
|
+
}),
|
|
39
|
+
relay: relayAdapter(() => {
|
|
40
|
+
relayCalls += 1
|
|
41
|
+
}),
|
|
74
42
|
},
|
|
75
43
|
},
|
|
76
44
|
})
|
|
45
|
+
const response = await app.request(quoteUrl({ ...baseUsdcQuery, provider: 'across' }), {
|
|
46
|
+
headers: TestApp.auth.headers,
|
|
47
|
+
})
|
|
77
48
|
|
|
49
|
+
expect(response.status).toBe(200)
|
|
50
|
+
expect(acrossCalls).toBe(1)
|
|
51
|
+
expect(relayCalls).toBe(0)
|
|
52
|
+
const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
|
|
53
|
+
expect(body.data[0]?.quote.expiresAt).toBe('2026-01-01T00:01:00Z')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('returns non-EVM Rhino quotes with stable chain and token ids', async () => {
|
|
57
|
+
const app = TestApp.create({ funding: { adapters: { rhino: rhinoAdapter() } } })
|
|
78
58
|
const response = await app.request(
|
|
79
|
-
|
|
80
|
-
|
|
59
|
+
quoteUrl({
|
|
60
|
+
destinationToken: 'USDC.e',
|
|
61
|
+
provider: 'rhino',
|
|
62
|
+
sourceAmount: '1000000',
|
|
63
|
+
sourceChain: 'solana',
|
|
64
|
+
sourceToken: 'USDC',
|
|
65
|
+
}),
|
|
66
|
+
{ headers: TestApp.auth.headers },
|
|
81
67
|
)
|
|
82
|
-
expect(response.status).toBe(200)
|
|
83
|
-
const body = await TestApp.json(response, Funding.schema.listFundingRoutes.Response)
|
|
84
68
|
|
|
85
|
-
expect(
|
|
69
|
+
expect(response.status).toBe(200)
|
|
70
|
+
const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
|
|
86
71
|
expect(body.data[0]).toMatchObject({
|
|
87
|
-
|
|
88
|
-
id: '
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
tier: 'liquid',
|
|
72
|
+
id: 'solana-usdc-tempo-usdce-rhino',
|
|
73
|
+
provider: { id: 'rhino' },
|
|
74
|
+
sourceChain: {
|
|
75
|
+
addressFormat: 'base58',
|
|
76
|
+
id: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
77
|
+
kind: 'solana',
|
|
94
78
|
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
limits: {
|
|
99
|
-
instantSourceAmount: '1000000000',
|
|
100
|
-
maxSourceAmount: '100000000000',
|
|
101
|
-
minSourceAmount: '500000',
|
|
102
|
-
},
|
|
103
|
-
minimumOutputAmount: '980000',
|
|
104
|
-
outputAmount: '990000',
|
|
105
|
-
outputAmountFormatted: '0.99',
|
|
106
|
-
sampledAt: '2026-01-01T00:00:00Z',
|
|
79
|
+
sourceToken: {
|
|
80
|
+
tokenKey:
|
|
81
|
+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
107
82
|
},
|
|
108
|
-
status: 'available',
|
|
109
83
|
})
|
|
110
84
|
})
|
|
111
85
|
|
|
112
|
-
test('
|
|
113
|
-
const response = await TestApp.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
provider: 'relay',
|
|
117
|
-
sourceAmount: '1.25',
|
|
118
|
-
sourceAmountUnits: 'decimal',
|
|
119
|
-
sourceChain: 'eip155:8453',
|
|
120
|
-
sourceToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
TestApp.auth,
|
|
124
|
-
)
|
|
86
|
+
test('returns an empty list when no provider is configured', async () => {
|
|
87
|
+
const response = await TestApp.create().request(quoteUrl(), {
|
|
88
|
+
headers: TestApp.auth.headers,
|
|
89
|
+
})
|
|
125
90
|
expect(response.status).toBe(200)
|
|
126
|
-
const body = await TestApp.json(response, Funding.schema.
|
|
91
|
+
const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
|
|
92
|
+
expect(body.data).toEqual([])
|
|
93
|
+
})
|
|
127
94
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
95
|
+
test('returns an empty list when the provider reports no quote', async () => {
|
|
96
|
+
const app = TestApp.create({
|
|
97
|
+
funding: {
|
|
98
|
+
adapters: {
|
|
99
|
+
relay: {
|
|
100
|
+
async probeQuote() {
|
|
101
|
+
return { sampledAt: '2026-01-01T00:00:00Z', status: 'unavailable' }
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
132
106
|
})
|
|
107
|
+
const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
|
|
108
|
+
expect(response.status).toBe(200)
|
|
109
|
+
const body = await TestApp.json(response, Funding.schema.quoteFunding.Response)
|
|
110
|
+
expect(body.data).toEqual([])
|
|
133
111
|
})
|
|
134
112
|
|
|
135
|
-
test('
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
113
|
+
test('returns 502 when every matching provider fails', async () => {
|
|
114
|
+
const app = TestApp.create({
|
|
115
|
+
funding: {
|
|
116
|
+
adapters: {
|
|
117
|
+
relay: {
|
|
118
|
+
async probeQuote() {
|
|
119
|
+
throw new Error('upstream unavailable')
|
|
120
|
+
},
|
|
121
|
+
},
|
|
143
122
|
},
|
|
144
123
|
},
|
|
145
|
-
|
|
146
|
-
)
|
|
147
|
-
expect(
|
|
148
|
-
const
|
|
149
|
-
expect(
|
|
124
|
+
})
|
|
125
|
+
const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
|
|
126
|
+
expect(response.status).toBe(502)
|
|
127
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
128
|
+
expect(body.error.code).toBe('upstream_error')
|
|
129
|
+
})
|
|
150
130
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
131
|
+
test('logs provider quote deadlines as timeouts', async () => {
|
|
132
|
+
const entries: Log.Entry[] = []
|
|
133
|
+
const app = TestApp.create({
|
|
134
|
+
funding: {
|
|
135
|
+
adapterTimeoutMs: 1,
|
|
136
|
+
adapters: {
|
|
137
|
+
across: {
|
|
138
|
+
probeQuote() {
|
|
139
|
+
return new Promise<FundingQuotes.QuoteProbeResult>(() => {})
|
|
140
|
+
},
|
|
141
|
+
},
|
|
157
142
|
},
|
|
158
143
|
},
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
lowercasedSolanaResponse,
|
|
164
|
-
Funding.schema.listFundingRoutes.Response,
|
|
165
|
-
)
|
|
166
|
-
expect(lowercasedSolanaBody.data).toEqual([])
|
|
144
|
+
logger: (entry) => {
|
|
145
|
+
entries.push(entry)
|
|
146
|
+
},
|
|
147
|
+
})
|
|
167
148
|
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
149
|
+
const response = await app.request(quoteUrl({ ...baseUsdcQuery, provider: 'across' }), {
|
|
150
|
+
headers: TestApp.auth.headers,
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
expect(response.status).toBe(502)
|
|
154
|
+
expect(entries[0]?.provider).toEqual({
|
|
155
|
+
failure: 'timeout',
|
|
156
|
+
id: 'across',
|
|
157
|
+
operation: 'swap/approval',
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('logs every failure when multiple providers fail', async () => {
|
|
162
|
+
const entries: Log.Entry[] = []
|
|
163
|
+
const app = TestApp.create({
|
|
164
|
+
funding: {
|
|
165
|
+
adapters: {
|
|
166
|
+
across: {
|
|
167
|
+
async probeQuote() {
|
|
168
|
+
throw new TypeError('Across network failure')
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
relay: {
|
|
172
|
+
async probeQuote() {
|
|
173
|
+
throw new Error('Relay failure')
|
|
174
|
+
},
|
|
175
|
+
},
|
|
174
176
|
},
|
|
175
177
|
},
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
expect(
|
|
178
|
+
logger: (entry) => {
|
|
179
|
+
entries.push(entry)
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
|
|
184
|
+
|
|
185
|
+
expect(response.status).toBe(502)
|
|
186
|
+
expect(entries[0]?.provider).toEqual({
|
|
187
|
+
failure: 'network',
|
|
188
|
+
id: 'across',
|
|
189
|
+
operation: 'swap/approval',
|
|
190
|
+
})
|
|
191
|
+
expect(entries[0]?.providerFailures).toEqual([
|
|
192
|
+
{ failure: 'network', id: 'across', operation: 'swap/approval' },
|
|
193
|
+
{ failure: 'unknown', id: 'relay', operation: 'quote/v2' },
|
|
194
|
+
])
|
|
184
195
|
})
|
|
185
196
|
|
|
186
|
-
test('
|
|
187
|
-
const
|
|
188
|
-
{
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
197
|
+
test('rejects successful provider results without output amounts', async () => {
|
|
198
|
+
const app = TestApp.create({
|
|
199
|
+
funding: {
|
|
200
|
+
adapters: {
|
|
201
|
+
relay: {
|
|
202
|
+
async probeQuote() {
|
|
203
|
+
return { sampledAt: '2026-01-01T00:00:00Z', status: 'available' }
|
|
204
|
+
},
|
|
205
|
+
},
|
|
194
206
|
},
|
|
195
207
|
},
|
|
196
|
-
|
|
208
|
+
})
|
|
209
|
+
const response = await app.request(quoteUrl(), { headers: TestApp.auth.headers })
|
|
210
|
+
expect(response.status).toBe(502)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('rejects invalid query parameters', async () => {
|
|
214
|
+
const response = await TestApp.create().request(
|
|
215
|
+
quoteUrl({ sourceAmount: '1000000', sourceChain: 'base' }),
|
|
216
|
+
{ headers: TestApp.auth.headers },
|
|
197
217
|
)
|
|
198
|
-
expect(response.status).toBe(
|
|
199
|
-
const body = await TestApp.json(response,
|
|
200
|
-
expect(body.
|
|
218
|
+
expect(response.status).toBe(400)
|
|
219
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
220
|
+
expect(body.error.code).toBe('query_invalid')
|
|
201
221
|
})
|
|
202
222
|
|
|
203
|
-
test('rejects
|
|
204
|
-
const response = await TestApp.
|
|
205
|
-
{
|
|
206
|
-
|
|
207
|
-
sourceAmount: '1.0000001',
|
|
208
|
-
sourceAmountUnits: 'decimal',
|
|
209
|
-
sourceChain: 'base',
|
|
210
|
-
sourceToken: 'USDC',
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
TestApp.auth,
|
|
223
|
+
test('rejects unsupported providers', async () => {
|
|
224
|
+
const response = await TestApp.create().request(
|
|
225
|
+
quoteUrl({ ...baseUsdcQuery, provider: 'bridge' }),
|
|
226
|
+
{ headers: TestApp.auth.headers },
|
|
214
227
|
)
|
|
215
228
|
expect(response.status).toBe(400)
|
|
216
229
|
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
217
230
|
expect(body.error.code).toBe('query_invalid')
|
|
218
231
|
})
|
|
219
232
|
|
|
220
|
-
test('rejects unsupported
|
|
233
|
+
test('rejects unsupported source chains', async () => {
|
|
221
234
|
const response = await TestApp.create().request(
|
|
222
|
-
|
|
235
|
+
quoteUrl({ ...baseUsdcQuery, sourceChain: 'bitcoin' }),
|
|
223
236
|
{ headers: TestApp.auth.headers },
|
|
224
237
|
)
|
|
225
238
|
expect(response.status).toBe(400)
|
|
226
239
|
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
227
240
|
expect(body.error.code).toBe('query_invalid')
|
|
228
241
|
})
|
|
242
|
+
|
|
243
|
+
test('does not expose the removed routes endpoint', async () => {
|
|
244
|
+
const response = await TestApp.create().request(
|
|
245
|
+
'/v1/funding/routes?sourceChain=base&sourceToken=USDC&sourceAmount=1000000',
|
|
246
|
+
{ headers: TestApp.auth.headers },
|
|
247
|
+
)
|
|
248
|
+
expect(response.status).toBe(404)
|
|
249
|
+
})
|
|
229
250
|
})
|
|
230
251
|
|
|
231
252
|
describe('GET /funding/chains', () => {
|
|
232
|
-
test('lists
|
|
253
|
+
test('lists only inputs supported by quote providers', async () => {
|
|
233
254
|
const response = await TestApp.client().v1.funding.chains.$get(undefined, TestApp.auth)
|
|
234
255
|
expect(response.status).toBe(200)
|
|
235
256
|
const body = await TestApp.json(response, Funding.schema.listFundingChains.Response)
|
|
@@ -238,42 +259,133 @@ describe('GET /funding/chains', () => {
|
|
|
238
259
|
'eip155:1',
|
|
239
260
|
'eip155:8453',
|
|
240
261
|
'eip155:56',
|
|
241
|
-
'solana:
|
|
242
|
-
'tron:
|
|
262
|
+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
263
|
+
'tron:0x2b6653dc',
|
|
243
264
|
])
|
|
244
|
-
expect(body.data.
|
|
245
|
-
expect.arrayContaining([
|
|
265
|
+
expect(body.data.flatMap((chain) => chain.tokens.map((token) => token.symbol))).toEqual(
|
|
266
|
+
expect.arrayContaining(['USDC', 'USDT']),
|
|
246
267
|
)
|
|
247
268
|
})
|
|
248
269
|
})
|
|
249
270
|
|
|
250
|
-
describe('GET /providers', () => {
|
|
251
|
-
test('lists providers
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
TestApp.auth,
|
|
255
|
-
)
|
|
271
|
+
describe('GET /funding/providers', () => {
|
|
272
|
+
test('lists quote providers and their configuration status', async () => {
|
|
273
|
+
const app = TestApp.create({ funding: { adapters: { relay: relayAdapter() } } })
|
|
274
|
+
const response = await app.request('/v1/funding/providers', {
|
|
275
|
+
headers: TestApp.auth.headers,
|
|
276
|
+
})
|
|
256
277
|
expect(response.status).toBe(200)
|
|
257
278
|
const body = await TestApp.json(response, Funding.schema.listProviders.Response)
|
|
258
279
|
|
|
259
|
-
expect(body.data
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
280
|
+
expect(body.data).toEqual([
|
|
281
|
+
{
|
|
282
|
+
id: 'across',
|
|
283
|
+
kind: 'intentBridge',
|
|
284
|
+
name: 'Across',
|
|
285
|
+
requiresApiKey: true,
|
|
286
|
+
status: 'unconfigured',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: 'relay',
|
|
290
|
+
kind: 'solverNetwork',
|
|
291
|
+
name: 'Relay',
|
|
292
|
+
requiresApiKey: false,
|
|
293
|
+
status: 'configured',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'rhino',
|
|
297
|
+
kind: 'bridge',
|
|
298
|
+
name: 'Rhino',
|
|
299
|
+
requiresApiKey: false,
|
|
300
|
+
status: 'unconfigured',
|
|
301
|
+
},
|
|
268
302
|
])
|
|
269
303
|
})
|
|
270
304
|
|
|
271
|
-
test('
|
|
272
|
-
const response = await TestApp.create().request('/v1/providers
|
|
305
|
+
test('does not expose the removed generic provider endpoint', async () => {
|
|
306
|
+
const response = await TestApp.create().request('/v1/providers', {
|
|
273
307
|
headers: TestApp.auth.headers,
|
|
274
308
|
})
|
|
275
|
-
expect(response.status).toBe(
|
|
276
|
-
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
277
|
-
expect(body.error.code).toBe('query_invalid')
|
|
309
|
+
expect(response.status).toBe(404)
|
|
278
310
|
})
|
|
279
311
|
})
|
|
312
|
+
|
|
313
|
+
const baseUsdcQuery = {
|
|
314
|
+
sourceAmount: '1000000',
|
|
315
|
+
sourceChain: 'base',
|
|
316
|
+
sourceToken: 'USDC',
|
|
317
|
+
} as const
|
|
318
|
+
|
|
319
|
+
type QuoteQuery = Record<string, string>
|
|
320
|
+
|
|
321
|
+
function quoteUrl(query: QuoteQuery = baseUsdcQuery) {
|
|
322
|
+
return `/v1/funding/quotes?${new URLSearchParams(
|
|
323
|
+
Object.entries(query).map(([key, value]) => [key, String(value)]),
|
|
324
|
+
)}`
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function relayAdapter(onProbe: () => void = () => {}): FundingQuotes.ProviderAdapter {
|
|
328
|
+
return {
|
|
329
|
+
async probeQuote(input, signal) {
|
|
330
|
+
onProbe()
|
|
331
|
+
expect(signal.aborted).toBe(false)
|
|
332
|
+
expect(input.candidate.id).toBe('base-usdc-tempo-usdce-relay')
|
|
333
|
+
return {
|
|
334
|
+
minimumOutputAmount: '980000',
|
|
335
|
+
outputAmount: '990000',
|
|
336
|
+
outputAmountFormatted: '0.99',
|
|
337
|
+
quality: {
|
|
338
|
+
estimatedSeconds: 4,
|
|
339
|
+
liquiditySource: 'providerQuote',
|
|
340
|
+
sourceDetail: 'relay:quote-v2',
|
|
341
|
+
tier: 'liquid',
|
|
342
|
+
},
|
|
343
|
+
sampledAt: '2026-01-01T00:00:00Z',
|
|
344
|
+
status: 'available',
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function acrossAdapter(onProbe: () => void = () => {}): FundingQuotes.ProviderAdapter {
|
|
351
|
+
return {
|
|
352
|
+
async probeQuote(input, signal) {
|
|
353
|
+
onProbe()
|
|
354
|
+
expect(signal.aborted).toBe(false)
|
|
355
|
+
expect(input.candidate.id).toBe('base-usdc-tempo-usdce-across')
|
|
356
|
+
return {
|
|
357
|
+
expiresAt: '2026-01-01T00:01:00Z',
|
|
358
|
+
minimumOutputAmount: '988000',
|
|
359
|
+
outputAmount: '990000',
|
|
360
|
+
outputAmountFormatted: '0.99',
|
|
361
|
+
quality: {
|
|
362
|
+
estimatedSeconds: 2,
|
|
363
|
+
liquiditySource: 'providerQuote',
|
|
364
|
+
sourceDetail: 'across:swap-approval',
|
|
365
|
+
tier: 'liquid',
|
|
366
|
+
},
|
|
367
|
+
sampledAt: '2026-01-01T00:00:00Z',
|
|
368
|
+
status: 'available',
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function rhinoAdapter(): FundingQuotes.ProviderAdapter {
|
|
375
|
+
return {
|
|
376
|
+
async probeQuote(input, signal) {
|
|
377
|
+
expect(signal.aborted).toBe(false)
|
|
378
|
+
expect(input.candidate.id).toBe('solana-usdc-tempo-usdce-rhino')
|
|
379
|
+
return {
|
|
380
|
+
outputAmount: '983000',
|
|
381
|
+
quality: {
|
|
382
|
+
liquiditySource: 'providerQuote',
|
|
383
|
+
sourceDetail: 'rhino:bridge-swap-public',
|
|
384
|
+
tier: 'liquid',
|
|
385
|
+
},
|
|
386
|
+
sampledAt: '2026-01-01T00:00:00Z',
|
|
387
|
+
status: 'available',
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
}
|
|
391
|
+
}
|