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
|
@@ -14,7 +14,9 @@ import * as Timing from '../../../internal/Timing.js';
|
|
|
14
14
|
import * as Ttl from '../../../internal/Ttl.js';
|
|
15
15
|
import * as Value from '../../../internal/Value.js';
|
|
16
16
|
import * as VerifiedTokens from '../../../internal/VerifiedTokens.js';
|
|
17
|
+
import * as FxOracle from '../FxOracle.js';
|
|
17
18
|
import * as Tokens from './tokens.js';
|
|
19
|
+
import * as Valuation from './valuation.js';
|
|
18
20
|
// TIP-20 tokens emit the standard ERC-20 Transfer event. Supplying the
|
|
19
21
|
// signature exposes a decoded `Transfer` CTE (with `from`/`to`/`value`) to
|
|
20
22
|
// the indexer SQL.
|
|
@@ -24,52 +26,44 @@ export const signature = 'event Transfer(address indexed from, address indexed t
|
|
|
24
26
|
// text memo is decoded into `memo`; an MPP attribution fingerprint memo is
|
|
25
27
|
// resolved into `attribution` instead.
|
|
26
28
|
const memoSignature = 'event TransferWithMemo(address indexed from, address indexed to, uint256 amount, bytes32 indexed memo)';
|
|
29
|
+
const tokenFields = ['token.logoUri', 'token.verified'];
|
|
27
30
|
/** Zod schemas owned by the transfer handlers. */
|
|
28
31
|
export var schema;
|
|
29
32
|
(function (schema) {
|
|
30
|
-
/**
|
|
31
|
-
* The token moved by a transfer: its contract address and the transferred
|
|
32
|
-
* amount, plus the trimmed token reference spread in when requested via
|
|
33
|
-
* `include=token`. Reference fields are absent on the base (no-`include`)
|
|
34
|
-
* response and best-effort when requested (a row whose metadata is
|
|
35
|
-
* unavailable keeps just `address`/`amount`). Shared by every per-row
|
|
36
|
-
* transfer response across the API.
|
|
37
|
-
*
|
|
38
|
-
* The optional metadata fields are inlined here (rather than spread from
|
|
39
|
-
* `Tokens.schema.Token` via `z.partial`) so this module avoids referencing
|
|
40
|
-
* the `tokens` schema namespace at module-init time — `transfers` and
|
|
41
|
-
* `tokens` import each other for runtime helpers (`Transfers.query`,
|
|
42
|
-
* `Tokens.resolveToken`), and the schema-evaluation back-edge would
|
|
43
|
-
* otherwise deadlock the namespace.
|
|
44
|
-
*/
|
|
33
|
+
/** A transfer token reference with RPC metadata. Inlined to avoid the `transfers`/`tokens` module cycle. */
|
|
45
34
|
schema.SourceToken = z
|
|
46
35
|
.object({
|
|
47
36
|
address: Schema.TokenAddress.check(z.describe('The TIP-20 token contract address on Tempo.')),
|
|
48
|
-
amount: z
|
|
49
|
-
.string()
|
|
50
|
-
.check(z.regex(/^\d+$/), z.describe('A non-negative whole number, given as a decimal string so very large token amounts keep full precision. Expressed in the smallest unit of the token.'), z.meta({ examples: ['1000000'] })),
|
|
51
37
|
currency: z
|
|
52
|
-
.
|
|
38
|
+
.string()
|
|
53
39
|
.check(z.describe('The currency label for this token, such as `USD` for USD-denominated stablecoins.'), z.meta({ examples: ['USD'] })),
|
|
54
40
|
decimals: z
|
|
55
|
-
.
|
|
41
|
+
.number()
|
|
42
|
+
.check(z.int(), z.nonnegative())
|
|
56
43
|
.check(z.describe('The number of decimal places the token uses; Tempo stablecoins typically use 6.'), z.meta({ examples: [6] })),
|
|
57
|
-
logoUri: z.optional(z.string()).check(z.describe('A URL for the token’s logo image,
|
|
44
|
+
logoUri: z.optional(z.string()).check(z.describe('A URL for the token’s logo image, returned with `include=token.logoUri`.'), z.meta({
|
|
58
45
|
examples: [
|
|
59
46
|
'https://api.tempo.xyz/data/4217/icons/0x20c0000000000000000000008f5425160ebe5525.svg',
|
|
60
47
|
],
|
|
61
48
|
})),
|
|
62
49
|
name: z
|
|
63
|
-
.
|
|
50
|
+
.string()
|
|
64
51
|
.check(z.describe('The token’s human-readable name.'), z.meta({ examples: ['USD Coin'] })),
|
|
65
52
|
symbol: z
|
|
66
|
-
.
|
|
53
|
+
.string()
|
|
67
54
|
.check(z.describe('The short ticker symbol wallets and apps show for this token.'), z.meta({ examples: ['USDC'] })),
|
|
68
55
|
verified: z
|
|
69
56
|
.optional(z.boolean())
|
|
70
|
-
.check(z.describe('Whether this token is in Tempo’s curated verified token
|
|
57
|
+
.check(z.describe('Whether this token is in Tempo’s curated verified list, returned with `include=token.verified`.'), z.meta({ examples: [true] })),
|
|
71
58
|
})
|
|
72
|
-
.check(z.describe('The token
|
|
59
|
+
.check(z.describe('The token that moved in this transfer, with RPC metadata and optional curated fields.'));
|
|
60
|
+
/** A token amount carrying its nominal value in the requested denomination. */
|
|
61
|
+
const ValuedAmount = z.extend(Schema.TokenAmount, {
|
|
62
|
+
valuation: z
|
|
63
|
+
.optional(z.nullable(Valuation.schema.Value))
|
|
64
|
+
.check(z.describe('The amount’s nominal value in the requested `denomination`. `null` when the token is ' +
|
|
65
|
+
'unverified, its display currency has no rate, or rates were unavailable.')),
|
|
66
|
+
});
|
|
73
67
|
/**
|
|
74
68
|
* Filter predicates shared verbatim by the `GET /transfers` list query
|
|
75
69
|
* ({@link getTransfers.Query}, which spreads `Predicates.shape`) and `transfer`
|
|
@@ -99,15 +93,14 @@ export var schema;
|
|
|
99
93
|
(function (getTransfers) {
|
|
100
94
|
/** Optional related resources that callers opt into via `include`. */
|
|
101
95
|
getTransfers.Include = z
|
|
102
|
-
.enum(['crossToken', 'memo',
|
|
96
|
+
.enum(['crossToken', 'memo', ...tokenFields, 'totalCount'])
|
|
103
97
|
.check(z.describe('Related resources to include only when you request them with `include`.'));
|
|
104
98
|
/**
|
|
105
99
|
* Parses a comma-separated `include` query value into a list of optional
|
|
106
|
-
* resources to embed. Extra lookups
|
|
107
|
-
*
|
|
108
|
-
* resource is requested, keeping the base transfer page fast.
|
|
100
|
+
* resources to embed. Extra lookups run only when their resource is
|
|
101
|
+
* requested, keeping the base transfer page fast.
|
|
109
102
|
*/
|
|
110
|
-
getTransfers.includeQuery = Schema.includeQuery(getTransfers.Include, 'Comma-separated related resources to include, such as `token,memo,crossToken,totalCount`.');
|
|
103
|
+
getTransfers.includeQuery = Schema.includeQuery(getTransfers.Include, 'Comma-separated related resources to include, such as `token.logoUri,token.verified,memo,crossToken,totalCount`.');
|
|
111
104
|
/**
|
|
112
105
|
* Query parameters for top-level transfer list requests. `address` is a
|
|
113
106
|
* convenience for "either side" — passing it alongside an explicit
|
|
@@ -127,6 +120,7 @@ export var schema;
|
|
|
127
120
|
'blockNumber.to': Schema.blockNumberBound('transfers', 'to'),
|
|
128
121
|
chainId: Schema.ChainIdQuery,
|
|
129
122
|
cursor: Schema.Cursor,
|
|
123
|
+
denomination: Schema.Denomination,
|
|
130
124
|
include: getTransfers.includeQuery,
|
|
131
125
|
limit: Schema.Limit,
|
|
132
126
|
order: Schema.Order,
|
|
@@ -152,9 +146,12 @@ export var schema;
|
|
|
152
146
|
blockNumber: z
|
|
153
147
|
.number()
|
|
154
148
|
.check(z.int(), z.nonnegative(), z.describe('The block number where this transfer was recorded.'), z.meta({ examples: [23456789] })),
|
|
149
|
+
destinationAmount: z
|
|
150
|
+
.optional(ValuedAmount)
|
|
151
|
+
.check(z.describe('Amount delivered to the recipient for a cross-token transfer. Omitted with `destinationToken` for a same-token transfer.')),
|
|
155
152
|
destinationToken: z
|
|
156
153
|
.optional(schema.SourceToken)
|
|
157
|
-
.check(z.describe('The token
|
|
154
|
+
.check(z.describe('The token delivered to the recipient when it differs from `sourceToken`. Omitted for same-token transfers and returned only with `include=crossToken`.')),
|
|
158
155
|
id: z.string().check(z.describe('A stable resource ID for this transfer, built from `${transactionHash}-${logIndex}`.'), z.meta({
|
|
159
156
|
examples: ['0x515801d7f9a5ac705e793e85904c9c69b3f1694b465cc8fb6ba3f0298dc82665-0'],
|
|
160
157
|
})),
|
|
@@ -166,30 +163,46 @@ export var schema;
|
|
|
166
163
|
.check(z.describe('The decoded text memo from the matching `TransferWithMemo` event, when one is present. Returned when you request `include=memo`.'), z.meta({ examples: ['Payment for invoice 123'] })),
|
|
167
164
|
recipient: Schema.Address.check(z.describe('The account address that received the tokens.')),
|
|
168
165
|
sender: Schema.Address.check(z.describe('The account address that sent the tokens.')),
|
|
169
|
-
|
|
166
|
+
sourceAmount: ValuedAmount.check(z.describe('Amount associated with `sourceToken`.')),
|
|
167
|
+
sourceToken: schema.SourceToken.check(z.describe('The token that moved.')),
|
|
170
168
|
timestamp: z.iso
|
|
171
169
|
.datetime()
|
|
172
170
|
.check(z.describe('The block timestamp when this transfer was recorded.'), z.meta({ examples: ['2024-01-01T00:00:00Z'] })),
|
|
173
171
|
transactionHash: Schema.Hash.check(z.describe('The hash of the transaction that emitted this transfer event.')),
|
|
174
172
|
})
|
|
175
|
-
.check(z.
|
|
173
|
+
.check(z.refine((data) => (data.destinationToken === undefined) === (data.destinationAmount === undefined), {
|
|
174
|
+
error: '`destinationAmount` and `destinationToken` must be returned together.',
|
|
175
|
+
path: ['destinationAmount'],
|
|
176
|
+
}), z.describe('One TIP-20 token transfer event, optionally including a payment memo for reconciliation or invoices.'));
|
|
177
|
+
/** Page-level resources: opt-in counts plus valuation rate provenance. */
|
|
178
|
+
getTransfers.Meta = z
|
|
179
|
+
.object({
|
|
180
|
+
pricing: z
|
|
181
|
+
.optional(Valuation.schema.Pricing)
|
|
182
|
+
.check(z.describe('Rate provenance for leg valuations. Present when conversion rates were ' +
|
|
183
|
+
'consulted; absent when every value was identity-valued or rates were unavailable.')),
|
|
184
|
+
totalCount: z.optional(Schema.TotalCount),
|
|
185
|
+
totalCountCapped: z.optional(Schema.TotalCountCapped),
|
|
186
|
+
})
|
|
187
|
+
.check(z.describe('Page-level resources attached to this response.'));
|
|
176
188
|
/** Page of TIP-20 token transfers across the chain, ordered by block then log index. */
|
|
177
189
|
getTransfers.Response = z
|
|
178
190
|
.object({
|
|
179
191
|
data: z.array(getTransfers.Transfer).check(z.describe('The transfers in this page.')),
|
|
180
192
|
// Response-wide `meta` carries the opt-in capped total count
|
|
181
|
-
// (`include=totalCount`); per-row embeds
|
|
182
|
-
// each `data` item, not here.
|
|
193
|
+
// (`include=totalCount`) and valuation rate provenance; per-row embeds
|
|
194
|
+
// (`memo`, `token`) attach to each `data` item, not here.
|
|
183
195
|
meta: z
|
|
184
|
-
.optional(
|
|
185
|
-
.check(z.describe('Page-level resources
|
|
196
|
+
.optional(getTransfers.Meta)
|
|
197
|
+
.check(z.describe('Page-level resources: opt-in counts and valuation rate provenance.')),
|
|
186
198
|
nextCursor: Schema.NextCursor,
|
|
187
199
|
})
|
|
188
200
|
.check(z.describe('A page of TIP-20 token transfer events across the chain.'));
|
|
189
201
|
})(getTransfers = schema.getTransfers || (schema.getTransfers = {}));
|
|
190
202
|
})(schema || (schema = {}));
|
|
191
203
|
/** Creates transfer handlers. */
|
|
192
|
-
export function transfers() {
|
|
204
|
+
export function transfers(options = {}) {
|
|
205
|
+
const oracle = options.fx?.oracle ?? FxOracle.ecb();
|
|
193
206
|
return new Hono().get('/v1/transfers', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('query', schema.getTransfers.Query, {
|
|
194
207
|
code: 'query_invalid',
|
|
195
208
|
message: 'Invalid query parameters',
|
|
@@ -197,7 +210,7 @@ export function transfers() {
|
|
|
197
210
|
description: 'List token transfer events across Tempo.',
|
|
198
211
|
operationId: 'getTransfers',
|
|
199
212
|
responses: OpenApi.responses({
|
|
200
|
-
errors: { 502: 'The
|
|
213
|
+
errors: { 502: 'The indexer or Tempo RPC could not serve this request.' },
|
|
201
214
|
success: {
|
|
202
215
|
description: 'A page of token transfer events.',
|
|
203
216
|
schema: schema.getTransfers.Response,
|
|
@@ -207,7 +220,7 @@ export function transfers() {
|
|
|
207
220
|
tags: ['Transfers'],
|
|
208
221
|
}), Cache.response({
|
|
209
222
|
cacheControl: Cache.policies.feed,
|
|
210
|
-
name: 'tempo-api:transfers:
|
|
223
|
+
name: 'tempo-api:transfers:v2',
|
|
211
224
|
key: (c) => Cache.urlKey(c, schema.getTransfers.Query),
|
|
212
225
|
}), async (c) => {
|
|
213
226
|
if (Auth.narrowAccess)
|
|
@@ -219,11 +232,10 @@ export function transfers() {
|
|
|
219
232
|
});
|
|
220
233
|
const options = c.req.valid('query');
|
|
221
234
|
const chainId = options.chainId ?? c.get('chainId');
|
|
222
|
-
|
|
223
|
-
//
|
|
224
|
-
// `feed` default.
|
|
235
|
+
const denomination = options.denomination ?? 'USD';
|
|
236
|
+
// Cursor pages carry current valuations and curated verification data.
|
|
225
237
|
if (options.cursor !== undefined)
|
|
226
|
-
Cache.setPolicy(c, Cache.policies.
|
|
238
|
+
Cache.setPolicy(c, Cache.policies.metadata);
|
|
227
239
|
// Filters shared by the page query and the opt-in total count, so the
|
|
228
240
|
// count always matches the page it annotates.
|
|
229
241
|
const predicates = {
|
|
@@ -257,11 +269,6 @@ export function transfers() {
|
|
|
257
269
|
page: options.page,
|
|
258
270
|
timing: 'transfers',
|
|
259
271
|
});
|
|
260
|
-
// Token metadata is opt-in. When requested, resolve each page token's
|
|
261
|
-
// metadata concurrently (sharing one verified-token fetch) and spread
|
|
262
|
-
// it into the row's `sourceToken`. Resolution is best-effort: a token
|
|
263
|
-
// whose metadata is unavailable keeps `sourceToken` as just
|
|
264
|
-
// `address`/`amount` on that row rather than failing the whole page.
|
|
265
272
|
let data = page.data.map((row) => ({
|
|
266
273
|
blockNumber: row.blockNumber,
|
|
267
274
|
id: `${row.transactionHash}-${row.logIndex}`,
|
|
@@ -315,55 +322,124 @@ export function transfers() {
|
|
|
315
322
|
return suppress.has(key) ? [] : [transfer];
|
|
316
323
|
});
|
|
317
324
|
}
|
|
318
|
-
|
|
319
|
-
|
|
325
|
+
// Verification is ancillary to the feed. Skip empty pages and degrade
|
|
326
|
+
// cold snapshot failures to null valuations.
|
|
327
|
+
const snapshot = data.length > 0
|
|
328
|
+
? await VerifiedTokens.snapshot(c, chainId).catch(() => undefined)
|
|
329
|
+
: undefined;
|
|
330
|
+
const includeLogoUri = options.include.includes('token.logoUri');
|
|
331
|
+
const includeVerified = options.include.includes('token.verified');
|
|
332
|
+
const uniqueTokens = Array.from(new Set(data.flatMap((transfer) => [
|
|
333
|
+
transfer.sourceToken.address,
|
|
334
|
+
...(transfer.destinationToken ? [transfer.destinationToken.address] : []),
|
|
335
|
+
])));
|
|
336
|
+
const tokensByAddress = new Map(await Timing.time(c, 'tokens', () => Promise.all(uniqueTokens.map(async (token) => {
|
|
337
|
+
const [logoUri, metadata] = await Promise.all([
|
|
338
|
+
includeLogoUri
|
|
339
|
+
? Timing.time(c, 'token_logo', () => Tokens.getTokenLogo(c, { address: token, chainId })).catch(() => undefined)
|
|
340
|
+
: undefined,
|
|
341
|
+
Timing.time(c, 'token_metadata', () => Tokens.getTokenMetadata(c, { address: token, chainId })),
|
|
342
|
+
]);
|
|
343
|
+
return [
|
|
344
|
+
token.toLowerCase(),
|
|
345
|
+
{
|
|
346
|
+
...metadata,
|
|
347
|
+
logoUri: includeLogoUri
|
|
348
|
+
? (logoUri ??
|
|
349
|
+
snapshot?.byAddress.get(token.toLowerCase())?.logoUri ??
|
|
350
|
+
metadata.logoUri)
|
|
351
|
+
: undefined,
|
|
352
|
+
},
|
|
353
|
+
];
|
|
354
|
+
}))));
|
|
355
|
+
// Value each leg via its curated display currency, in the requested
|
|
356
|
+
// denomination.
|
|
357
|
+
const rates = await Valuation.ratesFor(c, {
|
|
358
|
+
currencies: data.flatMap((transfer) => [
|
|
320
359
|
transfer.sourceToken.address,
|
|
321
360
|
...(transfer.destinationToken ? [transfer.destinationToken.address] : []),
|
|
322
|
-
])
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
361
|
+
].flatMap((address) => {
|
|
362
|
+
const held = snapshot?.byAddress.get(address.toLowerCase())?.currency;
|
|
363
|
+
return held === undefined ? [] : [held];
|
|
364
|
+
})),
|
|
365
|
+
denomination,
|
|
366
|
+
oracle,
|
|
367
|
+
});
|
|
368
|
+
const enrich = (leg) => {
|
|
369
|
+
const metadata = tokensByAddress.get(leg.address.toLowerCase());
|
|
370
|
+
if (!metadata)
|
|
371
|
+
throw new Error(`Token metadata unavailable for ${leg.address}`);
|
|
372
|
+
return {
|
|
373
|
+
amount: {
|
|
374
|
+
...Value.tokenAmount({
|
|
375
|
+
baseUnits: leg.amount,
|
|
376
|
+
currency: metadata.currency,
|
|
377
|
+
decimals: metadata.decimals,
|
|
378
|
+
}),
|
|
379
|
+
valuation: Valuation.valuationFor({
|
|
380
|
+
amount: BigInt(leg.amount),
|
|
381
|
+
denomination,
|
|
382
|
+
rates,
|
|
383
|
+
token: snapshot?.byAddress.get(leg.address.toLowerCase()),
|
|
384
|
+
}),
|
|
385
|
+
},
|
|
386
|
+
token: {
|
|
387
|
+
address: leg.address,
|
|
388
|
+
currency: metadata.currency,
|
|
389
|
+
decimals: metadata.decimals,
|
|
390
|
+
...(metadata.logoUri !== undefined ? { logoUri: metadata.logoUri } : {}),
|
|
391
|
+
name: metadata.name,
|
|
392
|
+
symbol: metadata.symbol,
|
|
393
|
+
...(includeVerified && snapshot
|
|
394
|
+
? { verified: snapshot.byAddress.has(leg.address.toLowerCase()) }
|
|
395
|
+
: {}),
|
|
396
|
+
},
|
|
335
397
|
};
|
|
336
|
-
|
|
398
|
+
};
|
|
399
|
+
const responseData = data.map(({ destinationToken, sourceToken, ...transfer }) => {
|
|
400
|
+
const source = enrich(sourceToken);
|
|
401
|
+
if (!destinationToken)
|
|
402
|
+
return {
|
|
403
|
+
...transfer,
|
|
404
|
+
sourceAmount: source.amount,
|
|
405
|
+
sourceToken: source.token,
|
|
406
|
+
};
|
|
407
|
+
const destination = enrich(destinationToken);
|
|
408
|
+
return {
|
|
337
409
|
...transfer,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
const
|
|
410
|
+
destinationAmount: destination.amount,
|
|
411
|
+
destinationToken: destination.token,
|
|
412
|
+
sourceAmount: source.amount,
|
|
413
|
+
sourceToken: source.token,
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
const counts = countPromise ? await countPromise : undefined;
|
|
417
|
+
const meta = {
|
|
418
|
+
...(rates ? { pricing: Valuation.pricing(rates, oracle) } : {}),
|
|
419
|
+
...counts,
|
|
420
|
+
};
|
|
345
421
|
return c.json(Response.validated(schema.getTransfers.Response, {
|
|
346
|
-
data,
|
|
347
|
-
...(meta ? { meta } : {}),
|
|
422
|
+
data: responseData,
|
|
423
|
+
...(Object.keys(meta).length > 0 ? { meta } : {}),
|
|
348
424
|
nextCursor: page.nextCursor,
|
|
349
425
|
}), 200);
|
|
350
426
|
}
|
|
351
427
|
catch (cause) {
|
|
428
|
+
if (cause instanceof Valuation.UnsupportedDenominationError)
|
|
429
|
+
return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 });
|
|
352
430
|
return Response.upstream(c, cause);
|
|
353
431
|
}
|
|
354
432
|
});
|
|
355
433
|
}
|
|
356
434
|
/**
|
|
357
435
|
* Shared TIP-20 `Transfer` page query. Used by `GET /transfers` and the
|
|
358
|
-
* webhook poller. Builds
|
|
436
|
+
* webhook poller. Builds signature-decoded `Transfer` CTE queries, applies
|
|
359
437
|
* opaque keyset pagination on `(block_num, log_idx)`, and decodes rows into a
|
|
360
438
|
* strongly-typed intermediate shape that each caller maps to its own public
|
|
361
439
|
* response.
|
|
362
440
|
*
|
|
363
|
-
* `eitherSide`
|
|
364
|
-
*
|
|
365
|
-
* because composing it with the keyset cursor pre-WHERE in SQL is awkward at
|
|
366
|
-
* the call site; the helper takes the knob directly.
|
|
441
|
+
* `eitherSide` is exposed alongside one-sided `sender`/`recipient` filters
|
|
442
|
+
* because it requires two upstream scans. The helper owns their ordered merge.
|
|
367
443
|
*
|
|
368
444
|
* Memoization lives inside the helper under one canonical `transfers:v1:…`
|
|
369
445
|
* key derived from every filter — so equivalent filter combinations share a
|
|
@@ -404,32 +480,48 @@ export async function scan(deps, options) {
|
|
|
404
480
|
// the head page, so it shares the head's SQL and cache entries; deeper pages
|
|
405
481
|
// translate to a SQL OFFSET of `(page - 1) * limit` rows.
|
|
406
482
|
const offset = options.page !== undefined && options.page > 1 ? (options.page - 1) * limit : undefined;
|
|
407
|
-
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
483
|
+
const address = options.eitherSide;
|
|
484
|
+
// Transfer events filtered by any combination of contract / sender /
|
|
485
|
+
// recipient / block / timestamp. The address shortcut is split below so
|
|
486
|
+
// each upstream query can use one indexed side.
|
|
411
487
|
const filters = filterClauses(options);
|
|
412
488
|
if (cursor !== undefined)
|
|
413
489
|
filters.push(Cursor.keyset([
|
|
414
490
|
{ literal: Cursor.literal(cursor[0], 'int'), name: 'block_num', order },
|
|
415
491
|
{ literal: Cursor.literal(cursor[1], 'int'), name: 'log_idx', order },
|
|
416
492
|
]));
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
493
|
+
const addressScoped = address !== undefined;
|
|
494
|
+
const window = addressScoped ? (offset ?? 0) + limit + 1 : limit + 1;
|
|
495
|
+
const fetchRows = async (side) => {
|
|
496
|
+
const sideFilters = side !== undefined && address !== undefined
|
|
497
|
+
? [`"${side}" = '${address}'`, ...filters]
|
|
498
|
+
: filters;
|
|
499
|
+
const where = sideFilters.length > 0 ? `WHERE ${sideFilters.join(' AND ')}` : '';
|
|
500
|
+
const result = await tidx.fetch({
|
|
501
|
+
chainId,
|
|
502
|
+
...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
|
|
503
|
+
query: `
|
|
504
|
+
SELECT "from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}, tx_hash, block_num, log_idx, block_timestamp
|
|
505
|
+
FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
|
|
506
|
+
${where}
|
|
507
|
+
ORDER BY block_num ${direction}, log_idx ${direction}
|
|
508
|
+
LIMIT ${window}${!addressScoped && offset !== undefined ? ` OFFSET ${offset}` : ''}
|
|
509
|
+
`,
|
|
510
|
+
});
|
|
511
|
+
return result.rows;
|
|
512
|
+
};
|
|
513
|
+
// TIDX cannot plan `(from = X OR to = X)` over the decoded Transfer table.
|
|
514
|
+
// Merge two bounded, server-filtered streams while preserving global order.
|
|
515
|
+
const rows = addressScoped
|
|
516
|
+
? mergeTransferRows((await Promise.all([fetchRows('from'), fetchRows('to')])).flat(), {
|
|
517
|
+
limit: limit + 1,
|
|
518
|
+
offset: offset ?? 0,
|
|
519
|
+
order,
|
|
520
|
+
})
|
|
521
|
+
: await fetchRows();
|
|
430
522
|
// The next page anchors below the last fetched row's `(block, log_idx)`.
|
|
431
523
|
const page = Cursor.paginate({
|
|
432
|
-
rows
|
|
524
|
+
rows,
|
|
433
525
|
limit,
|
|
434
526
|
key: (row) => {
|
|
435
527
|
const block = Value.toNumber(row['block_num']);
|
|
@@ -471,17 +563,37 @@ export async function scan(deps, options) {
|
|
|
471
563
|
}
|
|
472
564
|
return { hasMore: page.hasMore, items, limit, nextCursor: page.nextCursor };
|
|
473
565
|
}
|
|
566
|
+
/** Deduplicates and globally orders bounded `from`/`to` transfer streams. */
|
|
567
|
+
function mergeTransferRows(rows, options) {
|
|
568
|
+
const positioned = [];
|
|
569
|
+
const seen = new Set();
|
|
570
|
+
for (const row of rows) {
|
|
571
|
+
const blockNumber = Value.toNumber(row['block_num']);
|
|
572
|
+
const logIndex = Value.toNumber(row['log_idx']);
|
|
573
|
+
const transactionHash = Value.toText(row['tx_hash']);
|
|
574
|
+
if (blockNumber === undefined || logIndex === undefined || !transactionHash)
|
|
575
|
+
continue;
|
|
576
|
+
const key = eventKey(transactionHash, logIndex);
|
|
577
|
+
if (seen.has(key))
|
|
578
|
+
continue;
|
|
579
|
+
seen.add(key);
|
|
580
|
+
positioned.push({ blockNumber, logIndex, row });
|
|
581
|
+
}
|
|
582
|
+
positioned.sort((a, b) => {
|
|
583
|
+
const comparison = a.blockNumber - b.blockNumber || a.logIndex - b.logIndex;
|
|
584
|
+
return options.order === 'asc' ? comparison : -comparison;
|
|
585
|
+
});
|
|
586
|
+
return positioned.slice(options.offset, options.offset + options.limit).map(({ row }) => row);
|
|
587
|
+
}
|
|
474
588
|
/**
|
|
475
589
|
* Builds shared transfer filters. A token predicate targets `token_transfers`'s
|
|
476
590
|
* physical `token` column; unscoped queries omit it and use the `Transfer` CTE.
|
|
477
591
|
*/
|
|
478
592
|
function filterClauses(options) {
|
|
479
|
-
const {
|
|
593
|
+
const { fromBlock, fromTimestamp, recipient, sender, toBlock, toTimestamp, token } = options;
|
|
480
594
|
const filters = [];
|
|
481
595
|
if (token !== undefined)
|
|
482
596
|
filters.push(`token = '${token}'`);
|
|
483
|
-
if (eitherSide !== undefined)
|
|
484
|
-
filters.push(`("from" = '${eitherSide}' OR "to" = '${eitherSide}')`);
|
|
485
597
|
if (sender !== undefined)
|
|
486
598
|
filters.push(`"from" = '${sender}'`);
|
|
487
599
|
if (recipient !== undefined)
|
|
@@ -515,54 +627,84 @@ export function count(c, options) {
|
|
|
515
627
|
/**
|
|
516
628
|
* Context-free core of {@link count}: counts token-scoped rows from
|
|
517
629
|
* `token_transfers`, otherwise the decoded `Transfer` CTE. The capped inner
|
|
518
|
-
* scan bounds work
|
|
630
|
+
* scan bounds work while preserving each source's established query engine.
|
|
519
631
|
*/
|
|
520
632
|
export async function countScan(deps, options) {
|
|
521
633
|
const { chainId } = options;
|
|
522
634
|
const { tidx } = deps;
|
|
523
635
|
const filters = filterClauses(options);
|
|
524
636
|
const tokenScoped = options.token !== undefined;
|
|
525
|
-
const
|
|
637
|
+
const address = options.eitherSide;
|
|
526
638
|
// Folded count: cross-token transfers collapse several `Transfer` legs into
|
|
527
639
|
// one row, so a raw `count(*)` would overcount. Scan the capped `Transfer`
|
|
528
640
|
// rows, classify each transaction, and subtract the folded-away legs. Bounded
|
|
529
641
|
// by `countCap` like the raw count, so it stays within the indexer's budget.
|
|
530
642
|
if (options.crossToken) {
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
643
|
+
const fetchRows = async (side) => {
|
|
644
|
+
const sideFilters = side !== undefined && address !== undefined
|
|
645
|
+
? [`"${side}" = '${address}'`, ...filters]
|
|
646
|
+
: filters;
|
|
647
|
+
const sideWhere = sideFilters.length > 0 ? `WHERE ${sideFilters.join(' AND ')}` : '';
|
|
648
|
+
const result = await tidx.fetch({
|
|
649
|
+
chainId,
|
|
650
|
+
...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
|
|
651
|
+
query: `
|
|
652
|
+
SELECT tx_hash, block_num, log_idx, "from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}
|
|
653
|
+
FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
|
|
654
|
+
${sideWhere}
|
|
655
|
+
ORDER BY block_num, log_idx
|
|
656
|
+
LIMIT ${Schema.countCap}
|
|
657
|
+
`,
|
|
658
|
+
});
|
|
659
|
+
return result.rows;
|
|
660
|
+
};
|
|
661
|
+
const rows = address !== undefined
|
|
662
|
+
? mergeTransferRows((await Promise.all([fetchRows('from'), fetchRows('to')])).flat(), {
|
|
663
|
+
limit: Schema.countCap,
|
|
664
|
+
offset: 0,
|
|
665
|
+
order: 'asc',
|
|
666
|
+
})
|
|
667
|
+
: await fetchRows();
|
|
668
|
+
const rawCount = rows.length;
|
|
543
669
|
let suppressed = 0;
|
|
544
|
-
for (const legs of legsByTransaction(
|
|
670
|
+
for (const legs of legsByTransaction(rows).values()) {
|
|
545
671
|
const bundle = classifyCrossToken(legs);
|
|
546
672
|
if (bundle)
|
|
547
673
|
suppressed += bundle.suppressLogIndexes.length;
|
|
548
674
|
}
|
|
549
675
|
return { totalCountCapped: rawCount >= Schema.countCap, totalCount: rawCount - suppressed };
|
|
550
676
|
}
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
SELECT
|
|
559
|
-
FROM
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
677
|
+
const countRows = async (additionalFilters = []) => {
|
|
678
|
+
const countFilters = [...additionalFilters, ...filters];
|
|
679
|
+
const countWhere = countFilters.length > 0 ? `WHERE ${countFilters.join(' AND ')}` : '';
|
|
680
|
+
const result = await tidx.fetch({
|
|
681
|
+
chainId,
|
|
682
|
+
...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
|
|
683
|
+
query: `
|
|
684
|
+
SELECT count(*) AS total
|
|
685
|
+
FROM (
|
|
686
|
+
SELECT 1
|
|
687
|
+
FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
|
|
688
|
+
${countWhere}
|
|
689
|
+
LIMIT ${Schema.countCap}
|
|
690
|
+
) AS capped
|
|
691
|
+
`,
|
|
692
|
+
});
|
|
693
|
+
return Value.toNumber(result.rows[0]?.['total']) ?? 0;
|
|
694
|
+
};
|
|
695
|
+
if (address !== undefined) {
|
|
696
|
+
// Keep branches disjoint so self-transfers count once.
|
|
697
|
+
const [sent, received] = await Promise.all([
|
|
698
|
+
countRows([`"from" = '${address}'`]),
|
|
699
|
+
countRows([`"to" = '${address}'`, `"from" != '${address}'`]),
|
|
700
|
+
]);
|
|
701
|
+
const rawCount = sent + received;
|
|
702
|
+
return {
|
|
703
|
+
totalCount: Math.min(rawCount, Schema.countCap),
|
|
704
|
+
totalCountCapped: sent >= Schema.countCap || received >= Schema.countCap || rawCount >= Schema.countCap,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const totalCount = await countRows();
|
|
566
708
|
return { totalCountCapped: totalCount >= Schema.countCap, totalCount };
|
|
567
709
|
}
|
|
568
710
|
/** `'tx1', 'tx2', …` SQL `IN` list of the page's distinct transaction hashes. */
|