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
|
@@ -17,7 +17,9 @@ import * as Timing from '../../../internal/Timing.js'
|
|
|
17
17
|
import * as Ttl from '../../../internal/Ttl.js'
|
|
18
18
|
import * as Value from '../../../internal/Value.js'
|
|
19
19
|
import * as VerifiedTokens from '../../../internal/VerifiedTokens.js'
|
|
20
|
+
import * as FxOracle from '../FxOracle.js'
|
|
20
21
|
import * as Tokens from './tokens.js'
|
|
22
|
+
import * as Valuation from './valuation.js'
|
|
21
23
|
|
|
22
24
|
// TIP-20 tokens emit the standard ERC-20 Transfer event. Supplying the
|
|
23
25
|
// signature exposes a decoded `Transfer` CTE (with `from`/`to`/`value`) to
|
|
@@ -31,37 +33,16 @@ export const signature = 'event Transfer(address indexed from, address indexed t
|
|
|
31
33
|
const memoSignature =
|
|
32
34
|
'event TransferWithMemo(address indexed from, address indexed to, uint256 amount, bytes32 indexed memo)'
|
|
33
35
|
|
|
36
|
+
const tokenFields = ['token.logoUri', 'token.verified'] as const
|
|
37
|
+
|
|
34
38
|
/** Zod schemas owned by the transfer handlers. */
|
|
35
39
|
export namespace schema {
|
|
36
|
-
/**
|
|
37
|
-
* The token moved by a transfer: its contract address and the transferred
|
|
38
|
-
* amount, plus the trimmed token reference spread in when requested via
|
|
39
|
-
* `include=token`. Reference fields are absent on the base (no-`include`)
|
|
40
|
-
* response and best-effort when requested (a row whose metadata is
|
|
41
|
-
* unavailable keeps just `address`/`amount`). Shared by every per-row
|
|
42
|
-
* transfer response across the API.
|
|
43
|
-
*
|
|
44
|
-
* The optional metadata fields are inlined here (rather than spread from
|
|
45
|
-
* `Tokens.schema.Token` via `z.partial`) so this module avoids referencing
|
|
46
|
-
* the `tokens` schema namespace at module-init time — `transfers` and
|
|
47
|
-
* `tokens` import each other for runtime helpers (`Transfers.query`,
|
|
48
|
-
* `Tokens.resolveToken`), and the schema-evaluation back-edge would
|
|
49
|
-
* otherwise deadlock the namespace.
|
|
50
|
-
*/
|
|
40
|
+
/** A transfer token reference with RPC metadata. Inlined to avoid the `transfers`/`tokens` module cycle. */
|
|
51
41
|
export const SourceToken = z
|
|
52
42
|
.object({
|
|
53
43
|
address: Schema.TokenAddress.check(z.describe('The TIP-20 token contract address on Tempo.')),
|
|
54
|
-
amount: z
|
|
55
|
-
.string()
|
|
56
|
-
.check(
|
|
57
|
-
z.regex(/^\d+$/),
|
|
58
|
-
z.describe(
|
|
59
|
-
'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.',
|
|
60
|
-
),
|
|
61
|
-
z.meta({ examples: ['1000000'] }),
|
|
62
|
-
),
|
|
63
44
|
currency: z
|
|
64
|
-
.
|
|
45
|
+
.string()
|
|
65
46
|
.check(
|
|
66
47
|
z.describe(
|
|
67
48
|
'The currency label for this token, such as `USD` for USD-denominated stablecoins.',
|
|
@@ -69,7 +50,8 @@ export namespace schema {
|
|
|
69
50
|
z.meta({ examples: ['USD'] }),
|
|
70
51
|
),
|
|
71
52
|
decimals: z
|
|
72
|
-
.
|
|
53
|
+
.number()
|
|
54
|
+
.check(z.int(), z.nonnegative())
|
|
73
55
|
.check(
|
|
74
56
|
z.describe(
|
|
75
57
|
'The number of decimal places the token uses; Tempo stablecoins typically use 6.',
|
|
@@ -77,7 +59,7 @@ export namespace schema {
|
|
|
77
59
|
z.meta({ examples: [6] }),
|
|
78
60
|
),
|
|
79
61
|
logoUri: z.optional(z.string()).check(
|
|
80
|
-
z.describe('A URL for the token’s logo image,
|
|
62
|
+
z.describe('A URL for the token’s logo image, returned with `include=token.logoUri`.'),
|
|
81
63
|
z.meta({
|
|
82
64
|
examples: [
|
|
83
65
|
'https://api.tempo.xyz/data/4217/icons/0x20c0000000000000000000008f5425160ebe5525.svg',
|
|
@@ -85,10 +67,10 @@ export namespace schema {
|
|
|
85
67
|
}),
|
|
86
68
|
),
|
|
87
69
|
name: z
|
|
88
|
-
.
|
|
70
|
+
.string()
|
|
89
71
|
.check(z.describe('The token’s human-readable name.'), z.meta({ examples: ['USD Coin'] })),
|
|
90
72
|
symbol: z
|
|
91
|
-
.
|
|
73
|
+
.string()
|
|
92
74
|
.check(
|
|
93
75
|
z.describe('The short ticker symbol wallets and apps show for this token.'),
|
|
94
76
|
z.meta({ examples: ['USDC'] }),
|
|
@@ -96,16 +78,30 @@ export namespace schema {
|
|
|
96
78
|
verified: z
|
|
97
79
|
.optional(z.boolean())
|
|
98
80
|
.check(
|
|
99
|
-
z.describe(
|
|
81
|
+
z.describe(
|
|
82
|
+
'Whether this token is in Tempo’s curated verified list, returned with `include=token.verified`.',
|
|
83
|
+
),
|
|
100
84
|
z.meta({ examples: [true] }),
|
|
101
85
|
),
|
|
102
86
|
})
|
|
103
87
|
.check(
|
|
104
88
|
z.describe(
|
|
105
|
-
'The token
|
|
89
|
+
'The token that moved in this transfer, with RPC metadata and optional curated fields.',
|
|
106
90
|
),
|
|
107
91
|
)
|
|
108
92
|
|
|
93
|
+
/** A token amount carrying its nominal value in the requested denomination. */
|
|
94
|
+
const ValuedAmount = z.extend(Schema.TokenAmount, {
|
|
95
|
+
valuation: z
|
|
96
|
+
.optional(z.nullable(Valuation.schema.Value))
|
|
97
|
+
.check(
|
|
98
|
+
z.describe(
|
|
99
|
+
'The amount’s nominal value in the requested `denomination`. `null` when the token is ' +
|
|
100
|
+
'unverified, its display currency has no rate, or rates were unavailable.',
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
})
|
|
104
|
+
|
|
109
105
|
/**
|
|
110
106
|
* Filter predicates shared verbatim by the `GET /transfers` list query
|
|
111
107
|
* ({@link getTransfers.Query}, which spreads `Predicates.shape`) and `transfer`
|
|
@@ -137,18 +133,17 @@ export namespace schema {
|
|
|
137
133
|
export namespace getTransfers {
|
|
138
134
|
/** Optional related resources that callers opt into via `include`. */
|
|
139
135
|
export const Include = z
|
|
140
|
-
.enum(['crossToken', 'memo',
|
|
136
|
+
.enum(['crossToken', 'memo', ...tokenFields, 'totalCount'])
|
|
141
137
|
.check(z.describe('Related resources to include only when you request them with `include`.'))
|
|
142
138
|
|
|
143
139
|
/**
|
|
144
140
|
* Parses a comma-separated `include` query value into a list of optional
|
|
145
|
-
* resources to embed. Extra lookups
|
|
146
|
-
*
|
|
147
|
-
* resource is requested, keeping the base transfer page fast.
|
|
141
|
+
* resources to embed. Extra lookups run only when their resource is
|
|
142
|
+
* requested, keeping the base transfer page fast.
|
|
148
143
|
*/
|
|
149
144
|
export const includeQuery = Schema.includeQuery(
|
|
150
145
|
Include,
|
|
151
|
-
'Comma-separated related resources to include, such as `token,memo,crossToken,totalCount`.',
|
|
146
|
+
'Comma-separated related resources to include, such as `token.logoUri,token.verified,memo,crossToken,totalCount`.',
|
|
152
147
|
)
|
|
153
148
|
|
|
154
149
|
/**
|
|
@@ -174,6 +169,7 @@ export namespace schema {
|
|
|
174
169
|
'blockNumber.to': Schema.blockNumberBound('transfers', 'to'),
|
|
175
170
|
chainId: Schema.ChainIdQuery,
|
|
176
171
|
cursor: Schema.Cursor,
|
|
172
|
+
denomination: Schema.Denomination,
|
|
177
173
|
include: includeQuery,
|
|
178
174
|
limit: Schema.Limit,
|
|
179
175
|
order: Schema.Order,
|
|
@@ -214,11 +210,18 @@ export namespace schema {
|
|
|
214
210
|
z.describe('The block number where this transfer was recorded.'),
|
|
215
211
|
z.meta({ examples: [23456789] }),
|
|
216
212
|
),
|
|
213
|
+
destinationAmount: z
|
|
214
|
+
.optional(ValuedAmount)
|
|
215
|
+
.check(
|
|
216
|
+
z.describe(
|
|
217
|
+
'Amount delivered to the recipient for a cross-token transfer. Omitted with `destinationToken` for a same-token transfer.',
|
|
218
|
+
),
|
|
219
|
+
),
|
|
217
220
|
destinationToken: z
|
|
218
221
|
.optional(schema.SourceToken)
|
|
219
222
|
.check(
|
|
220
223
|
z.describe(
|
|
221
|
-
'The token
|
|
224
|
+
'The token delivered to the recipient when it differs from `sourceToken`. Omitted for same-token transfers and returned only with `include=crossToken`.',
|
|
222
225
|
),
|
|
223
226
|
),
|
|
224
227
|
id: z.string().check(
|
|
@@ -249,7 +252,8 @@ export namespace schema {
|
|
|
249
252
|
z.describe('The account address that received the tokens.'),
|
|
250
253
|
),
|
|
251
254
|
sender: Schema.Address.check(z.describe('The account address that sent the tokens.')),
|
|
252
|
-
|
|
255
|
+
sourceAmount: ValuedAmount.check(z.describe('Amount associated with `sourceToken`.')),
|
|
256
|
+
sourceToken: schema.SourceToken.check(z.describe('The token that moved.')),
|
|
253
257
|
timestamp: z.iso
|
|
254
258
|
.datetime()
|
|
255
259
|
.check(
|
|
@@ -261,29 +265,62 @@ export namespace schema {
|
|
|
261
265
|
),
|
|
262
266
|
})
|
|
263
267
|
.check(
|
|
268
|
+
z.refine(
|
|
269
|
+
(data) =>
|
|
270
|
+
(data.destinationToken === undefined) === (data.destinationAmount === undefined),
|
|
271
|
+
{
|
|
272
|
+
error: '`destinationAmount` and `destinationToken` must be returned together.',
|
|
273
|
+
path: ['destinationAmount'],
|
|
274
|
+
},
|
|
275
|
+
),
|
|
264
276
|
z.describe(
|
|
265
277
|
'One TIP-20 token transfer event, optionally including a payment memo for reconciliation or invoices.',
|
|
266
278
|
),
|
|
267
279
|
)
|
|
268
280
|
|
|
281
|
+
/** Page-level resources: opt-in counts plus valuation rate provenance. */
|
|
282
|
+
export const Meta = z
|
|
283
|
+
.object({
|
|
284
|
+
pricing: z
|
|
285
|
+
.optional(Valuation.schema.Pricing)
|
|
286
|
+
.check(
|
|
287
|
+
z.describe(
|
|
288
|
+
'Rate provenance for leg valuations. Present when conversion rates were ' +
|
|
289
|
+
'consulted; absent when every value was identity-valued or rates were unavailable.',
|
|
290
|
+
),
|
|
291
|
+
),
|
|
292
|
+
totalCount: z.optional(Schema.TotalCount),
|
|
293
|
+
totalCountCapped: z.optional(Schema.TotalCountCapped),
|
|
294
|
+
})
|
|
295
|
+
.check(z.describe('Page-level resources attached to this response.'))
|
|
296
|
+
|
|
269
297
|
/** Page of TIP-20 token transfers across the chain, ordered by block then log index. */
|
|
270
298
|
export const Response = z
|
|
271
299
|
.object({
|
|
272
300
|
data: z.array(Transfer).check(z.describe('The transfers in this page.')),
|
|
273
301
|
// Response-wide `meta` carries the opt-in capped total count
|
|
274
|
-
// (`include=totalCount`); per-row embeds
|
|
275
|
-
// each `data` item, not here.
|
|
302
|
+
// (`include=totalCount`) and valuation rate provenance; per-row embeds
|
|
303
|
+
// (`memo`, `token`) attach to each `data` item, not here.
|
|
276
304
|
meta: z
|
|
277
|
-
.optional(
|
|
278
|
-
.check(z.describe('Page-level resources
|
|
305
|
+
.optional(Meta)
|
|
306
|
+
.check(z.describe('Page-level resources: opt-in counts and valuation rate provenance.')),
|
|
279
307
|
nextCursor: Schema.NextCursor,
|
|
280
308
|
})
|
|
281
309
|
.check(z.describe('A page of TIP-20 token transfer events across the chain.'))
|
|
282
310
|
}
|
|
283
311
|
}
|
|
284
312
|
|
|
313
|
+
type UnenrichedTransfer = Omit<
|
|
314
|
+
z.output<typeof schema.getTransfers.Transfer>,
|
|
315
|
+
'destinationAmount' | 'destinationToken' | 'sourceAmount' | 'sourceToken'
|
|
316
|
+
> & {
|
|
317
|
+
destinationToken?: CrossToken | undefined
|
|
318
|
+
sourceToken: CrossToken
|
|
319
|
+
}
|
|
320
|
+
|
|
285
321
|
/** Creates transfer handlers. */
|
|
286
|
-
export function transfers() {
|
|
322
|
+
export function transfers(options: transfers.Options = {}) {
|
|
323
|
+
const oracle = options.fx?.oracle ?? FxOracle.ecb()
|
|
287
324
|
return new Hono<App.Environment>().get(
|
|
288
325
|
'/v1/transfers',
|
|
289
326
|
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
@@ -295,7 +332,7 @@ export function transfers() {
|
|
|
295
332
|
description: 'List token transfer events across Tempo.',
|
|
296
333
|
operationId: 'getTransfers',
|
|
297
334
|
responses: OpenApi.responses({
|
|
298
|
-
errors: { 502: 'The
|
|
335
|
+
errors: { 502: 'The indexer or Tempo RPC could not serve this request.' },
|
|
299
336
|
success: {
|
|
300
337
|
description: 'A page of token transfer events.',
|
|
301
338
|
schema: schema.getTransfers.Response,
|
|
@@ -306,7 +343,7 @@ export function transfers() {
|
|
|
306
343
|
}),
|
|
307
344
|
Cache.response<App.Environment>({
|
|
308
345
|
cacheControl: Cache.policies.feed,
|
|
309
|
-
name: 'tempo-api:transfers:
|
|
346
|
+
name: 'tempo-api:transfers:v2',
|
|
310
347
|
key: (c) => Cache.urlKey(c, schema.getTransfers.Query),
|
|
311
348
|
}),
|
|
312
349
|
async (c) => {
|
|
@@ -319,11 +356,10 @@ export function transfers() {
|
|
|
319
356
|
|
|
320
357
|
const options = c.req.valid('query')
|
|
321
358
|
const chainId = options.chainId ?? c.get('chainId')
|
|
359
|
+
const denomination = options.denomination ?? 'USD'
|
|
322
360
|
|
|
323
|
-
// Cursor pages
|
|
324
|
-
|
|
325
|
-
// `feed` default.
|
|
326
|
-
if (options.cursor !== undefined) Cache.setPolicy(c, Cache.policies.immutable)
|
|
361
|
+
// Cursor pages carry current valuations and curated verification data.
|
|
362
|
+
if (options.cursor !== undefined) Cache.setPolicy(c, Cache.policies.metadata)
|
|
327
363
|
|
|
328
364
|
// Filters shared by the page query and the opt-in total count, so the
|
|
329
365
|
// count always matches the page it annotates.
|
|
@@ -361,12 +397,7 @@ export function transfers() {
|
|
|
361
397
|
timing: 'transfers',
|
|
362
398
|
})
|
|
363
399
|
|
|
364
|
-
|
|
365
|
-
// metadata concurrently (sharing one verified-token fetch) and spread
|
|
366
|
-
// it into the row's `sourceToken`. Resolution is best-effort: a token
|
|
367
|
-
// whose metadata is unavailable keeps `sourceToken` as just
|
|
368
|
-
// `address`/`amount` on that row rather than failing the whole page.
|
|
369
|
-
let data: z.output<typeof schema.getTransfers.Transfer>[] = page.data.map((row) => ({
|
|
400
|
+
let data: UnenrichedTransfer[] = page.data.map((row) => ({
|
|
370
401
|
blockNumber: row.blockNumber,
|
|
371
402
|
id: `${row.transactionHash}-${row.logIndex}`,
|
|
372
403
|
logIndex: row.logIndex,
|
|
@@ -427,73 +458,157 @@ export function transfers() {
|
|
|
427
458
|
return suppress.has(key) ? [] : [transfer]
|
|
428
459
|
})
|
|
429
460
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
461
|
+
// Verification is ancillary to the feed. Skip empty pages and degrade
|
|
462
|
+
// cold snapshot failures to null valuations.
|
|
463
|
+
const snapshot =
|
|
464
|
+
data.length > 0
|
|
465
|
+
? await VerifiedTokens.snapshot(c, chainId).catch(() => undefined)
|
|
466
|
+
: undefined
|
|
467
|
+
const includeLogoUri = options.include.includes('token.logoUri')
|
|
468
|
+
const includeVerified = options.include.includes('token.verified')
|
|
469
|
+
const uniqueTokens = Array.from(
|
|
470
|
+
new Set(
|
|
471
|
+
data.flatMap((transfer) => [
|
|
472
|
+
transfer.sourceToken.address,
|
|
473
|
+
...(transfer.destinationToken ? [transfer.destinationToken.address] : []),
|
|
474
|
+
]),
|
|
475
|
+
),
|
|
476
|
+
)
|
|
477
|
+
const tokensByAddress = new Map(
|
|
478
|
+
await Timing.time(c, 'tokens', () =>
|
|
441
479
|
Promise.all(
|
|
442
|
-
uniqueTokens.map((token) =>
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
.
|
|
450
|
-
|
|
480
|
+
uniqueTokens.map(async (token) => {
|
|
481
|
+
const [logoUri, metadata] = await Promise.all([
|
|
482
|
+
includeLogoUri
|
|
483
|
+
? Timing.time(c, 'token_logo', () =>
|
|
484
|
+
Tokens.getTokenLogo(c, { address: token, chainId }),
|
|
485
|
+
).catch(() => undefined)
|
|
486
|
+
: undefined,
|
|
487
|
+
Timing.time(c, 'token_metadata', () =>
|
|
488
|
+
Tokens.getTokenMetadata(c, { address: token, chainId }),
|
|
489
|
+
),
|
|
490
|
+
])
|
|
491
|
+
return [
|
|
492
|
+
token.toLowerCase(),
|
|
493
|
+
{
|
|
494
|
+
...metadata,
|
|
495
|
+
logoUri: includeLogoUri
|
|
496
|
+
? (logoUri ??
|
|
497
|
+
snapshot?.byAddress.get(token.toLowerCase())?.logoUri ??
|
|
498
|
+
metadata.logoUri)
|
|
499
|
+
: undefined,
|
|
500
|
+
},
|
|
501
|
+
] as const
|
|
502
|
+
}),
|
|
451
503
|
),
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
504
|
+
),
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
// Value each leg via its curated display currency, in the requested
|
|
508
|
+
// denomination.
|
|
509
|
+
const rates = await Valuation.ratesFor(c, {
|
|
510
|
+
currencies: data.flatMap((transfer) =>
|
|
511
|
+
[
|
|
512
|
+
transfer.sourceToken.address,
|
|
513
|
+
...(transfer.destinationToken ? [transfer.destinationToken.address] : []),
|
|
514
|
+
].flatMap((address) => {
|
|
515
|
+
const held = snapshot?.byAddress.get(address.toLowerCase())?.currency
|
|
516
|
+
return held === undefined ? [] : [held]
|
|
517
|
+
}),
|
|
518
|
+
),
|
|
519
|
+
denomination,
|
|
520
|
+
oracle,
|
|
521
|
+
})
|
|
522
|
+
const enrich = (leg: CrossToken) => {
|
|
523
|
+
const metadata = tokensByAddress.get(leg.address.toLowerCase())
|
|
524
|
+
if (!metadata) throw new Error(`Token metadata unavailable for ${leg.address}`)
|
|
525
|
+
return {
|
|
526
|
+
amount: {
|
|
527
|
+
...Value.tokenAmount({
|
|
528
|
+
baseUnits: leg.amount,
|
|
529
|
+
currency: metadata.currency,
|
|
530
|
+
decimals: metadata.decimals,
|
|
531
|
+
}),
|
|
532
|
+
valuation: Valuation.valuationFor({
|
|
533
|
+
amount: BigInt(leg.amount),
|
|
534
|
+
denomination,
|
|
535
|
+
rates,
|
|
536
|
+
token: snapshot?.byAddress.get(leg.address.toLowerCase()),
|
|
537
|
+
}),
|
|
538
|
+
},
|
|
539
|
+
token: {
|
|
540
|
+
address: leg.address,
|
|
541
|
+
currency: metadata.currency,
|
|
542
|
+
decimals: metadata.decimals,
|
|
543
|
+
...(metadata.logoUri !== undefined ? { logoUri: metadata.logoUri } : {}),
|
|
544
|
+
name: metadata.name,
|
|
545
|
+
symbol: metadata.symbol,
|
|
546
|
+
...(includeVerified && snapshot
|
|
547
|
+
? { verified: snapshot.byAddress.has(leg.address.toLowerCase()) }
|
|
548
|
+
: {}),
|
|
549
|
+
},
|
|
459
550
|
}
|
|
460
|
-
data = data.map((transfer) => ({
|
|
461
|
-
...transfer,
|
|
462
|
-
sourceToken: withToken(transfer.sourceToken),
|
|
463
|
-
...(transfer.destinationToken
|
|
464
|
-
? { destinationToken: withToken(transfer.destinationToken) }
|
|
465
|
-
: {}),
|
|
466
|
-
}))
|
|
467
551
|
}
|
|
552
|
+
const responseData: z.output<typeof schema.getTransfers.Transfer>[] = data.map(
|
|
553
|
+
({ destinationToken, sourceToken, ...transfer }) => {
|
|
554
|
+
const source = enrich(sourceToken)
|
|
555
|
+
if (!destinationToken)
|
|
556
|
+
return {
|
|
557
|
+
...transfer,
|
|
558
|
+
sourceAmount: source.amount,
|
|
559
|
+
sourceToken: source.token,
|
|
560
|
+
}
|
|
561
|
+
const destination = enrich(destinationToken)
|
|
562
|
+
return {
|
|
563
|
+
...transfer,
|
|
564
|
+
destinationAmount: destination.amount,
|
|
565
|
+
destinationToken: destination.token,
|
|
566
|
+
sourceAmount: source.amount,
|
|
567
|
+
sourceToken: source.token,
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
)
|
|
468
571
|
|
|
469
|
-
const
|
|
572
|
+
const counts = countPromise ? await countPromise : undefined
|
|
573
|
+
const meta = {
|
|
574
|
+
...(rates ? { pricing: Valuation.pricing(rates, oracle) } : {}),
|
|
575
|
+
...counts,
|
|
576
|
+
}
|
|
470
577
|
|
|
471
578
|
return c.json(
|
|
472
579
|
Response.validated(schema.getTransfers.Response, {
|
|
473
|
-
data,
|
|
474
|
-
...(meta ? { meta } : {}),
|
|
580
|
+
data: responseData,
|
|
581
|
+
...(Object.keys(meta).length > 0 ? { meta } : {}),
|
|
475
582
|
nextCursor: page.nextCursor,
|
|
476
583
|
}),
|
|
477
584
|
200,
|
|
478
585
|
)
|
|
479
586
|
} catch (cause) {
|
|
587
|
+
if (cause instanceof Valuation.UnsupportedDenominationError)
|
|
588
|
+
return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 })
|
|
480
589
|
return Response.upstream(c, cause)
|
|
481
590
|
}
|
|
482
591
|
},
|
|
483
592
|
)
|
|
484
593
|
}
|
|
485
594
|
|
|
595
|
+
export declare namespace transfers {
|
|
596
|
+
/** Options for the transfer handlers. */
|
|
597
|
+
type Options = {
|
|
598
|
+
/** FX configuration backing leg valuation. */
|
|
599
|
+
fx?: Valuation.addresses.Fx | undefined
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
486
603
|
/**
|
|
487
604
|
* Shared TIP-20 `Transfer` page query. Used by `GET /transfers` and the
|
|
488
|
-
* webhook poller. Builds
|
|
605
|
+
* webhook poller. Builds signature-decoded `Transfer` CTE queries, applies
|
|
489
606
|
* opaque keyset pagination on `(block_num, log_idx)`, and decodes rows into a
|
|
490
607
|
* strongly-typed intermediate shape that each caller maps to its own public
|
|
491
608
|
* response.
|
|
492
609
|
*
|
|
493
|
-
* `eitherSide`
|
|
494
|
-
*
|
|
495
|
-
* because composing it with the keyset cursor pre-WHERE in SQL is awkward at
|
|
496
|
-
* the call site; the helper takes the knob directly.
|
|
610
|
+
* `eitherSide` is exposed alongside one-sided `sender`/`recipient` filters
|
|
611
|
+
* because it requires two upstream scans. The helper owns their ordered merge.
|
|
497
612
|
*
|
|
498
613
|
* Memoization lives inside the helper under one canonical `transfers:v1:…`
|
|
499
614
|
* key derived from every filter — so equivalent filter combinations share a
|
|
@@ -583,11 +698,11 @@ export async function scan(deps: scan.Deps, options: scan.Options): Promise<scan
|
|
|
583
698
|
// translate to a SQL OFFSET of `(page - 1) * limit` rows.
|
|
584
699
|
const offset =
|
|
585
700
|
options.page !== undefined && options.page > 1 ? (options.page - 1) * limit : undefined
|
|
701
|
+
const address = options.eitherSide
|
|
586
702
|
|
|
587
|
-
// Transfer events filtered by any combination of contract /
|
|
588
|
-
//
|
|
589
|
-
//
|
|
590
|
-
// page-only and added on top of the shared filters below.
|
|
703
|
+
// Transfer events filtered by any combination of contract / sender /
|
|
704
|
+
// recipient / block / timestamp. The address shortcut is split below so
|
|
705
|
+
// each upstream query can use one indexed side.
|
|
591
706
|
const filters = filterClauses(options)
|
|
592
707
|
if (cursor !== undefined)
|
|
593
708
|
filters.push(
|
|
@@ -596,24 +711,41 @@ export async function scan(deps: scan.Deps, options: scan.Options): Promise<scan
|
|
|
596
711
|
{ literal: Cursor.literal(cursor[1]!, 'int'), name: 'log_idx', order },
|
|
597
712
|
]),
|
|
598
713
|
)
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
714
|
+
const addressScoped = address !== undefined
|
|
715
|
+
const window = addressScoped ? (offset ?? 0) + limit + 1 : limit + 1
|
|
716
|
+
const fetchRows = async (side?: 'from' | 'to') => {
|
|
717
|
+
const sideFilters =
|
|
718
|
+
side !== undefined && address !== undefined
|
|
719
|
+
? [`"${side}" = '${address}'`, ...filters]
|
|
720
|
+
: filters
|
|
721
|
+
const where = sideFilters.length > 0 ? `WHERE ${sideFilters.join(' AND ')}` : ''
|
|
722
|
+
const result = await tidx.fetch({
|
|
723
|
+
chainId,
|
|
724
|
+
...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
|
|
725
|
+
query: `
|
|
726
|
+
SELECT "from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}, tx_hash, block_num, log_idx, block_timestamp
|
|
727
|
+
FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
|
|
728
|
+
${where}
|
|
729
|
+
ORDER BY block_num ${direction}, log_idx ${direction}
|
|
730
|
+
LIMIT ${window}${!addressScoped && offset !== undefined ? ` OFFSET ${offset}` : ''}
|
|
731
|
+
` as string,
|
|
732
|
+
})
|
|
733
|
+
return result.rows
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// TIDX cannot plan `(from = X OR to = X)` over the decoded Transfer table.
|
|
737
|
+
// Merge two bounded, server-filtered streams while preserving global order.
|
|
738
|
+
const rows = addressScoped
|
|
739
|
+
? mergeTransferRows((await Promise.all([fetchRows('from'), fetchRows('to')])).flat(), {
|
|
740
|
+
limit: limit + 1,
|
|
741
|
+
offset: offset ?? 0,
|
|
742
|
+
order,
|
|
743
|
+
})
|
|
744
|
+
: await fetchRows()
|
|
613
745
|
|
|
614
746
|
// The next page anchors below the last fetched row's `(block, log_idx)`.
|
|
615
747
|
const page = Cursor.paginate({
|
|
616
|
-
rows
|
|
748
|
+
rows,
|
|
617
749
|
limit,
|
|
618
750
|
key: (row) => {
|
|
619
751
|
const block = Value.toNumber(row['block_num'])
|
|
@@ -660,6 +792,49 @@ export async function scan(deps: scan.Deps, options: scan.Options): Promise<scan
|
|
|
660
792
|
return { hasMore: page.hasMore, items, limit, nextCursor: page.nextCursor }
|
|
661
793
|
}
|
|
662
794
|
|
|
795
|
+
/** Deduplicates and globally orders bounded `from`/`to` transfer streams. */
|
|
796
|
+
function mergeTransferRows(
|
|
797
|
+
rows: readonly Record<string, unknown>[],
|
|
798
|
+
options: mergeTransferRows.Options,
|
|
799
|
+
): Record<string, unknown>[] {
|
|
800
|
+
const positioned: mergeTransferRows.PositionedRow[] = []
|
|
801
|
+
const seen = new Set<string>()
|
|
802
|
+
for (const row of rows) {
|
|
803
|
+
const blockNumber = Value.toNumber(row['block_num'])
|
|
804
|
+
const logIndex = Value.toNumber(row['log_idx'])
|
|
805
|
+
const transactionHash = Value.toText(row['tx_hash'])
|
|
806
|
+
if (blockNumber === undefined || logIndex === undefined || !transactionHash) continue
|
|
807
|
+
const key = eventKey(transactionHash, logIndex)
|
|
808
|
+
if (seen.has(key)) continue
|
|
809
|
+
seen.add(key)
|
|
810
|
+
positioned.push({ blockNumber, logIndex, row })
|
|
811
|
+
}
|
|
812
|
+
positioned.sort((a, b) => {
|
|
813
|
+
const comparison = a.blockNumber - b.blockNumber || a.logIndex - b.logIndex
|
|
814
|
+
return options.order === 'asc' ? comparison : -comparison
|
|
815
|
+
})
|
|
816
|
+
return positioned.slice(options.offset, options.offset + options.limit).map(({ row }) => row)
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
declare namespace mergeTransferRows {
|
|
820
|
+
type Options = {
|
|
821
|
+
/** Maximum merged rows to return. */
|
|
822
|
+
limit: number
|
|
823
|
+
/** Number of globally ordered rows to skip. */
|
|
824
|
+
offset: number
|
|
825
|
+
/** Global transfer ordering. */
|
|
826
|
+
order: 'asc' | 'desc'
|
|
827
|
+
}
|
|
828
|
+
type PositionedRow = {
|
|
829
|
+
/** Transfer block number. */
|
|
830
|
+
blockNumber: number
|
|
831
|
+
/** Transfer log index. */
|
|
832
|
+
logIndex: number
|
|
833
|
+
/** Raw TIDX row. */
|
|
834
|
+
row: Record<string, unknown>
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
663
838
|
export declare namespace scan {
|
|
664
839
|
/** Dependencies for {@link scan}. */
|
|
665
840
|
type Deps = {
|
|
@@ -689,11 +864,9 @@ export declare namespace scan {
|
|
|
689
864
|
* physical `token` column; unscoped queries omit it and use the `Transfer` CTE.
|
|
690
865
|
*/
|
|
691
866
|
function filterClauses(options: filterClauses.Options): string[] {
|
|
692
|
-
const {
|
|
693
|
-
options
|
|
867
|
+
const { fromBlock, fromTimestamp, recipient, sender, toBlock, toTimestamp, token } = options
|
|
694
868
|
const filters: string[] = []
|
|
695
869
|
if (token !== undefined) filters.push(`token = '${token}'`)
|
|
696
|
-
if (eitherSide !== undefined) filters.push(`("from" = '${eitherSide}' OR "to" = '${eitherSide}')`)
|
|
697
870
|
if (sender !== undefined) filters.push(`"from" = '${sender}'`)
|
|
698
871
|
if (recipient !== undefined) filters.push(`"to" = '${recipient}'`)
|
|
699
872
|
if (fromBlock !== undefined) filters.push(`block_num >= ${fromBlock}`)
|
|
@@ -707,14 +880,7 @@ declare namespace filterClauses {
|
|
|
707
880
|
/** The non-pagination filter fields of {@link scan.Options}. */
|
|
708
881
|
type Options = Pick<
|
|
709
882
|
scan.Options,
|
|
710
|
-
| '
|
|
711
|
-
| 'fromBlock'
|
|
712
|
-
| 'fromTimestamp'
|
|
713
|
-
| 'recipient'
|
|
714
|
-
| 'sender'
|
|
715
|
-
| 'toBlock'
|
|
716
|
-
| 'toTimestamp'
|
|
717
|
-
| 'token'
|
|
883
|
+
'fromBlock' | 'fromTimestamp' | 'recipient' | 'sender' | 'toBlock' | 'toTimestamp' | 'token'
|
|
718
884
|
>
|
|
719
885
|
}
|
|
720
886
|
|
|
@@ -763,7 +929,7 @@ export declare namespace count {
|
|
|
763
929
|
/**
|
|
764
930
|
* Context-free core of {@link count}: counts token-scoped rows from
|
|
765
931
|
* `token_transfers`, otherwise the decoded `Transfer` CTE. The capped inner
|
|
766
|
-
* scan bounds work
|
|
932
|
+
* scan bounds work while preserving each source's established query engine.
|
|
767
933
|
*/
|
|
768
934
|
export async function countScan(
|
|
769
935
|
deps: scan.Deps,
|
|
@@ -773,56 +939,90 @@ export async function countScan(
|
|
|
773
939
|
const { tidx } = deps
|
|
774
940
|
const filters = filterClauses(options)
|
|
775
941
|
const tokenScoped = options.token !== undefined
|
|
776
|
-
const
|
|
942
|
+
const address = options.eitherSide
|
|
777
943
|
|
|
778
944
|
// Folded count: cross-token transfers collapse several `Transfer` legs into
|
|
779
945
|
// one row, so a raw `count(*)` would overcount. Scan the capped `Transfer`
|
|
780
946
|
// rows, classify each transaction, and subtract the folded-away legs. Bounded
|
|
781
947
|
// by `countCap` like the raw count, so it stays within the indexer's budget.
|
|
782
948
|
if (options.crossToken) {
|
|
949
|
+
const fetchRows = async (side?: 'from' | 'to') => {
|
|
950
|
+
const sideFilters =
|
|
951
|
+
side !== undefined && address !== undefined
|
|
952
|
+
? [`"${side}" = '${address}'`, ...filters]
|
|
953
|
+
: filters
|
|
954
|
+
const sideWhere = sideFilters.length > 0 ? `WHERE ${sideFilters.join(' AND ')}` : ''
|
|
955
|
+
const result = await tidx.fetch({
|
|
956
|
+
chainId,
|
|
957
|
+
...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
|
|
958
|
+
query: `
|
|
959
|
+
SELECT tx_hash, block_num, log_idx, "from", "to", ${tokenScoped ? 'token AS address, amount AS value' : 'address, value'}
|
|
960
|
+
FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
|
|
961
|
+
${sideWhere}
|
|
962
|
+
ORDER BY block_num, log_idx
|
|
963
|
+
LIMIT ${Schema.countCap}
|
|
964
|
+
` as string,
|
|
965
|
+
})
|
|
966
|
+
return result.rows
|
|
967
|
+
}
|
|
968
|
+
const rows =
|
|
969
|
+
address !== undefined
|
|
970
|
+
? mergeTransferRows((await Promise.all([fetchRows('from'), fetchRows('to')])).flat(), {
|
|
971
|
+
limit: Schema.countCap,
|
|
972
|
+
offset: 0,
|
|
973
|
+
order: 'asc',
|
|
974
|
+
})
|
|
975
|
+
: await fetchRows()
|
|
976
|
+
const rawCount = rows.length
|
|
977
|
+
let suppressed = 0
|
|
978
|
+
for (const legs of legsByTransaction(rows).values()) {
|
|
979
|
+
const bundle = classifyCrossToken(legs)
|
|
980
|
+
if (bundle) suppressed += bundle.suppressLogIndexes.length
|
|
981
|
+
}
|
|
982
|
+
return { totalCountCapped: rawCount >= Schema.countCap, totalCount: rawCount - suppressed }
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
const countRows = async (additionalFilters: readonly string[] = []) => {
|
|
986
|
+
const countFilters = [...additionalFilters, ...filters]
|
|
987
|
+
const countWhere = countFilters.length > 0 ? `WHERE ${countFilters.join(' AND ')}` : ''
|
|
783
988
|
const result = await tidx.fetch({
|
|
784
989
|
chainId,
|
|
785
990
|
...(tokenScoped ? { engine: 'clickhouse' } : { signatures: [signature] }),
|
|
786
991
|
query: `
|
|
787
|
-
SELECT
|
|
788
|
-
FROM
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
992
|
+
SELECT count(*) AS total
|
|
993
|
+
FROM (
|
|
994
|
+
SELECT 1
|
|
995
|
+
FROM ${tokenScoped ? 'token_transfers' : 'Transfer'}
|
|
996
|
+
${countWhere}
|
|
997
|
+
LIMIT ${Schema.countCap}
|
|
998
|
+
) AS capped
|
|
792
999
|
` as string,
|
|
793
1000
|
})
|
|
794
|
-
|
|
795
|
-
let suppressed = 0
|
|
796
|
-
for (const legs of legsByTransaction(result.rows).values()) {
|
|
797
|
-
const bundle = classifyCrossToken(legs)
|
|
798
|
-
if (bundle) suppressed += bundle.suppressLogIndexes.length
|
|
799
|
-
}
|
|
800
|
-
return { totalCountCapped: rawCount >= Schema.countCap, totalCount: rawCount - suppressed }
|
|
1001
|
+
return Value.toNumber(result.rows[0]?.['total']) ?? 0
|
|
801
1002
|
}
|
|
802
1003
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
})
|
|
1004
|
+
if (address !== undefined) {
|
|
1005
|
+
// Keep branches disjoint so self-transfers count once.
|
|
1006
|
+
const [sent, received] = await Promise.all([
|
|
1007
|
+
countRows([`"from" = '${address}'`]),
|
|
1008
|
+
countRows([`"to" = '${address}'`, `"from" != '${address}'`]),
|
|
1009
|
+
])
|
|
1010
|
+
const rawCount = sent + received
|
|
1011
|
+
return {
|
|
1012
|
+
totalCount: Math.min(rawCount, Schema.countCap),
|
|
1013
|
+
totalCountCapped:
|
|
1014
|
+
sent >= Schema.countCap || received >= Schema.countCap || rawCount >= Schema.countCap,
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
817
1017
|
|
|
818
|
-
const totalCount =
|
|
1018
|
+
const totalCount = await countRows()
|
|
819
1019
|
return { totalCountCapped: totalCount >= Schema.countCap, totalCount }
|
|
820
1020
|
}
|
|
821
1021
|
|
|
822
1022
|
export declare namespace countScan {
|
|
823
1023
|
/** Options for {@link countScan}: the shared filters plus the target chain. */
|
|
824
1024
|
type Options = filterClauses.Options &
|
|
825
|
-
Pick<scan.Options, 'chainId'> & {
|
|
1025
|
+
Pick<scan.Options, 'chainId' | 'eitherSide'> & {
|
|
826
1026
|
/** Count folded cross-token transfers instead of raw `Transfer` logs. */
|
|
827
1027
|
crossToken?: boolean | undefined
|
|
828
1028
|
}
|
|
@@ -906,7 +1106,7 @@ declare namespace memoMap {
|
|
|
906
1106
|
}
|
|
907
1107
|
}
|
|
908
1108
|
|
|
909
|
-
/** A
|
|
1109
|
+
/** A transfer leg before amount and token metadata are separated. */
|
|
910
1110
|
type CrossToken = { address: z.output<typeof Schema.Address>; amount: string }
|
|
911
1111
|
|
|
912
1112
|
/** A decoded `Transfer` leg used for cross-token classification. */
|