tapimo 0.5.3 → 0.7.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/dist/App.d.ts +15830 -7220
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +9 -1
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +4 -0
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +5 -0
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +1011 -412
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +4 -0
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +2 -1
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/early-access.d.ts +148 -0
- package/dist/admin/apps/early-access.d.ts.map +1 -0
- package/dist/admin/apps/early-access.js +133 -0
- package/dist/admin/apps/early-access.js.map +1 -0
- package/dist/admin/apps/organizations.d.ts +124 -1
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +97 -6
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +526 -1
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +214 -1
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +95 -75
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +20 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +18 -0
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +36 -7
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +6260 -4924
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +11 -6
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.js +194 -16
- package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +71 -0
- package/dist/apps/data/FxOracle.d.ts.map +1 -0
- package/dist/apps/data/FxOracle.js +97 -0
- package/dist/apps/data/FxOracle.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/activities.d.ts +3599 -1382
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +250 -88
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +872 -251
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/addresses.js +11 -9
- package/dist/apps/data/routes/addresses.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +77 -26
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +88 -45
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +39 -39
- package/dist/apps/data/routes/coingecko.d.ts +54 -54
- package/dist/apps/data/routes/exchanges.d.ts +856 -105
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +518 -76
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +81 -67
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +76 -91
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/funding.d.ts +80 -36
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +65 -31
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +392 -315
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +152 -77
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +167 -174
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +15 -13
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +277 -536
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +96 -77
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +157 -81
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +272 -130
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +307 -0
- package/dist/apps/data/routes/valuation.d.ts.map +1 -0
- package/dist/apps/data/routes/valuation.js +292 -0
- package/dist/apps/data/routes/valuation.js.map +1 -0
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/verified-tokens.js +7 -5
- package/dist/apps/data/routes/verified-tokens.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +103 -123
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +124 -25
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/index.d.ts +2 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +2 -1
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +8554 -1692
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +12 -1
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +7 -0
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +17 -3
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +157 -162
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +31 -23
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +232 -0
- package/dist/apps/management/routes/faucet.d.ts.map +1 -0
- package/dist/apps/management/routes/faucet.js +154 -0
- package/dist/apps/management/routes/faucet.js.map +1 -0
- package/dist/apps/management/routes/invitations.d.ts +60 -60
- package/dist/apps/management/routes/invite-links.d.ts +49 -20
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +26 -1
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +6 -6
- package/dist/apps/management/routes/members.d.ts +6 -6
- package/dist/apps/management/routes/orgs.d.ts +42 -42
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +5 -0
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +44 -44
- package/dist/apps/management/routes/usage.d.ts +3 -0
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +4 -0
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
- package/dist/apps/management/routes/verified-token-requests.js +271 -0
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
- package/dist/apps/management/routes/webhooks.d.ts +6977 -0
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
- package/dist/apps/management/routes/webhooks.js +573 -0
- package/dist/apps/management/routes/webhooks.js.map +1 -0
- package/dist/apps/mpp/App.d.ts +363 -0
- package/dist/apps/mpp/App.d.ts.map +1 -0
- package/dist/apps/mpp/App.js +72 -0
- package/dist/apps/mpp/App.js.map +1 -0
- package/dist/apps/mpp/Mpp.d.ts +178 -0
- package/dist/apps/mpp/Mpp.d.ts.map +1 -0
- package/dist/apps/mpp/Mpp.js +183 -0
- package/dist/apps/mpp/Mpp.js.map +1 -0
- package/dist/apps/mpp/index.d.ts +3 -0
- package/dist/apps/mpp/index.d.ts.map +1 -0
- package/dist/apps/mpp/index.js +3 -0
- package/dist/apps/mpp/index.js.map +1 -0
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +56 -9
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/db/Db.d.ts +6 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +110 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +53 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +56 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earlyAccess.d.ts +50 -0
- package/dist/db/tables/earlyAccess.d.ts.map +1 -0
- package/dist/db/tables/earlyAccess.js +63 -0
- package/dist/db/tables/earlyAccess.js.map +1 -0
- package/dist/db/tables/enabledBillingSources.d.ts +73 -0
- package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
- package/dist/db/tables/enabledBillingSources.js +69 -0
- package/dist/db/tables/enabledBillingSources.js.map +1 -0
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/invitations.js +3 -0
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/inviteLinks.d.ts +10 -2
- package/dist/db/tables/inviteLinks.d.ts.map +1 -1
- package/dist/db/tables/inviteLinks.js +10 -1
- package/dist/db/tables/inviteLinks.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +5 -2
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +13 -2
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +2 -0
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
- package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
- package/dist/db/tables/verifiedTokenRequests.js +120 -0
- package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
- package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +16 -0
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/handlers/internal/utils.d.ts +1 -16
- package/dist/handlers/internal/utils.d.ts.map +1 -1
- package/dist/handlers/internal/utils.js +17 -6
- package/dist/handlers/internal/utils.js.map +1 -1
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +37 -5
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +11 -2
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +14 -47
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/FundingQuotes.d.ts +147 -47
- package/dist/internal/FundingQuotes.d.ts.map +1 -1
- package/dist/internal/FundingQuotes.js +119 -33
- package/dist/internal/FundingQuotes.js.map +1 -1
- package/dist/internal/Log.d.ts +37 -5
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +52 -3
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +24 -0
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +2 -2
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +3 -0
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +2 -2
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +12 -4
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +14 -5
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +23 -5
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Value.d.ts +18 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js +11 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/VerifiedTokens.d.ts.map +1 -1
- package/dist/internal/VerifiedTokens.js +10 -1
- package/dist/internal/VerifiedTokens.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +24 -3
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +34 -8
- package/dist/internal/Webhooks.js.map +1 -1
- package/package.json +7 -2
- package/src/App.test.ts +9 -4
- package/src/App.ts +23 -3
- package/src/Client.test-d.ts +733 -20
- package/src/Scope.ts +5 -0
- package/src/admin/App.test.ts +4 -0
- package/src/admin/App.ts +4 -0
- package/src/admin/apps/early-access.test.ts +125 -0
- package/src/admin/apps/early-access.ts +176 -0
- package/src/admin/apps/organizations.test.ts +86 -2
- package/src/admin/apps/organizations.ts +155 -31
- package/src/admin/apps/scopes.test.ts +5 -0
- package/src/admin/apps/verified-tokens.test.ts +201 -0
- package/src/admin/apps/verified-tokens.ts +532 -286
- package/src/admin/ui/plugin.ts +2 -1
- package/src/admin/ui/src/lib/early-access.ts +63 -0
- package/src/admin/ui/src/lib/organizations.test.ts +81 -1
- package/src/admin/ui/src/lib/organizations.ts +92 -0
- package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
- package/src/admin/ui/src/routeTree.gen.ts +33 -2
- package/src/admin/ui/src/routes/__root.tsx +2 -1
- package/src/admin/ui/src/routes/api-keys.tsx +1 -1
- package/src/admin/ui/src/routes/early-access.tsx +162 -0
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
- package/src/admin/ui.generated.ts +95 -94
- package/src/analytics/Analytics.test.ts +83 -0
- package/src/analytics/Analytics.ts +20 -0
- package/src/analytics/tables/requestEvents.ts +60 -7
- package/src/apps/composition.test.ts +11 -2
- package/src/apps/data/App.test.ts +1 -1
- package/src/apps/data/App.ts +22 -14
- package/src/apps/data/FundingQuoteProviders.test.ts +297 -4
- package/src/apps/data/FundingQuoteProviders.ts +255 -29
- package/src/apps/data/FxOracle.test.ts +118 -0
- package/src/apps/data/FxOracle.ts +153 -0
- package/src/apps/data/index.ts +1 -0
- package/src/apps/data/routes/activities.test.ts +177 -3
- package/src/apps/data/routes/activities.ts +441 -132
- package/src/apps/data/routes/addresses.ts +19 -9
- package/src/apps/data/routes/balances.test.ts +148 -11
- package/src/apps/data/routes/balances.ts +140 -51
- package/src/apps/data/routes/exchanges.test.ts +544 -9
- package/src/apps/data/routes/exchanges.ts +690 -103
- package/src/apps/data/routes/fee-amm.test.ts +47 -23
- package/src/apps/data/routes/fee-amm.ts +98 -119
- package/src/apps/data/routes/funding.test.ts +113 -8
- package/src/apps/data/routes/funding.ts +77 -51
- package/src/apps/data/routes/receipts.test.ts +129 -23
- package/src/apps/data/routes/receipts.ts +205 -100
- package/src/apps/data/routes/tokens.test.ts +43 -0
- package/src/apps/data/routes/tokens.ts +28 -13
- package/src/apps/data/routes/transactions.test.ts +72 -19
- package/src/apps/data/routes/transactions.ts +162 -92
- package/src/apps/data/routes/transfers.test.ts +341 -31
- package/src/apps/data/routes/transfers.ts +358 -158
- package/src/apps/data/routes/valuation.test.ts +437 -0
- package/src/apps/data/routes/valuation.ts +423 -0
- package/src/apps/data/routes/verified-tokens.ts +14 -2
- package/src/apps/data/routes/webhooks.test.ts +56 -8
- package/src/apps/data/routes/webhooks.ts +144 -27
- package/src/apps/index.ts +2 -1
- package/src/apps/management/App.ts +13 -0
- package/src/apps/management/routes/api-keys.test.ts +14 -0
- package/src/apps/management/routes/api-keys.ts +27 -3
- package/src/apps/management/routes/billing.test.ts +152 -20
- package/src/apps/management/routes/billing.ts +34 -18
- package/src/apps/management/routes/faucet.test.ts +87 -0
- package/src/apps/management/routes/faucet.ts +201 -0
- package/src/apps/management/routes/invite-links.test.ts +117 -0
- package/src/apps/management/routes/invite-links.ts +43 -1
- package/src/apps/management/routes/me.test.ts +9 -99
- package/src/apps/management/routes/orgs.test.ts +45 -2
- package/src/apps/management/routes/orgs.ts +6 -0
- package/src/apps/management/routes/scopes.test.ts +1 -0
- package/src/apps/management/routes/usage.test.ts +6 -0
- package/src/apps/management/routes/usage.ts +7 -0
- package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
- package/src/apps/management/routes/verified-token-requests.ts +397 -0
- package/src/apps/management/routes/webhooks.test.ts +426 -0
- package/src/apps/management/routes/webhooks.ts +672 -0
- package/src/apps/mpp/App.ts +86 -0
- package/src/apps/mpp/Mpp.test.ts +193 -0
- package/src/apps/mpp/Mpp.ts +353 -0
- package/src/apps/mpp/index.ts +2 -0
- package/src/apps/mpp/internal/Adapter.ts +11 -0
- package/src/apps/mpp/internal/Error.ts +9 -0
- package/src/apps/mpp/internal/Idempotency.ts +7 -0
- package/src/apps/mpp/internal/Screening.ts +10 -0
- package/src/apps/relay/App.test.ts +88 -6
- package/src/apps/relay/App.ts +61 -9
- package/src/db/Db.test.ts +19 -0
- package/src/db/Db.ts +125 -0
- package/src/db/Schema.ts +68 -0
- package/src/db/tables/earlyAccess.ts +86 -0
- package/src/db/tables/enabledBillingSources.test.ts +303 -0
- package/src/db/tables/enabledBillingSources.ts +118 -0
- package/src/db/tables/invitations.ts +3 -0
- package/src/db/tables/inviteLinks.ts +22 -3
- package/src/db/tables/organizations.test.ts +103 -0
- package/src/db/tables/organizations.ts +18 -2
- package/src/db/tables/sponsoredTransactions.ts +4 -0
- package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
- package/src/db/tables/verifiedTokenRequests.ts +182 -0
- package/src/db/tables/webhookSubscriptions.ts +31 -0
- package/src/handlers/internal/utils.ts +22 -6
- package/src/handlers/relay.errors.test.ts +153 -0
- package/src/handlers/relay.ts +42 -7
- package/src/internal/Auth.test.ts +3 -3
- package/src/internal/Auth.ts +15 -47
- package/src/internal/FundingQuotes.test.ts +161 -6
- package/src/internal/FundingQuotes.ts +141 -39
- package/src/internal/Log.test-d.ts +21 -0
- package/src/internal/Log.test.ts +141 -0
- package/src/internal/Log.ts +93 -6
- package/src/internal/MetricSink.test.ts +215 -0
- package/src/internal/MetricSink.ts +24 -0
- package/src/internal/OpenApi.ts +3 -0
- package/src/internal/Response.test.ts +28 -0
- package/src/internal/Response.ts +12 -4
- package/src/internal/Schema.ts +50 -5
- package/src/internal/Value.test.ts +20 -0
- package/src/internal/Value.ts +24 -1
- package/src/internal/VerifiedTokens.ts +9 -1
- package/src/internal/Webhooks.test.ts +140 -0
- package/src/internal/Webhooks.ts +53 -9
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as z from 'zod/mini'
|
|
2
2
|
|
|
3
3
|
import * as FundingQuotes from '../../internal/FundingQuotes.js'
|
|
4
|
+
import * as Store from '../../internal/Store.js'
|
|
4
5
|
|
|
5
6
|
type Fetch = typeof globalThis.fetch
|
|
6
7
|
|
|
@@ -10,13 +11,23 @@ const defaultAcrossBaseUrl = 'https://app.across.to/api'
|
|
|
10
11
|
const defaultRelayBaseUrl = 'https://api.relay.link'
|
|
11
12
|
const defaultRhinoBaseUrl = 'https://api.rhino.fi/bridge'
|
|
12
13
|
const defaultSquidBaseUrl = 'https://v2.api.squidrouter.com/v2'
|
|
14
|
+
const defaultSymbiosisBaseUrl = 'https://api.symbiosis.finance/crosschain'
|
|
15
|
+
// Keep one upstream slot free for deployment smoke tests and operational probes.
|
|
16
|
+
const symbiosisRateLimit = 4
|
|
17
|
+
const symbiosisRateLimitWindowMs = 1_000
|
|
13
18
|
const providerOperations = {
|
|
14
19
|
across: 'swap/approval',
|
|
15
20
|
relay: 'quote/v2',
|
|
16
21
|
rhino: 'quote/bridge-swap/public',
|
|
17
22
|
squid: 'v2/route',
|
|
23
|
+
symbiosis: 'v2/quote',
|
|
18
24
|
} as const
|
|
19
25
|
|
|
26
|
+
/** Returns the stable upstream operation used by a funding quote provider. */
|
|
27
|
+
export function providerOperation(provider: FundingQuotes.ProviderId) {
|
|
28
|
+
return providerOperations[provider]
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
namespace schema {
|
|
21
32
|
const IntegerString = z.string().check(z.regex(/^\d+$/))
|
|
22
33
|
|
|
@@ -65,6 +76,30 @@ namespace schema {
|
|
|
65
76
|
}),
|
|
66
77
|
}),
|
|
67
78
|
})
|
|
79
|
+
|
|
80
|
+
export const SymbiosisToken = z.object({
|
|
81
|
+
address: z.string(),
|
|
82
|
+
chainId: z.number().check(z.int(), z.nonnegative()),
|
|
83
|
+
decimals: z.number().check(z.int(), z.nonnegative()),
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const SymbiosisTokenAmount = z.extend(SymbiosisToken, { amount: IntegerString })
|
|
87
|
+
|
|
88
|
+
export const SymbiosisQuote = z.object({
|
|
89
|
+
estimatedTime: z.optional(z.number().check(z.nonnegative())),
|
|
90
|
+
kind: z.union([z.literal('bridge'), z.literal('crosschain-swap'), z.literal('intent-swap')]),
|
|
91
|
+
routes: z
|
|
92
|
+
.array(
|
|
93
|
+
z.object({
|
|
94
|
+
tokens: z.array(SymbiosisToken).check(z.minLength(1)),
|
|
95
|
+
}),
|
|
96
|
+
)
|
|
97
|
+
.check(z.minLength(1)),
|
|
98
|
+
tokenAmountOut: SymbiosisTokenAmount,
|
|
99
|
+
tokenAmountOutMin: SymbiosisTokenAmount,
|
|
100
|
+
tx: z.object({ chainId: z.number().check(z.int(), z.nonnegative()) }),
|
|
101
|
+
type: z.union([z.literal('evm'), z.literal('tron')]),
|
|
102
|
+
})
|
|
68
103
|
}
|
|
69
104
|
|
|
70
105
|
/** Creates the live funding quote adapters configured for this deployment. */
|
|
@@ -74,6 +109,7 @@ export function createAdapters(options: createAdapters.Options = {}) {
|
|
|
74
109
|
...(options.relay ? { relay: relay(options.relay) } : {}),
|
|
75
110
|
...(options.rhino ? { rhino: rhino(options.rhino) } : {}),
|
|
76
111
|
...(options.squid ? { squid: squid(options.squid) } : {}),
|
|
112
|
+
...(options.symbiosis ? { symbiosis: symbiosis(options.symbiosis) } : {}),
|
|
77
113
|
} satisfies FundingQuotes.ProviderAdapters
|
|
78
114
|
}
|
|
79
115
|
|
|
@@ -88,6 +124,8 @@ export declare namespace createAdapters {
|
|
|
88
124
|
rhino?: rhino.Options | false | undefined
|
|
89
125
|
/** Squid quote adapter options. */
|
|
90
126
|
squid?: squid.Options | false | undefined
|
|
127
|
+
/** Symbiosis quote adapter options. */
|
|
128
|
+
symbiosis?: symbiosis.Options | false | undefined
|
|
91
129
|
}
|
|
92
130
|
}
|
|
93
131
|
|
|
@@ -137,6 +175,22 @@ export declare namespace configureSquid {
|
|
|
137
175
|
}
|
|
138
176
|
}
|
|
139
177
|
|
|
178
|
+
/** Resolves Symbiosis deployment configuration. */
|
|
179
|
+
export function configureSymbiosis(
|
|
180
|
+
options: configureSymbiosis.Options = {},
|
|
181
|
+
): symbiosis.Options | undefined {
|
|
182
|
+
if (!options.userAddress) return undefined
|
|
183
|
+
return { userAddress: options.userAddress }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export declare namespace configureSymbiosis {
|
|
187
|
+
/** Optional deployment binding required to enable Symbiosis. */
|
|
188
|
+
type Options = {
|
|
189
|
+
/** Symbiosis quote-user binding. */
|
|
190
|
+
userAddress?: string | undefined
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
140
194
|
/** Creates an Across quote adapter backed by `/swap/approval`. */
|
|
141
195
|
export function across(options: across.Options): FundingQuotes.ProviderAdapter {
|
|
142
196
|
assertAcrossConfiguration(options)
|
|
@@ -238,7 +292,7 @@ export function relay(options: relay.Options): FundingQuotes.ProviderAdapter {
|
|
|
238
292
|
method: 'POST',
|
|
239
293
|
signal,
|
|
240
294
|
})
|
|
241
|
-
return formatRelayResult(
|
|
295
|
+
return formatRelayResult(quote, now())
|
|
242
296
|
} catch (cause) {
|
|
243
297
|
const unavailable = providerUnavailable(cause, [
|
|
244
298
|
'NO_QUOTES',
|
|
@@ -397,9 +451,87 @@ export declare namespace squid {
|
|
|
397
451
|
}
|
|
398
452
|
}
|
|
399
453
|
|
|
454
|
+
/** Creates a Symbiosis quote adapter backed by `/v2/quote`. */
|
|
455
|
+
export function symbiosis(options: symbiosis.Options): FundingQuotes.ProviderAdapter {
|
|
456
|
+
assertUserAddress(options.userAddress)
|
|
457
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl ?? defaultSymbiosisBaseUrl)
|
|
458
|
+
const fetcher = options.fetch ?? defaultFetch
|
|
459
|
+
const now = options.now ?? (() => new Date())
|
|
460
|
+
const rateLimitStore = options.rateLimitStore ?? Store.memory()
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
async probeQuote({ candidate }, signal) {
|
|
464
|
+
const fromChainId = Number(providerChainId(candidate.sourceChain, 'symbiosis'))
|
|
465
|
+
const fromToken = symbiosisSourceToken(candidate.sourceChain, candidate.sourceToken)
|
|
466
|
+
const toChainId = Number(eip155ChainId(candidate.destinationChain))
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
await consumeSymbiosisQuota(rateLimitStore, now())
|
|
470
|
+
const quote = await requestJson(new URL(`${baseUrl}/v2/quote`), {
|
|
471
|
+
body: {
|
|
472
|
+
from: options.userAddress,
|
|
473
|
+
slippage: 100,
|
|
474
|
+
to: options.userAddress,
|
|
475
|
+
tokenAmountIn: {
|
|
476
|
+
address: fromToken,
|
|
477
|
+
amount: candidate.sourceAmount.amount,
|
|
478
|
+
chainId: fromChainId,
|
|
479
|
+
decimals: candidate.sourceToken.decimals,
|
|
480
|
+
symbol: candidate.sourceToken.symbol,
|
|
481
|
+
},
|
|
482
|
+
tokenOut: {
|
|
483
|
+
address: candidate.destinationToken.address,
|
|
484
|
+
chainId: toChainId,
|
|
485
|
+
decimals: candidate.destinationToken.decimals,
|
|
486
|
+
symbol: candidate.destinationToken.symbol,
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
fetcher,
|
|
490
|
+
headers: { 'x-partner-id': 'tempo' },
|
|
491
|
+
method: 'POST',
|
|
492
|
+
signal,
|
|
493
|
+
})
|
|
494
|
+
return formatSymbiosisResult(candidate, quote, now())
|
|
495
|
+
} catch (cause) {
|
|
496
|
+
const unavailable =
|
|
497
|
+
providerUnavailable(cause, [
|
|
498
|
+
'AMOUNT_TOO_LOW',
|
|
499
|
+
'NO_ROUTE',
|
|
500
|
+
'NO_ROUTE_FOUND',
|
|
501
|
+
'ROUTE_NOT_FOUND',
|
|
502
|
+
'UNSUPPORTED_ROUTE',
|
|
503
|
+
]) ?? symbiosisUnavailable(cause)
|
|
504
|
+
if (!unavailable) throw cause
|
|
505
|
+
return unavailableResult({
|
|
506
|
+
detail: 'symbiosis:quote-v2',
|
|
507
|
+
message: unavailable,
|
|
508
|
+
now: now(),
|
|
509
|
+
source: 'providerQuote',
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export declare namespace symbiosis {
|
|
517
|
+
/** Symbiosis quote adapter options. */
|
|
518
|
+
type Options = {
|
|
519
|
+
/** Override for tests or Symbiosis test environments. */
|
|
520
|
+
baseUrl?: string | undefined
|
|
521
|
+
/** Fetch implementation override for tests. */
|
|
522
|
+
fetch?: Fetch | undefined
|
|
523
|
+
/** Clock override for deterministic tests. */
|
|
524
|
+
now?: (() => Date) | undefined
|
|
525
|
+
/** Atomic store used to enforce the provider-wide quote quota. */
|
|
526
|
+
rateLimitStore?: Store.Store | undefined
|
|
527
|
+
/** Probe wallet address used as the Symbiosis sender and recipient. */
|
|
528
|
+
userAddress: string
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
400
532
|
/** Returns bounded provider failure details suitable for structured request logs. */
|
|
401
533
|
export function providerFailure(options: providerFailure.Options): providerFailure.Result {
|
|
402
|
-
const operation =
|
|
534
|
+
const operation = providerOperation(options.provider)
|
|
403
535
|
if (
|
|
404
536
|
options.cause instanceof FundingQuotes.ProviderAdapterTimeoutError ||
|
|
405
537
|
(options.cause instanceof DOMException && options.cause.name === 'AbortError')
|
|
@@ -417,6 +549,8 @@ export function providerFailure(options: providerFailure.Options): providerFailu
|
|
|
417
549
|
}
|
|
418
550
|
if (options.cause instanceof ProviderPayloadError)
|
|
419
551
|
return { failure: 'payload', id: options.provider, operation }
|
|
552
|
+
if (options.cause instanceof ProviderRateLimitError)
|
|
553
|
+
return { failure: 'rate_limit', id: options.provider, operation }
|
|
420
554
|
if (options.cause instanceof TypeError)
|
|
421
555
|
return { failure: 'network', id: options.provider, operation }
|
|
422
556
|
return { failure: 'unknown', id: options.provider, operation }
|
|
@@ -431,7 +565,7 @@ export declare namespace providerFailure {
|
|
|
431
565
|
/** Stable provider error code, when one is safe to retain. */
|
|
432
566
|
code?: string | undefined
|
|
433
567
|
/** Failure boundary that rejected the provider request. */
|
|
434
|
-
failure: 'http' | 'network' | 'payload' | 'timeout' | 'unknown'
|
|
568
|
+
failure: 'http' | 'network' | 'payload' | 'rate_limit' | 'timeout' | 'unknown'
|
|
435
569
|
/** Provider identifier. */
|
|
436
570
|
id: FundingQuotes.ProviderId
|
|
437
571
|
/** Provider operation identifier. */
|
|
@@ -466,10 +600,6 @@ function formatAcrossResult(
|
|
|
466
600
|
expiresAt: expiresAt.toISOString(),
|
|
467
601
|
minimumOutputAmount: response.minOutputAmount,
|
|
468
602
|
outputAmount: response.expectedOutputAmount,
|
|
469
|
-
outputAmountFormatted: decimalFromBaseUnits(
|
|
470
|
-
response.expectedOutputAmount,
|
|
471
|
-
candidate.destinationToken.decimals,
|
|
472
|
-
),
|
|
473
603
|
quality: {
|
|
474
604
|
estimatedSeconds: response.expectedFillTime,
|
|
475
605
|
liquiditySource: 'providerQuote',
|
|
@@ -481,11 +611,7 @@ function formatAcrossResult(
|
|
|
481
611
|
}
|
|
482
612
|
}
|
|
483
613
|
|
|
484
|
-
function formatRelayResult(
|
|
485
|
-
candidate: FundingQuotes.QuoteCandidate,
|
|
486
|
-
quote: unknown,
|
|
487
|
-
now: Date,
|
|
488
|
-
): FundingQuotes.QuoteProbeResult {
|
|
614
|
+
function formatRelayResult(quote: unknown, now: Date): FundingQuotes.QuoteProbeResult {
|
|
489
615
|
const details = record(record(quote)['details'])
|
|
490
616
|
const currencyOut = record(details['currencyOut'])
|
|
491
617
|
const outputAmount = decimalString(currencyOut['amount'])
|
|
@@ -496,9 +622,6 @@ function formatRelayResult(
|
|
|
496
622
|
return {
|
|
497
623
|
minimumOutputAmount: decimalString(currencyOut['minimumAmount']),
|
|
498
624
|
outputAmount,
|
|
499
|
-
outputAmountFormatted: outputAmount
|
|
500
|
-
? decimalFromBaseUnits(outputAmount, candidate.destinationToken.decimals)
|
|
501
|
-
: undefined,
|
|
502
625
|
quality: {
|
|
503
626
|
estimatedSeconds: nonnegativeInteger(details['timeEstimate']),
|
|
504
627
|
liquiditySource: 'providerQuote',
|
|
@@ -535,7 +658,6 @@ function formatRhinoResult(
|
|
|
535
658
|
|
|
536
659
|
return {
|
|
537
660
|
outputAmount,
|
|
538
|
-
outputAmountFormatted: decimalFromBaseUnits(outputAmount, candidate.destinationToken.decimals),
|
|
539
661
|
quality: {
|
|
540
662
|
...(response.estimatedDuration === undefined || response.estimatedDuration === null
|
|
541
663
|
? {}
|
|
@@ -570,10 +692,6 @@ function formatSquidResult(
|
|
|
570
692
|
return {
|
|
571
693
|
minimumOutputAmount: response.toAmountMin,
|
|
572
694
|
outputAmount: response.toAmount,
|
|
573
|
-
outputAmountFormatted: decimalFromBaseUnits(
|
|
574
|
-
response.toAmount,
|
|
575
|
-
candidate.destinationToken.decimals,
|
|
576
|
-
),
|
|
577
695
|
quality: {
|
|
578
696
|
estimatedSeconds: Math.ceil(response.estimatedRouteDuration),
|
|
579
697
|
liquiditySource: 'providerQuote',
|
|
@@ -585,6 +703,64 @@ function formatSquidResult(
|
|
|
585
703
|
}
|
|
586
704
|
}
|
|
587
705
|
|
|
706
|
+
function formatSymbiosisResult(
|
|
707
|
+
candidate: FundingQuotes.QuoteCandidate,
|
|
708
|
+
quote: unknown,
|
|
709
|
+
now: Date,
|
|
710
|
+
): FundingQuotes.QuoteProbeResult {
|
|
711
|
+
const parsed = schema.SymbiosisQuote.safeParse(quote)
|
|
712
|
+
if (!parsed.success) throw new ProviderPayloadError()
|
|
713
|
+
const response = parsed.data
|
|
714
|
+
const sourceChainId = Number(providerChainId(candidate.sourceChain, 'symbiosis'))
|
|
715
|
+
const sourceToken = symbiosisSourceToken(candidate.sourceChain, candidate.sourceToken)
|
|
716
|
+
const firstToken = response.routes[0]!.tokens[0]!
|
|
717
|
+
const lastRoute = response.routes[response.routes.length - 1]!
|
|
718
|
+
const lastToken = lastRoute.tokens[lastRoute.tokens.length - 1]!
|
|
719
|
+
if (
|
|
720
|
+
response.tx.chainId !== sourceChainId ||
|
|
721
|
+
response.type !== candidate.sourceChain.kind ||
|
|
722
|
+
!matchesSymbiosisToken(firstToken, {
|
|
723
|
+
address: sourceToken,
|
|
724
|
+
chainId: sourceChainId,
|
|
725
|
+
decimals: candidate.sourceToken.decimals,
|
|
726
|
+
}) ||
|
|
727
|
+
!matchesSymbiosisToken(lastToken, {
|
|
728
|
+
address: candidate.destinationToken.address,
|
|
729
|
+
chainId: Number(eip155ChainId(candidate.destinationChain)),
|
|
730
|
+
decimals: candidate.destinationToken.decimals,
|
|
731
|
+
}) ||
|
|
732
|
+
!matchesSymbiosisToken(response.tokenAmountOut, {
|
|
733
|
+
address: candidate.destinationToken.address,
|
|
734
|
+
chainId: Number(eip155ChainId(candidate.destinationChain)),
|
|
735
|
+
decimals: candidate.destinationToken.decimals,
|
|
736
|
+
}) ||
|
|
737
|
+
!matchesSymbiosisToken(response.tokenAmountOutMin, {
|
|
738
|
+
address: candidate.destinationToken.address,
|
|
739
|
+
chainId: Number(eip155ChainId(candidate.destinationChain)),
|
|
740
|
+
decimals: candidate.destinationToken.decimals,
|
|
741
|
+
}) ||
|
|
742
|
+
BigInt(response.tokenAmountOut.amount) <= 0n ||
|
|
743
|
+
BigInt(response.tokenAmountOutMin.amount) <= 0n ||
|
|
744
|
+
BigInt(response.tokenAmountOutMin.amount) > BigInt(response.tokenAmountOut.amount)
|
|
745
|
+
)
|
|
746
|
+
throw new ProviderPayloadError()
|
|
747
|
+
|
|
748
|
+
return {
|
|
749
|
+
minimumOutputAmount: response.tokenAmountOutMin.amount,
|
|
750
|
+
outputAmount: response.tokenAmountOut.amount,
|
|
751
|
+
quality: {
|
|
752
|
+
...(response.estimatedTime === undefined
|
|
753
|
+
? {}
|
|
754
|
+
: { estimatedSeconds: Math.ceil(response.estimatedTime) }),
|
|
755
|
+
liquiditySource: 'providerQuote',
|
|
756
|
+
sourceDetail: 'symbiosis:quote-v2',
|
|
757
|
+
tier: 'liquid',
|
|
758
|
+
},
|
|
759
|
+
sampledAt: now.toISOString(),
|
|
760
|
+
status: 'available',
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
588
764
|
function unavailableResult(options: {
|
|
589
765
|
detail: string
|
|
590
766
|
message: string
|
|
@@ -664,6 +840,22 @@ function squidUnavailable(cause: unknown) {
|
|
|
664
840
|
return undefined
|
|
665
841
|
}
|
|
666
842
|
|
|
843
|
+
function symbiosisUnavailable(cause: unknown) {
|
|
844
|
+
if (!(cause instanceof ProviderResponseError) || cause.status !== 400) return undefined
|
|
845
|
+
const body = record(cause.body)
|
|
846
|
+
if (body['message'] === 'Amount is too low to cover fees') return 'AMOUNT_TOO_LOW'
|
|
847
|
+
return undefined
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
async function consumeSymbiosisQuota(store: Store.Store, now: Date) {
|
|
851
|
+
const time = now.getTime()
|
|
852
|
+
const bucket = Math.floor(time / symbiosisRateLimitWindowMs)
|
|
853
|
+
const count = await Store.increment(store, `funding:symbiosis:quote:${bucket}`, {
|
|
854
|
+
ttl: symbiosisRateLimitWindowMs * 2,
|
|
855
|
+
})
|
|
856
|
+
if (count > symbiosisRateLimit) throw new ProviderRateLimitError()
|
|
857
|
+
}
|
|
858
|
+
|
|
667
859
|
function providerChainId(
|
|
668
860
|
quoteChain: FundingQuotes.QuoteCandidate['sourceChain'],
|
|
669
861
|
provider: string,
|
|
@@ -694,6 +886,16 @@ function rhinoToken(token: FundingQuotes.TokenRef) {
|
|
|
694
886
|
return id
|
|
695
887
|
}
|
|
696
888
|
|
|
889
|
+
function symbiosisSourceToken(chain: FundingQuotes.ChainRef, token: FundingQuotes.TokenRef) {
|
|
890
|
+
if (chain.kind !== 'tron') return token.address
|
|
891
|
+
const tokens: Record<string, string> = {
|
|
892
|
+
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t: '0xa614f803b6fd780986a42c78ec9c7f77e6ded13c',
|
|
893
|
+
}
|
|
894
|
+
const id = tokens[token.address]
|
|
895
|
+
if (!id) throw new UnsupportedProviderTokenError()
|
|
896
|
+
return id
|
|
897
|
+
}
|
|
898
|
+
|
|
697
899
|
function matchesToken(
|
|
698
900
|
token: z.output<typeof schema.AcrossQuote>['inputToken'],
|
|
699
901
|
chain: FundingQuotes.ChainRef,
|
|
@@ -718,6 +920,29 @@ function matchesSquidToken(
|
|
|
718
920
|
)
|
|
719
921
|
}
|
|
720
922
|
|
|
923
|
+
function matchesSymbiosisToken(
|
|
924
|
+
token: z.output<typeof schema.SymbiosisToken>,
|
|
925
|
+
options: matchesSymbiosisToken.Options,
|
|
926
|
+
) {
|
|
927
|
+
return (
|
|
928
|
+
token.address.toLowerCase() === options.address.toLowerCase() &&
|
|
929
|
+
token.chainId === options.chainId &&
|
|
930
|
+
token.decimals === options.decimals
|
|
931
|
+
)
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
declare namespace matchesSymbiosisToken {
|
|
935
|
+
/** Expected provider token identity. */
|
|
936
|
+
type Options = {
|
|
937
|
+
/** Provider-specific token address. */
|
|
938
|
+
address: string
|
|
939
|
+
/** Provider-specific chain id. */
|
|
940
|
+
chainId: number
|
|
941
|
+
/** Token decimal precision. */
|
|
942
|
+
decimals: number
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
721
946
|
function eip155ChainId(chain: { id: string; kind: string }) {
|
|
722
947
|
if (chain.kind !== 'evm') throw new UnsupportedProviderChainError()
|
|
723
948
|
const match = /^eip155:(\d+)$/.exec(chain.id)
|
|
@@ -760,13 +985,6 @@ function nonnegativeInteger(input: unknown) {
|
|
|
760
985
|
return undefined
|
|
761
986
|
}
|
|
762
987
|
|
|
763
|
-
function decimalFromBaseUnits(baseUnits: string, decimals: number) {
|
|
764
|
-
const padded = baseUnits.padStart(decimals + 1, '0')
|
|
765
|
-
const whole = decimals === 0 ? padded : padded.slice(0, -decimals)
|
|
766
|
-
const fraction = decimals === 0 ? '' : padded.slice(-decimals).replace(/0+$/, '')
|
|
767
|
-
return fraction ? `${whole}.${fraction}` : whole
|
|
768
|
-
}
|
|
769
|
-
|
|
770
988
|
function baseUnitsFromDecimal(value: string, decimals: number) {
|
|
771
989
|
if (!/^\d+(\.\d+)?$/.test(value)) return undefined
|
|
772
990
|
const [whole, fraction = ''] = value.split('.')
|
|
@@ -785,8 +1003,12 @@ function assertAcrossConfiguration(options: across.Options) {
|
|
|
785
1003
|
}
|
|
786
1004
|
|
|
787
1005
|
function assertSquidConfiguration(options: squid.Options) {
|
|
788
|
-
if (options.integratorId.length === 0
|
|
789
|
-
|
|
1006
|
+
if (options.integratorId.length === 0) throw new ProviderConfigurationError()
|
|
1007
|
+
assertUserAddress(options.userAddress)
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function assertUserAddress(userAddress: string) {
|
|
1011
|
+
if (!/^0x[\da-fA-F]{40}$/.test(userAddress)) throw new ProviderConfigurationError()
|
|
790
1012
|
}
|
|
791
1013
|
|
|
792
1014
|
class ProviderConfigurationError extends Error {
|
|
@@ -797,6 +1019,10 @@ class ProviderPayloadError extends Error {
|
|
|
797
1019
|
override name = 'FundingQuoteProviders.ProviderPayloadError'
|
|
798
1020
|
}
|
|
799
1021
|
|
|
1022
|
+
class ProviderRateLimitError extends Error {
|
|
1023
|
+
override name = 'FundingQuoteProviders.ProviderRateLimitError'
|
|
1024
|
+
}
|
|
1025
|
+
|
|
800
1026
|
class ProviderResponseError extends Error {
|
|
801
1027
|
override name = 'FundingQuoteProviders.ProviderResponseError'
|
|
802
1028
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import * as FxOracle from './FxOracle.js'
|
|
2
|
+
|
|
3
|
+
const set = {
|
|
4
|
+
asOf: '2026-01-01T00:00:00.000Z',
|
|
5
|
+
base: 'EUR',
|
|
6
|
+
rates: { AUD: '1.6', USD: '1.25' },
|
|
7
|
+
} satisfies FxOracle.RateSet
|
|
8
|
+
|
|
9
|
+
function feedUrl(usd: string) {
|
|
10
|
+
const body = `<Envelope><Cube><Cube time="2026-01-01"><Cube currency="AUD" rate="1.6"/><Cube currency="USD" rate="${usd}"/></Cube></Cube></Envelope>`
|
|
11
|
+
return `data:application/xml,${encodeURIComponent(body)}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('from', () => {
|
|
15
|
+
test('applies the default ttl and preserves overrides', () => {
|
|
16
|
+
const oracle = FxOracle.from({ name: 'test', rates: async () => set })
|
|
17
|
+
expect(oracle.name).toBe('test')
|
|
18
|
+
expect(oracle.ttl).toMatchInlineSnapshot(`3600000`)
|
|
19
|
+
expect(FxOracle.from({ name: 'test', rates: async () => set, ttl: 5 }).ttl).toBe(5)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('resolves the definition rates', async () => {
|
|
23
|
+
const oracle = FxOracle.from({ name: 'test', rates: async () => set })
|
|
24
|
+
await expect(oracle.rates()).resolves.toEqual(set)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('rethrows rates failures as RatesError', async () => {
|
|
28
|
+
const oracle = FxOracle.from({
|
|
29
|
+
name: 'test',
|
|
30
|
+
rates: async () => {
|
|
31
|
+
throw new Error('boom')
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
const error = await oracle.rates().catch((error: unknown) => error)
|
|
35
|
+
expect(error).toBeInstanceOf(FxOracle.RatesError)
|
|
36
|
+
expect(error).toMatchInlineSnapshot(
|
|
37
|
+
`[FxOracle.RatesError: FX oracle "test" failed to produce rates]`,
|
|
38
|
+
)
|
|
39
|
+
expect((error as Error).cause).toMatchInlineSnapshot(`[Error: boom]`)
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('ecb', () => {
|
|
44
|
+
test('fetches and parses the daily reference rates', async () => {
|
|
45
|
+
const rates = await FxOracle.ecb({ url: feedUrl('1.25') }).rates()
|
|
46
|
+
expect(rates).toMatchInlineSnapshot(`
|
|
47
|
+
{
|
|
48
|
+
"asOf": "2026-01-01T00:00:00.000Z",
|
|
49
|
+
"base": "EUR",
|
|
50
|
+
"rates": {
|
|
51
|
+
"AUD": "1.6",
|
|
52
|
+
"USD": "1.25",
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
`)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test.each(['', '-1.25', '1..25'])('rejects malformed rate %j', async (value) => {
|
|
59
|
+
const error = await FxOracle.ecb({ url: feedUrl(value) })
|
|
60
|
+
.rates()
|
|
61
|
+
.catch((error: unknown) => error)
|
|
62
|
+
expect(error).toBeInstanceOf(FxOracle.RatesError)
|
|
63
|
+
expect((error as Error).cause).toMatchInlineSnapshot(
|
|
64
|
+
`[FxOracle.ParseError: Rate feed returned an unrecognized payload]`,
|
|
65
|
+
)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('rejects non-positive rates', async () => {
|
|
69
|
+
const error = await FxOracle.ecb({ url: feedUrl('0.0') })
|
|
70
|
+
.rates()
|
|
71
|
+
.catch((error: unknown) => error)
|
|
72
|
+
expect(error).toBeInstanceOf(FxOracle.RatesError)
|
|
73
|
+
expect((error as Error).cause).toMatchInlineSnapshot(
|
|
74
|
+
`[FxOracle.ParseError: Rate feed returned an unrecognized payload]`,
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('rejects rates below fixed-point precision', async () => {
|
|
79
|
+
const error = await FxOracle.ecb({ url: feedUrl('0.0000000000000000001') })
|
|
80
|
+
.rates()
|
|
81
|
+
.catch((error: unknown) => error)
|
|
82
|
+
expect(error).toBeInstanceOf(FxOracle.RatesError)
|
|
83
|
+
expect((error as Error).cause).toMatchInlineSnapshot(
|
|
84
|
+
`[FxOracle.ParseError: Rate feed returned an unrecognized payload]`,
|
|
85
|
+
)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('rejects with RatesError when the feed is unreachable', async () => {
|
|
89
|
+
const oracle = FxOracle.ecb({ url: 'http://127.0.0.1:1' })
|
|
90
|
+
const error = await oracle.rates().catch((error: unknown) => error)
|
|
91
|
+
expect(error).toBeInstanceOf(FxOracle.RatesError)
|
|
92
|
+
expect(error).toMatchInlineSnapshot(
|
|
93
|
+
`[FxOracle.RatesError: FX oracle "ecb" failed to produce rates]`,
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe('rate', () => {
|
|
99
|
+
test('derives cross rates through the base', () => {
|
|
100
|
+
expect(FxOracle.rate(set, { from: 'USD', to: 'AUD' })).toMatchInlineSnapshot(
|
|
101
|
+
`1280000000000000000n`,
|
|
102
|
+
)
|
|
103
|
+
expect(FxOracle.rate(set, { from: 'EUR', to: 'USD' })).toMatchInlineSnapshot(
|
|
104
|
+
`1250000000000000000n`,
|
|
105
|
+
)
|
|
106
|
+
expect(FxOracle.rate(set, { from: 'USD', to: 'EUR' })).toMatchInlineSnapshot(
|
|
107
|
+
`800000000000000000n`,
|
|
108
|
+
)
|
|
109
|
+
expect(FxOracle.rate(set, { from: 'USD', to: 'USD' })).toMatchInlineSnapshot(
|
|
110
|
+
`1000000000000000000n`,
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('returns undefined for currencies the set does not price', () => {
|
|
115
|
+
expect(FxOracle.rate(set, { from: 'BTC', to: 'USD' })).toBeUndefined()
|
|
116
|
+
expect(FxOracle.rate(set, { from: 'USD', to: 'XXX' })).toBeUndefined()
|
|
117
|
+
})
|
|
118
|
+
})
|