tapimo 0.7.2 → 0.9.0
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 +10 -7
- package/dist/ApiKey.d.ts +3 -0
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +10 -0
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +7 -3
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +9 -2
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +20117 -19183
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +61 -4
- package/dist/App.js.map +1 -1
- package/dist/admin/App.d.ts +84 -74
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +14 -1
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +6 -2
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +2 -0
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +11 -11
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +67 -67
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +2 -0
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +3 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/apps/data/App.d.ts +11696 -13937
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +4 -8
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +271 -271
- package/dist/apps/data/routes/addresses.d.ts +398 -167
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +278 -8
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +114 -2
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +123 -123
- package/dist/apps/data/routes/coingecko.d.ts +170 -170
- package/dist/apps/data/routes/earn.d.ts +498 -6
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +876 -4
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +10 -10
- package/dist/apps/data/routes/fee-amm.d.ts +2 -2
- package/dist/apps/data/routes/indexer.d.ts +6 -6
- package/dist/apps/data/routes/indexer.js +4 -1
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +235 -235
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/rpc.js +4 -1
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +36 -36
- package/dist/apps/data/routes/tokens.d.ts +449 -449
- package/dist/apps/data/routes/transactions.d.ts +2 -2
- package/dist/apps/data/routes/transfers.d.ts +81 -81
- package/dist/apps/data/routes/valuation.d.ts +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/webhooks.d.ts +9 -9
- package/dist/apps/data/routes/zones.d.ts +14 -14
- package/dist/apps/funding/Access.d.ts +16 -0
- package/dist/apps/funding/Access.d.ts.map +1 -0
- package/dist/apps/funding/Access.js +22 -0
- package/dist/apps/funding/Access.js.map +1 -0
- package/dist/apps/{data/routes/funding.d.ts → funding/App.d.ts} +1640 -3217
- package/dist/apps/funding/App.d.ts.map +1 -0
- package/dist/apps/funding/App.js +61 -0
- package/dist/apps/funding/App.js.map +1 -0
- package/dist/apps/funding/Inventory.d.ts +5 -0
- package/dist/apps/funding/Inventory.d.ts.map +1 -0
- package/dist/apps/funding/Inventory.js +25 -0
- package/dist/apps/funding/Inventory.js.map +1 -0
- package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
- package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
- package/dist/apps/funding/ProviderAttempts.js +24 -0
- package/dist/apps/funding/ProviderAttempts.js.map +1 -0
- package/dist/apps/funding/index.d.ts +2 -0
- package/dist/apps/funding/index.d.ts.map +1 -0
- package/dist/apps/funding/index.js +2 -0
- package/dist/apps/funding/index.js.map +1 -0
- package/dist/apps/funding/routes/chains.d.ts +169 -0
- package/dist/apps/funding/routes/chains.d.ts.map +1 -0
- package/dist/apps/funding/routes/chains.js +80 -0
- package/dist/apps/funding/routes/chains.js.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts +1997 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.js +778 -0
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
- package/dist/apps/funding/routes/providers.d.ts +91 -0
- package/dist/apps/funding/routes/providers.d.ts.map +1 -0
- package/dist/apps/funding/routes/providers.js +56 -0
- package/dist/apps/funding/routes/providers.js.map +1 -0
- package/dist/apps/funding/routes/quotes.d.ts +426 -0
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
- package/dist/apps/funding/routes/quotes.js +188 -0
- package/dist/apps/funding/routes/quotes.js.map +1 -0
- package/dist/apps/funding/routes/transfers.d.ts +1999 -0
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
- package/dist/apps/funding/routes/transfers.js +614 -0
- package/dist/apps/funding/routes/transfers.js.map +1 -0
- package/dist/apps/index.d.ts +1 -0
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -0
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +430 -165
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +298 -21
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +75 -1
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +6 -6
- package/dist/apps/management/routes/faucet.d.ts +35 -35
- package/dist/apps/management/routes/invitations.d.ts +122 -122
- package/dist/apps/management/routes/invite-links.d.ts +38 -38
- package/dist/apps/management/routes/members.d.ts +69 -69
- package/dist/apps/management/routes/orgs.d.ts +99 -99
- package/dist/apps/management/routes/projects.d.ts +25 -25
- package/dist/apps/management/routes/scopes.d.ts +1 -1
- package/dist/apps/management/routes/usage.d.ts +72 -72
- package/dist/apps/management/routes/verified-token-requests.d.ts +77 -77
- package/dist/apps/management/routes/webhooks.d.ts +128 -128
- package/dist/apps/mcp.d.ts +2 -3
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +16 -14
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/metadata.d.ts +4 -0
- package/dist/apps/metadata.d.ts.map +1 -1
- package/dist/apps/metadata.js +10 -0
- package/dist/apps/metadata.js.map +1 -1
- package/dist/apps/mpp/App.d.ts +44 -44
- package/dist/cloudflare.d.ts +50 -0
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +178 -0
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +45 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +12 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +20 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.d.ts +13 -0
- package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.js +12 -0
- package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
- package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
- package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
- package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
- package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
- package/dist/db/tables/fundingDeposits.d.ts +74 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
- package/dist/db/tables/fundingDeposits.js +126 -0
- package/dist/db/tables/fundingDeposits.js.map +1 -1
- package/dist/internal/Auth.d.ts +15 -11
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +52 -17
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.js +1 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +2 -2
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +10 -4
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/IpAllowlist.d.ts +15 -0
- package/dist/internal/IpAllowlist.d.ts.map +1 -0
- package/dist/internal/IpAllowlist.js +27 -0
- package/dist/internal/IpAllowlist.js.map +1 -0
- package/dist/internal/OpenApi.d.ts +1 -1
- package/dist/internal/OpenApi.js +1 -1
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Tidx.d.ts +6 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +13 -3
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Viem.d.ts +8 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +12 -5
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +42 -0
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +37 -5
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/Evidence.d.ts +1 -5
- package/dist/internal/funding/Evidence.d.ts.map +1 -1
- package/dist/internal/funding/Evidence.js +4 -3
- package/dist/internal/funding/Evidence.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +102 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +5 -0
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts +16 -2
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +348 -54
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/SourceObservation.d.ts +108 -0
- package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
- package/dist/internal/funding/SourceObservation.js +143 -0
- package/dist/internal/funding/SourceObservation.js.map +1 -0
- package/dist/internal/funding/Subsidy.d.ts +2 -2
- package/dist/internal/funding/Subsidy.d.ts.map +1 -1
- package/dist/internal/funding/Subsidy.js +1 -1
- package/dist/internal/funding/Subsidy.js.map +1 -1
- package/dist/internal/funding/index.d.ts +1 -0
- package/dist/internal/funding/index.d.ts.map +1 -1
- package/dist/internal/funding/index.js +1 -0
- package/dist/internal/funding/index.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +43 -1
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +438 -27
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/package.json +3 -3
- package/src/ApiKey.test.ts +35 -0
- package/src/ApiKey.ts +27 -0
- package/src/ApiKeys.test.ts +29 -0
- package/src/ApiKeys.ts +11 -3
- package/src/App.test.ts +55 -5
- package/src/App.ts +79 -5
- package/src/Client.test-d.ts +203 -28
- package/src/admin/apps/api-keys.test.ts +25 -1
- package/src/admin/apps/api-keys.ts +6 -2
- package/src/admin/ui.generated.ts +67 -67
- package/src/analytics/Analytics.ts +4 -0
- package/src/apps/composition.test.ts +130 -3
- package/src/apps/data/App.test.ts +0 -12
- package/src/apps/data/App.ts +4 -8
- package/src/apps/data/routes/balances.test.ts +42 -0
- package/src/apps/data/routes/balances.ts +218 -85
- package/src/apps/data/routes/earn.test.ts +363 -0
- package/src/apps/data/routes/earn.ts +1141 -3
- package/src/apps/data/routes/indexer.ts +2 -1
- package/src/apps/data/routes/rpc.ts +2 -0
- package/src/apps/data/routes/tokens.test.ts +6 -8
- package/src/apps/funding/Access.ts +26 -0
- package/src/apps/funding/App.test.ts +78 -0
- package/src/apps/funding/App.ts +78 -0
- package/src/apps/funding/Inventory.ts +30 -0
- package/src/apps/funding/ProviderAttempts.ts +26 -0
- package/src/apps/funding/index.ts +1 -0
- package/src/apps/funding/routes/chains.test.ts +78 -0
- package/src/apps/funding/routes/chains.ts +101 -0
- package/src/apps/funding/routes/deposit-addresses.test.ts +1649 -0
- package/src/apps/funding/routes/deposit-addresses.ts +932 -0
- package/src/apps/funding/routes/providers.test.ts +165 -0
- package/src/apps/funding/routes/providers.ts +73 -0
- package/src/apps/funding/routes/quotes.test.ts +511 -0
- package/src/apps/funding/routes/quotes.ts +205 -0
- package/src/apps/funding/routes/transfers.test.ts +1236 -0
- package/src/apps/funding/routes/transfers.ts +704 -0
- package/src/apps/index.ts +1 -0
- package/src/apps/management/routes/api-keys.test.ts +98 -0
- package/src/apps/management/routes/api-keys.ts +96 -1
- package/src/apps/mcp.test.ts +17 -2
- package/src/apps/mcp.ts +18 -13
- package/src/apps/metadata.ts +12 -0
- package/src/apps/mpp/internal/Policy.test.ts +7 -1
- package/src/cloudflare.ts +279 -0
- package/src/db/Db.ts +71 -0
- package/src/db/Schema.ts +24 -0
- package/src/db/tables/fundingDepositAddresses.ts +28 -0
- package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
- package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
- package/src/db/tables/fundingDeposits.test.ts +239 -0
- package/src/db/tables/fundingDeposits.ts +214 -1
- package/src/internal/Auth.test.ts +205 -5
- package/src/internal/Auth.ts +63 -19
- package/src/internal/Cache.test.ts +37 -3
- package/src/internal/Cache.ts +1 -1
- package/src/internal/EdgeCache.test.ts +7 -7
- package/src/internal/EdgeCache.ts +13 -4
- package/src/internal/IpAllowlist.test.ts +52 -0
- package/src/internal/IpAllowlist.ts +39 -0
- package/src/internal/Log.test.ts +7 -1
- package/src/internal/OpenApi.ts +1 -1
- package/src/internal/Tidx.test.ts +28 -0
- package/src/internal/Tidx.ts +19 -3
- package/src/internal/Viem.test.ts +28 -0
- package/src/internal/Viem.ts +21 -5
- package/src/internal/funding/Deposit.ts +74 -5
- package/src/internal/funding/Evidence.test.ts +2 -2
- package/src/internal/funding/Evidence.ts +4 -9
- package/src/internal/funding/Provider.ts +115 -0
- package/src/internal/funding/Reconciliation.local.test.ts +7 -0
- package/src/internal/funding/Reconciliation.test.ts +1364 -109
- package/src/internal/funding/Reconciliation.ts +467 -88
- package/src/internal/funding/SourceObservation.test.ts +301 -0
- package/src/internal/funding/SourceObservation.ts +267 -0
- package/src/internal/funding/Subsidy.ts +2 -2
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/relay.test.ts +94 -4
- package/src/internal/funding/providers/relay.ts +533 -27
- package/dist/apps/data/routes/funding.d.ts.map +0 -1
- package/dist/apps/data/routes/funding.js +0 -1557
- package/dist/apps/data/routes/funding.js.map +0 -1
- package/src/apps/data/routes/funding.test.ts +0 -2994
- package/src/apps/data/routes/funding.ts +0 -1821
|
@@ -259,7 +259,7 @@ async function passthrough(
|
|
|
259
259
|
// Resolve the upstream for this caller. A resolver `tidx` can route by
|
|
260
260
|
// principal (e.g. anonymous `type: 'public'` callers to a public, no-auth
|
|
261
261
|
// indexer); a static config ignores the principal.
|
|
262
|
-
const { baseUrl, basicAuth, bearerAuth } = Tidx.resolve(tidx, {
|
|
262
|
+
const { baseUrl, basicAuth, bearerAuth, zoneHeaders } = Tidx.resolve(tidx, {
|
|
263
263
|
chainId,
|
|
264
264
|
principal: Auth.getPrincipal(c),
|
|
265
265
|
zone: c.get('zones').get(chainId),
|
|
@@ -296,6 +296,7 @@ async function passthrough(
|
|
|
296
296
|
const headers = new Headers()
|
|
297
297
|
for (const [name, value] of c.req.raw.headers)
|
|
298
298
|
if (!strippedHeaders.has(name.toLowerCase())) headers.set(name, value)
|
|
299
|
+
if (zoneHeaders) for (const [name, value] of new Headers(zoneHeaders)) headers.set(name, value)
|
|
299
300
|
if (basicAuth) headers.set('authorization', `Basic ${btoa(basicAuth)}`)
|
|
300
301
|
else if (bearerAuth) headers.set('authorization', `Bearer ${bearerAuth}`)
|
|
301
302
|
|
|
@@ -368,6 +368,7 @@ async function passthrough(
|
|
|
368
368
|
basicAuth,
|
|
369
369
|
bearerAuth,
|
|
370
370
|
publicZoneUrl,
|
|
371
|
+
zoneHeaders,
|
|
371
372
|
url: resolvedUrl,
|
|
372
373
|
} = Viem.resolveRpc(upstream_rpc, { chainId, principal, zone })
|
|
373
374
|
const usePublicZoneUrl = Boolean(zone && zoneToken)
|
|
@@ -385,6 +386,7 @@ async function passthrough(
|
|
|
385
386
|
const headers = new Headers()
|
|
386
387
|
for (const [name, value] of c.req.raw.headers)
|
|
387
388
|
if (!strippedHeaders.has(name.toLowerCase())) headers.set(name, value)
|
|
389
|
+
if (zoneHeaders) for (const [name, value] of new Headers(zoneHeaders)) headers.set(name, value)
|
|
388
390
|
// Public Zone endpoints authenticate with the caller's token, never internal RPC credentials.
|
|
389
391
|
if (!usePublicZoneUrl && basicAuth) headers.set('authorization', `Basic ${btoa(basicAuth)}`)
|
|
390
392
|
else if (!usePublicZoneUrl && bearerAuth) headers.set('authorization', `Bearer ${bearerAuth}`)
|
|
@@ -274,7 +274,6 @@ describe('GET /tokens?verified=true', () => {
|
|
|
274
274
|
describe('GET /tokens?addresses=', () => {
|
|
275
275
|
test('separates repeated addresses in the response cache', async () => {
|
|
276
276
|
const routed = TestApp.create({ db })
|
|
277
|
-
const headers = { authorization: `Bearer ${TestApp.key.token}` }
|
|
278
277
|
const repeated = new URLSearchParams()
|
|
279
278
|
repeated.append('addresses', TestApp.token)
|
|
280
279
|
repeated.append('addresses', TestApp.tokenWithHolders)
|
|
@@ -282,9 +281,9 @@ describe('GET /tokens?addresses=', () => {
|
|
|
282
281
|
addresses: TestApp.tokenWithHolders,
|
|
283
282
|
})
|
|
284
283
|
|
|
285
|
-
const primed = await routed.request(`/v1/tokens?${repeated}
|
|
286
|
-
const miss = await routed.request(`/v1/tokens?${single}
|
|
287
|
-
const hit = await routed.request(`/v1/tokens?${single}
|
|
284
|
+
const primed = await routed.request(`/v1/tokens?${repeated}`)
|
|
285
|
+
const miss = await routed.request(`/v1/tokens?${single}`)
|
|
286
|
+
const hit = await routed.request(`/v1/tokens?${single}`)
|
|
288
287
|
const primedBody = await TestApp.json(primed, Tokens.schema.getTokens.Response)
|
|
289
288
|
const body = await TestApp.json(hit, Tokens.schema.getTokens.Response)
|
|
290
289
|
|
|
@@ -302,7 +301,6 @@ describe('GET /tokens?addresses=', () => {
|
|
|
302
301
|
|
|
303
302
|
test('separates repeated includes in the response cache', async () => {
|
|
304
303
|
const routed = TestApp.create({ db })
|
|
305
|
-
const headers = { authorization: `Bearer ${TestApp.key.token}` }
|
|
306
304
|
const repeated = new URLSearchParams({ limit: '5' })
|
|
307
305
|
repeated.append('include', 'createdAt')
|
|
308
306
|
repeated.append('include', 'holderCount')
|
|
@@ -311,9 +309,9 @@ describe('GET /tokens?addresses=', () => {
|
|
|
311
309
|
limit: '5',
|
|
312
310
|
})
|
|
313
311
|
|
|
314
|
-
const primed = await routed.request(`/v1/tokens?${repeated}
|
|
315
|
-
const miss = await routed.request(`/v1/tokens?${single}
|
|
316
|
-
const hit = await routed.request(`/v1/tokens?${single}
|
|
312
|
+
const primed = await routed.request(`/v1/tokens?${repeated}`)
|
|
313
|
+
const miss = await routed.request(`/v1/tokens?${single}`)
|
|
314
|
+
const hit = await routed.request(`/v1/tokens?${single}`)
|
|
317
315
|
const primedBody = await TestApp.json(primed, Tokens.schema.getTokens.Response)
|
|
318
316
|
const body = await TestApp.json(hit, Tokens.schema.getTokens.Response)
|
|
319
317
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Context } from 'hono'
|
|
2
|
+
|
|
3
|
+
import type * as App from '../../App.js'
|
|
4
|
+
import type * as FundingTransfers from '../../db/tables/fundingTransfers.js'
|
|
5
|
+
import * as Auth from '../../internal/Auth.js'
|
|
6
|
+
import * as Response from '../../internal/Response.js'
|
|
7
|
+
|
|
8
|
+
/** Returns the funding owner encoded by an API-key principal. */
|
|
9
|
+
export function owner(c: Context<App.Environment>): FundingTransfers.Owner | null {
|
|
10
|
+
const principal = Auth.getPrincipal(c)
|
|
11
|
+
if (principal?.type !== 'api_key') return null
|
|
12
|
+
return {
|
|
13
|
+
environment: principal.environment,
|
|
14
|
+
orgId: principal.orgId,
|
|
15
|
+
projectId: principal.projectId,
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Returns the error used when a funding resource has no API-key owner. */
|
|
20
|
+
export function unauthorized(c: Context<App.Environment>) {
|
|
21
|
+
return Response.error(c, {
|
|
22
|
+
code: 'unauthorized',
|
|
23
|
+
message: 'Tempo could not determine which organization owns this funding resource.',
|
|
24
|
+
status: 401,
|
|
25
|
+
})
|
|
26
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { App } from 'tapimo'
|
|
2
|
+
|
|
3
|
+
import * as Store from '../../internal/Store.js'
|
|
4
|
+
import * as Viem from '../../internal/Viem.js'
|
|
5
|
+
import * as TestApp from '../../../test/App.js'
|
|
6
|
+
import { funding } from './App.js'
|
|
7
|
+
|
|
8
|
+
const sandbox = {
|
|
9
|
+
environment: 'sandbox',
|
|
10
|
+
id: 'key_sandbox_funding',
|
|
11
|
+
orgId: 'org_test',
|
|
12
|
+
scopes: ['data:read'],
|
|
13
|
+
token: 'secret_sandbox_funding',
|
|
14
|
+
} satisfies TestApp.kvStore.Key
|
|
15
|
+
|
|
16
|
+
function as(key: { token: string }) {
|
|
17
|
+
return { headers: { authorization: `Bearer ${key.token}` } } as const
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('funding access', () => {
|
|
21
|
+
test('rejects sandbox keys on a mainnet-default deployment', async () => {
|
|
22
|
+
const app = App.create({
|
|
23
|
+
auth: {},
|
|
24
|
+
db: TestApp.database(),
|
|
25
|
+
defaultChainId: Viem.chainId.mainnet,
|
|
26
|
+
kv: { store: TestApp.kvStore({ keys: [sandbox] }) },
|
|
27
|
+
}).route('/', funding())
|
|
28
|
+
|
|
29
|
+
const response = await app.request('/v1/funding/chains', as(sandbox))
|
|
30
|
+
|
|
31
|
+
expect(response.status).toBe(403)
|
|
32
|
+
expect(((await response.json()) as { error: { code: string } }).error.code).toBe(
|
|
33
|
+
'api_key_forbidden',
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('requires a matching Zone scope and bypasses the edge cache', async () => {
|
|
38
|
+
const chainId = 421_700_001
|
|
39
|
+
const reader = {
|
|
40
|
+
id: 'key_zone_funding',
|
|
41
|
+
orgId: 'org_test',
|
|
42
|
+
scopes: ['data:read', `zone:${chainId}:read`],
|
|
43
|
+
token: 'secret_zone_funding',
|
|
44
|
+
} satisfies TestApp.kvStore.Key
|
|
45
|
+
const app = App.create({
|
|
46
|
+
auth: {},
|
|
47
|
+
cache: { store: Store.memory() },
|
|
48
|
+
db: TestApp.database(),
|
|
49
|
+
defaultChainId: chainId,
|
|
50
|
+
kv: { store: TestApp.kvStore({ keys: [reader] }) },
|
|
51
|
+
zones: [TestApp.zone({ chainId, rpcUrl: `https://${chainId}.rpc.test` })],
|
|
52
|
+
}).route('/', funding())
|
|
53
|
+
|
|
54
|
+
const allowed = await app.request('/v1/funding/chains', as(reader))
|
|
55
|
+
const anonymous = await app.request('/v1/funding/chains')
|
|
56
|
+
|
|
57
|
+
expect(allowed.status).toBe(200)
|
|
58
|
+
expect(allowed.headers.get('cache-control')).toContain('private')
|
|
59
|
+
expect(anonymous.status).toBe(403)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('keeps public-chain inventory edge-cacheable on a Zone deployment', async () => {
|
|
63
|
+
const chainId = 421_700_001
|
|
64
|
+
const app = App.create({
|
|
65
|
+
auth: false,
|
|
66
|
+
cache: { store: Store.memory() },
|
|
67
|
+
db: TestApp.database(),
|
|
68
|
+
zones: [TestApp.zone({ chainId, rpcUrl: `https://${chainId}.rpc.test` })],
|
|
69
|
+
}).route('/', funding())
|
|
70
|
+
|
|
71
|
+
const first = await app.request('/v1/funding/chains')
|
|
72
|
+
const second = await app.request('/v1/funding/chains')
|
|
73
|
+
|
|
74
|
+
expect(first.status).toBe(200)
|
|
75
|
+
expect(second.status).toBe(200)
|
|
76
|
+
expect(second.headers.get('cache-control')).toContain('public')
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Hono } from 'hono'
|
|
2
|
+
|
|
3
|
+
import type * as App from '../../App.js'
|
|
4
|
+
import type * as FundingDepositAddress from '../../internal/funding/DepositAddress.js'
|
|
5
|
+
import type * as FundingProvider from '../../internal/funding/Provider.js'
|
|
6
|
+
import * as Metadata from '../metadata.js'
|
|
7
|
+
import { chains } from './routes/chains.js'
|
|
8
|
+
import { depositAddresses } from './routes/deposit-addresses.js'
|
|
9
|
+
import { providers } from './routes/providers.js'
|
|
10
|
+
import { quotes } from './routes/quotes.js'
|
|
11
|
+
import { transfers } from './routes/transfers.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mounts the Funding & Bridge API, including quotes, inventory, reusable
|
|
15
|
+
* deposit addresses, detected deposits, and transfers.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* App.create(options).route('/', data()).route('/', funding())
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function funding(options: funding.Options = {}) {
|
|
22
|
+
const app = new Hono<App.Environment>()
|
|
23
|
+
.route('/', quotes())
|
|
24
|
+
.route('/', chains())
|
|
25
|
+
.route('/', providers())
|
|
26
|
+
.route('/', depositAddresses(options))
|
|
27
|
+
.route('/', transfers())
|
|
28
|
+
return Metadata.defaultChain(Metadata.attach(app, openapi))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export declare namespace funding {
|
|
32
|
+
/** Inbound funding route configuration. */
|
|
33
|
+
type Options = {
|
|
34
|
+
/** Enqueues one deposit address for asynchronous reconciliation. */
|
|
35
|
+
dispatchReconciliation?:
|
|
36
|
+
| ((message: FundingProvider.Webhook.receive.Dispatchable) => Promise<void>)
|
|
37
|
+
| undefined
|
|
38
|
+
/** Registers a created address with a source-chain observer. */
|
|
39
|
+
registerDepositAddress?: ((address: FundingDepositAddress.Public) => Promise<void>) | undefined
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const openapi = {
|
|
44
|
+
tags: [
|
|
45
|
+
{
|
|
46
|
+
name: 'Chains',
|
|
47
|
+
description: 'Source chains and stablecoins supported for funding.',
|
|
48
|
+
'x-pagePath': 'funding/chains',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Deposit Addresses',
|
|
52
|
+
description: 'Reusable funding addresses and deposits detected at those addresses.',
|
|
53
|
+
'x-pagePath': 'funding/deposit-addresses',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Providers',
|
|
57
|
+
description: 'Providers available for inbound funding.',
|
|
58
|
+
'x-pagePath': 'funding/providers',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Quotes',
|
|
62
|
+
description: 'Live quotes for funding Tempo from other chains.',
|
|
63
|
+
'x-pagePath': 'funding/quotes',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Funding Transfers',
|
|
67
|
+
description: 'Inbound funding transfers.',
|
|
68
|
+
'x-displayName': 'Transfers',
|
|
69
|
+
'x-pagePath': 'funding/transfers',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
'x-tagGroups': [
|
|
73
|
+
{
|
|
74
|
+
name: 'Funding & Bridge API',
|
|
75
|
+
tags: ['Chains', 'Deposit Addresses', 'Providers', 'Quotes', 'Funding Transfers'],
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
} satisfies App.create.Metadata
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Context } from 'hono'
|
|
2
|
+
|
|
3
|
+
import type * as App from '../../App.js'
|
|
4
|
+
import type * as Db from '../../db/Db.js'
|
|
5
|
+
import * as FundingProvider from '../../internal/funding/Provider.js'
|
|
6
|
+
|
|
7
|
+
const sourceScopes = new WeakMap<Db.Source, number>()
|
|
8
|
+
let nextSourceScope = 0
|
|
9
|
+
|
|
10
|
+
/** Returns an inventory cache key scoped by request URL, providers, and database source. */
|
|
11
|
+
export function key(c: Context<App.Environment>) {
|
|
12
|
+
const url = new URL(c.req.url)
|
|
13
|
+
const providers = c
|
|
14
|
+
.get('providers')
|
|
15
|
+
.filter(FundingProvider.is)
|
|
16
|
+
.map((provider) => provider.id)
|
|
17
|
+
.sort()
|
|
18
|
+
url.searchParams.set('__tempo_database', String(sourceScope(c.get('dbCached'))))
|
|
19
|
+
url.searchParams.set('__tempo_providers', providers.join(','))
|
|
20
|
+
return url.toString()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sourceScope(source: Db.Source) {
|
|
24
|
+
const current = sourceScopes.get(source)
|
|
25
|
+
if (current !== undefined) return current
|
|
26
|
+
// A production isolate has one source, while multi-app runtimes receive distinct scopes.
|
|
27
|
+
const scope = nextSourceScope++
|
|
28
|
+
sourceScopes.set(source, scope)
|
|
29
|
+
return scope
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as FundingProvider from '../../internal/funding/Provider.js'
|
|
2
|
+
import type * as Log from '../../internal/Log.js'
|
|
3
|
+
|
|
4
|
+
/** Formats one provider attempt for the canonical request log. */
|
|
5
|
+
export function format(attempt: FundingProvider.ProviderAttempt): Log.FundingProviderAttempt {
|
|
6
|
+
const { id } = attempt.provider
|
|
7
|
+
if (attempt.outcome !== 'failed')
|
|
8
|
+
return {
|
|
9
|
+
durationMs: attempt.durationMs,
|
|
10
|
+
id,
|
|
11
|
+
operation: attempt.operation,
|
|
12
|
+
outcome: attempt.outcome,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
durationMs: attempt.durationMs,
|
|
17
|
+
failure: FundingProvider.failure({
|
|
18
|
+
cause: attempt.cause,
|
|
19
|
+
operation: attempt.operation,
|
|
20
|
+
provider: attempt.provider,
|
|
21
|
+
}).failure,
|
|
22
|
+
id,
|
|
23
|
+
operation: attempt.operation,
|
|
24
|
+
outcome: attempt.outcome,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { funding } from './App.js'
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as Across from '../../../internal/funding/providers/across.js'
|
|
2
|
+
|
|
3
|
+
import * as Relay from '../../../internal/funding/providers/relay.js'
|
|
4
|
+
|
|
5
|
+
import * as Rhino from '../../../internal/funding/providers/rhino.js'
|
|
6
|
+
|
|
7
|
+
import * as Squid from '../../../internal/funding/providers/squid.js'
|
|
8
|
+
|
|
9
|
+
import * as Symbiosis from '../../../internal/funding/providers/symbiosis.js'
|
|
10
|
+
|
|
11
|
+
import * as TestApp from '../../../../test/App.js'
|
|
12
|
+
|
|
13
|
+
import * as TestFunding from '../../../../test/Funding.js'
|
|
14
|
+
|
|
15
|
+
import * as Funding from './chains.js'
|
|
16
|
+
|
|
17
|
+
const db = TestApp.database()
|
|
18
|
+
|
|
19
|
+
const providers = [
|
|
20
|
+
Across.across({
|
|
21
|
+
apiKey: 'across-api-key',
|
|
22
|
+
integratorId: '0xdead',
|
|
23
|
+
userAddress: '0x1111111111111111111111111111111111111111',
|
|
24
|
+
}),
|
|
25
|
+
Relay.relay({ userAddress: '0x1111111111111111111111111111111111111111' }),
|
|
26
|
+
Rhino.rhino(),
|
|
27
|
+
Squid.squid({
|
|
28
|
+
integratorId: 'squid-integrator-id',
|
|
29
|
+
userAddress: '0x1111111111111111111111111111111111111111',
|
|
30
|
+
}),
|
|
31
|
+
Symbiosis.symbiosis({ userAddress: '0x1111111111111111111111111111111111111111' }),
|
|
32
|
+
] as const
|
|
33
|
+
|
|
34
|
+
beforeAll(() => TestFunding.publish(db))
|
|
35
|
+
|
|
36
|
+
function create(options: TestApp.create.Options = {}) {
|
|
37
|
+
return TestApp.create({ ...options, db })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('funding OpenAPI', () => {
|
|
41
|
+
test('publishes get operation ids for funding inventory', async () => {
|
|
42
|
+
const app = create({ auth: false })
|
|
43
|
+
const spec = await (await app.request('/openapi.json')).json()
|
|
44
|
+
|
|
45
|
+
expect({
|
|
46
|
+
chains: spec.paths['/v1/funding/chains'].get.operationId,
|
|
47
|
+
providers: spec.paths['/v1/funding/providers'].get.operationId,
|
|
48
|
+
}).toEqual({
|
|
49
|
+
chains: 'getFundingChains',
|
|
50
|
+
providers: 'getFundingProviders',
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('GET /funding/chains', () => {
|
|
56
|
+
test('lists only inputs supported by quote providers', async () => {
|
|
57
|
+
const response = await create({ providers }).request('/v1/funding/chains', {
|
|
58
|
+
headers: TestApp.auth.headers,
|
|
59
|
+
})
|
|
60
|
+
expect(response.status).toBe(200)
|
|
61
|
+
const body = await TestApp.json(response, Funding.schema.getFundingChains.Response)
|
|
62
|
+
|
|
63
|
+
expect(body.data.map((chain) => chain.id)).toEqual([
|
|
64
|
+
'eip155:1',
|
|
65
|
+
'eip155:56',
|
|
66
|
+
'eip155:8453',
|
|
67
|
+
'eip155:137',
|
|
68
|
+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
69
|
+
'tron:0x2b6653dc',
|
|
70
|
+
])
|
|
71
|
+
expect(body.data.flatMap((chain) => chain.tokens.map((token) => token.symbol))).toEqual(
|
|
72
|
+
expect.arrayContaining(['USDC', 'USDT']),
|
|
73
|
+
)
|
|
74
|
+
expect(
|
|
75
|
+
body.data.flatMap((chain) => chain.tokens).every((token) => token.currency === 'USD'),
|
|
76
|
+
).toBe(true)
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Hono } from 'hono'
|
|
2
|
+
import * as z from 'zod/mini'
|
|
3
|
+
|
|
4
|
+
import type * as App from '../../../App.js'
|
|
5
|
+
import * as Auth from '../../../internal/Auth.js'
|
|
6
|
+
import * as Cache from '../../../internal/Cache.js'
|
|
7
|
+
import * as FundingCatalog from '../../../internal/funding/Catalog.js'
|
|
8
|
+
import * as FundingProvider from '../../../internal/funding/Provider.js'
|
|
9
|
+
import * as OpenApi from '../../../internal/OpenApi.js'
|
|
10
|
+
import * as Response from '../../../internal/Response.js'
|
|
11
|
+
import * as Schema from '../../../internal/Schema.js'
|
|
12
|
+
import * as Inventory from '../Inventory.js'
|
|
13
|
+
|
|
14
|
+
/** Zod schemas owned by funding chain resources. */
|
|
15
|
+
export namespace schema {
|
|
16
|
+
/** Source chain reference. */
|
|
17
|
+
export const Chain = FundingProvider.schema.ChainRef
|
|
18
|
+
|
|
19
|
+
/** Source token reference. */
|
|
20
|
+
export const Token = FundingProvider.schema.TokenRef
|
|
21
|
+
|
|
22
|
+
/** Chain inventory entry returned by the funding chains endpoint. */
|
|
23
|
+
export const ChainInventory = Schema.describe(
|
|
24
|
+
z.extend(Chain, {
|
|
25
|
+
tokens: z.array(Token).check(z.describe('Stablecoins known on this source chain.')),
|
|
26
|
+
}),
|
|
27
|
+
'One source chain and its known stablecoin inventory.',
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
/** Schemas for the getFundingChains operation. */
|
|
31
|
+
export namespace getFundingChains {
|
|
32
|
+
/** Non-paginated source-chain inventory. */
|
|
33
|
+
export const Response = Schema.describe(
|
|
34
|
+
z.object({
|
|
35
|
+
data: z
|
|
36
|
+
.array(ChainInventory)
|
|
37
|
+
.check(z.describe('Source chains and stablecoins supported by funding quotes.')),
|
|
38
|
+
}),
|
|
39
|
+
'A non-paginated list of source chains and stablecoin inventory.',
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const baseChainExample = {
|
|
45
|
+
addressFormat: 'hex',
|
|
46
|
+
id: 'eip155:8453',
|
|
47
|
+
kind: 'evm',
|
|
48
|
+
name: 'Base',
|
|
49
|
+
} as const
|
|
50
|
+
|
|
51
|
+
const baseUsdcExample = {
|
|
52
|
+
address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
|
|
53
|
+
currency: 'USD',
|
|
54
|
+
decimals: 6,
|
|
55
|
+
name: 'USD Coin',
|
|
56
|
+
standard: 'ERC-20',
|
|
57
|
+
symbol: 'USDC',
|
|
58
|
+
tokenKey: 'eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
|
|
59
|
+
verified: true,
|
|
60
|
+
} as const
|
|
61
|
+
|
|
62
|
+
const fundingChainsResponseExample = {
|
|
63
|
+
data: [{ ...baseChainExample, tokens: [baseUsdcExample] }],
|
|
64
|
+
} satisfies z.output<typeof schema.getFundingChains.Response>
|
|
65
|
+
|
|
66
|
+
/** Mounts funding chain inventory routes. */
|
|
67
|
+
export function chains() {
|
|
68
|
+
return new Hono<App.Environment>().get(
|
|
69
|
+
'/v1/funding/chains',
|
|
70
|
+
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
71
|
+
OpenApi.describeRoute({
|
|
72
|
+
description: 'Lists supported source chains and tokens.',
|
|
73
|
+
operationId: 'getFundingChains',
|
|
74
|
+
responses: OpenApi.responses({
|
|
75
|
+
success: {
|
|
76
|
+
description: 'Supported source chains and stablecoins.',
|
|
77
|
+
example: fundingChainsResponseExample,
|
|
78
|
+
schema: schema.getFundingChains.Response,
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
summary: 'Get chains',
|
|
82
|
+
tags: ['Chains'],
|
|
83
|
+
}),
|
|
84
|
+
Cache.response<App.Environment>({
|
|
85
|
+
cacheControl: Cache.policies.stable,
|
|
86
|
+
key: Inventory.key,
|
|
87
|
+
name: 'tempo-api:funding-chains:v5',
|
|
88
|
+
}),
|
|
89
|
+
async (c) => {
|
|
90
|
+
if (Auth.narrowAccess) return Auth.paidAccessError(c)
|
|
91
|
+
const catalog = await FundingCatalog.snapshot(c)
|
|
92
|
+
const providers = c.get('providers').filter(FundingProvider.is)
|
|
93
|
+
return c.json(
|
|
94
|
+
Response.validated(schema.getFundingChains.Response, {
|
|
95
|
+
data: FundingProvider.getChains({ catalog, providers }),
|
|
96
|
+
}),
|
|
97
|
+
200,
|
|
98
|
+
)
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
}
|