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
|
@@ -397,6 +397,7 @@ export async function usage(db: Db.Db, options: usage.Options): Promise<usage.Bu
|
|
|
397
397
|
.select([
|
|
398
398
|
sql<string>`to_char(${bucket}, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`.as('timestamp'),
|
|
399
399
|
sql<string>`count(*)`.as('count'),
|
|
400
|
+
sql<string>`count(*) filter (where status = 'failed')`.as('failed'),
|
|
400
401
|
sql<string>`coalesce(sum(case when status = 'finalized' then fee_amount::numeric when status = 'pending' then fee_max::numeric else 0 end), 0)`.as('feeTotal'), // prettier-ignore
|
|
401
402
|
])
|
|
402
403
|
.where('orgId', '=', orgId)
|
|
@@ -410,6 +411,7 @@ export async function usage(db: Db.Db, options: usage.Options): Promise<usage.Bu
|
|
|
410
411
|
const rows = await query.execute()
|
|
411
412
|
return rows.map((row) => ({
|
|
412
413
|
count: Number(row.count),
|
|
414
|
+
failed: Number(row.failed),
|
|
413
415
|
feeTotal: BigInt(row.feeTotal),
|
|
414
416
|
timestamp: row.timestamp,
|
|
415
417
|
}))
|
|
@@ -420,6 +422,8 @@ export declare namespace usage {
|
|
|
420
422
|
type Bucket = {
|
|
421
423
|
/** Sponsored transactions recorded in the bucket, any status. */
|
|
422
424
|
count: number
|
|
425
|
+
/** Sponsored transactions in the bucket whose status is `failed`. */
|
|
426
|
+
failed: number
|
|
423
427
|
/** Committed fees in fee-token base units: finalized fees plus pending caps. */
|
|
424
428
|
feeTotal: bigint
|
|
425
429
|
/** Bucket start (ISO 8601), aligned to UTC calendar boundaries. */
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import * as TestApp from '../../../test/App.js'
|
|
2
|
+
import * as Organizations from './organizations.js'
|
|
3
|
+
import * as VerifiedTokenRequests from './verifiedTokenRequests.js'
|
|
4
|
+
|
|
5
|
+
const input = {
|
|
6
|
+
address: '0x20c00000000000000000000000000000000000aa',
|
|
7
|
+
chainId: 42431,
|
|
8
|
+
currency: 'USD',
|
|
9
|
+
decimals: 6,
|
|
10
|
+
name: 'Test USD',
|
|
11
|
+
orgId: 'org_1a2b3c4d5e6f7g8h9j0k1m2n',
|
|
12
|
+
requestedBy: 'usr_1a2b3c4d5e6f7g8h9j0k1m2n',
|
|
13
|
+
symbol: 'tUSD',
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
describe('create', () => {
|
|
17
|
+
test('inserts a pending request', async () => {
|
|
18
|
+
const db = TestApp.database()
|
|
19
|
+
const record = await VerifiedTokenRequests.create(db, { ...input, note: 'We are the issuer.' })
|
|
20
|
+
expect(record).toBeDefined()
|
|
21
|
+
const { createdAt, id, updatedAt, ...rest } = record!
|
|
22
|
+
expect(id).toMatch(/^vtr_/)
|
|
23
|
+
expect(Date.parse(createdAt)).not.toBeNaN()
|
|
24
|
+
expect(updatedAt).toBe(createdAt)
|
|
25
|
+
expect(rest).toMatchInlineSnapshot(`
|
|
26
|
+
{
|
|
27
|
+
"address": "0x20c00000000000000000000000000000000000aa",
|
|
28
|
+
"chainId": 42431,
|
|
29
|
+
"currency": "USD",
|
|
30
|
+
"decimals": 6,
|
|
31
|
+
"logo": null,
|
|
32
|
+
"logoUri": null,
|
|
33
|
+
"name": "Test USD",
|
|
34
|
+
"note": "We are the issuer.",
|
|
35
|
+
"orgId": "org_1a2b3c4d5e6f7g8h9j0k1m2n",
|
|
36
|
+
"requestedBy": "usr_1a2b3c4d5e6f7g8h9j0k1m2n",
|
|
37
|
+
"reviewNote": null,
|
|
38
|
+
"reviewedAt": null,
|
|
39
|
+
"reviewedBy": null,
|
|
40
|
+
"status": "pending",
|
|
41
|
+
"symbol": "tUSD",
|
|
42
|
+
}
|
|
43
|
+
`)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('a duplicate pending (chain, address) yields undefined, even cross-org', async () => {
|
|
47
|
+
const db = TestApp.database()
|
|
48
|
+
await VerifiedTokenRequests.create(db, input)
|
|
49
|
+
const duplicate = await VerifiedTokenRequests.create(db, {
|
|
50
|
+
...input,
|
|
51
|
+
orgId: 'org_2b3c4d5e6f7g8h9j0k1m2n3p',
|
|
52
|
+
})
|
|
53
|
+
expect(duplicate).toBeUndefined()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('a reviewed request frees the (chain, address) slot', async () => {
|
|
57
|
+
const db = TestApp.database()
|
|
58
|
+
const first = await VerifiedTokenRequests.create(db, input)
|
|
59
|
+
await VerifiedTokenRequests.review(db, first!.id, {
|
|
60
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
61
|
+
status: 'denied',
|
|
62
|
+
})
|
|
63
|
+
const second = await VerifiedTokenRequests.create(db, input)
|
|
64
|
+
expect(second).toBeDefined()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('the same address on another chain is independent', async () => {
|
|
68
|
+
const db = TestApp.database()
|
|
69
|
+
await VerifiedTokenRequests.create(db, input)
|
|
70
|
+
const other = await VerifiedTokenRequests.create(db, { ...input, chainId: 4217 })
|
|
71
|
+
expect(other).toBeDefined()
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('list', () => {
|
|
76
|
+
test('returns only the organization, newest first', async () => {
|
|
77
|
+
const db = TestApp.database()
|
|
78
|
+
const first = await VerifiedTokenRequests.create(db, input)
|
|
79
|
+
const second = await VerifiedTokenRequests.create(db, {
|
|
80
|
+
...input,
|
|
81
|
+
address: '0x20c00000000000000000000000000000000000bb',
|
|
82
|
+
symbol: 'tEUR',
|
|
83
|
+
})
|
|
84
|
+
await VerifiedTokenRequests.create(db, {
|
|
85
|
+
...input,
|
|
86
|
+
address: '0x20c00000000000000000000000000000000000cc',
|
|
87
|
+
orgId: 'org_2b3c4d5e6f7g8h9j0k1m2n3p',
|
|
88
|
+
symbol: 'tGBP',
|
|
89
|
+
})
|
|
90
|
+
const records = await VerifiedTokenRequests.list(db, input.orgId)
|
|
91
|
+
expect(records.map(({ id }) => id).sort()).toEqual([first!.id, second!.id].sort())
|
|
92
|
+
expect(records.every(({ orgId }) => orgId === input.orgId)).toBe(true)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('listByStatus', () => {
|
|
97
|
+
test('filters by status and joins the current org name', async () => {
|
|
98
|
+
const db = TestApp.database()
|
|
99
|
+
const org = await Organizations.create(db, { name: 'Acme' })
|
|
100
|
+
const pending = await VerifiedTokenRequests.create(db, { ...input, orgId: org.id })
|
|
101
|
+
const denied = await VerifiedTokenRequests.create(db, {
|
|
102
|
+
...input,
|
|
103
|
+
address: '0x20c00000000000000000000000000000000000bb',
|
|
104
|
+
symbol: 'tEUR',
|
|
105
|
+
})
|
|
106
|
+
await VerifiedTokenRequests.review(db, denied!.id, {
|
|
107
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
108
|
+
status: 'denied',
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const queue = await VerifiedTokenRequests.listByStatus(db, 'pending')
|
|
112
|
+
expect(queue.map(({ id, orgName }) => ({ id, orgName }))).toEqual([
|
|
113
|
+
{ id: pending!.id, orgName: 'Acme' },
|
|
114
|
+
])
|
|
115
|
+
// The denied row's org has no organizations row; the join yields null.
|
|
116
|
+
const reviewed = await VerifiedTokenRequests.listByStatus(db, 'denied')
|
|
117
|
+
expect(reviewed.map(({ id, orgName }) => ({ id, orgName }))).toEqual([
|
|
118
|
+
{ id: denied!.id, orgName: null },
|
|
119
|
+
])
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe('countPending', () => {
|
|
124
|
+
test('counts only the organization pending requests', async () => {
|
|
125
|
+
const db = TestApp.database()
|
|
126
|
+
await VerifiedTokenRequests.create(db, input)
|
|
127
|
+
const denied = await VerifiedTokenRequests.create(db, {
|
|
128
|
+
...input,
|
|
129
|
+
address: '0x20c00000000000000000000000000000000000bb',
|
|
130
|
+
symbol: 'tEUR',
|
|
131
|
+
})
|
|
132
|
+
await VerifiedTokenRequests.review(db, denied!.id, {
|
|
133
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
134
|
+
status: 'denied',
|
|
135
|
+
})
|
|
136
|
+
await VerifiedTokenRequests.create(db, {
|
|
137
|
+
...input,
|
|
138
|
+
address: '0x20c00000000000000000000000000000000000cc',
|
|
139
|
+
orgId: 'org_2b3c4d5e6f7g8h9j0k1m2n3p',
|
|
140
|
+
symbol: 'tGBP',
|
|
141
|
+
})
|
|
142
|
+
expect(await VerifiedTokenRequests.countPending(db, input.orgId)).toBe(1)
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
describe('remove', () => {
|
|
147
|
+
test('deletes only in-org pending requests', async () => {
|
|
148
|
+
const db = TestApp.database()
|
|
149
|
+
const record = await VerifiedTokenRequests.create(db, input)
|
|
150
|
+
expect(
|
|
151
|
+
await VerifiedTokenRequests.remove(db, 'org_2b3c4d5e6f7g8h9j0k1m2n3p', record!.id),
|
|
152
|
+
).toBeUndefined()
|
|
153
|
+
expect(await VerifiedTokenRequests.remove(db, input.orgId, record!.id)).toBeDefined()
|
|
154
|
+
expect(await VerifiedTokenRequests.get(db, record!.id)).toBeUndefined()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('leaves reviewed requests in place', async () => {
|
|
158
|
+
const db = TestApp.database()
|
|
159
|
+
const record = await VerifiedTokenRequests.create(db, input)
|
|
160
|
+
await VerifiedTokenRequests.review(db, record!.id, {
|
|
161
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
162
|
+
status: 'approved',
|
|
163
|
+
})
|
|
164
|
+
expect(await VerifiedTokenRequests.remove(db, input.orgId, record!.id)).toBeUndefined()
|
|
165
|
+
expect(await VerifiedTokenRequests.get(db, record!.id)).toBeDefined()
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
describe('review', () => {
|
|
170
|
+
test('marks a pending request with reviewer identity', async () => {
|
|
171
|
+
const db = TestApp.database()
|
|
172
|
+
const record = await VerifiedTokenRequests.create(db, input)
|
|
173
|
+
const reviewed = await VerifiedTokenRequests.review(db, record!.id, {
|
|
174
|
+
reviewNote: 'Logo unreachable.',
|
|
175
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
176
|
+
status: 'denied',
|
|
177
|
+
})
|
|
178
|
+
expect(reviewed).toMatchObject({
|
|
179
|
+
reviewNote: 'Logo unreachable.',
|
|
180
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
181
|
+
status: 'denied',
|
|
182
|
+
})
|
|
183
|
+
expect(Date.parse(reviewed!.reviewedAt!)).not.toBeNaN()
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('yields undefined once reviewed', async () => {
|
|
187
|
+
const db = TestApp.database()
|
|
188
|
+
const record = await VerifiedTokenRequests.create(db, input)
|
|
189
|
+
await VerifiedTokenRequests.review(db, record!.id, {
|
|
190
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
191
|
+
status: 'approved',
|
|
192
|
+
})
|
|
193
|
+
expect(
|
|
194
|
+
await VerifiedTokenRequests.review(db, record!.id, {
|
|
195
|
+
reviewedBy: 'admin@tempo.xyz',
|
|
196
|
+
status: 'denied',
|
|
197
|
+
}),
|
|
198
|
+
).toBeUndefined()
|
|
199
|
+
})
|
|
200
|
+
})
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { type Selectable, sql } from 'kysely'
|
|
2
|
+
|
|
3
|
+
import * as Id from '../../internal/Id.js'
|
|
4
|
+
import type * as Db from '../Db.js'
|
|
5
|
+
import type * as db_Schema from '../Schema.js'
|
|
6
|
+
|
|
7
|
+
/** Columns of the `verified_token_requests` table. */
|
|
8
|
+
export type Table = db_Schema.VerifiedTokenRequest
|
|
9
|
+
/** A stored verification request. */
|
|
10
|
+
export type Record = Selectable<Table>
|
|
11
|
+
/** Review status of a request. */
|
|
12
|
+
export type Status = Record['status']
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Inserts a pending request; `undefined` when a pending request for
|
|
16
|
+
* (chainId, address) already exists.
|
|
17
|
+
*/
|
|
18
|
+
export function create(db: Db.Db, input: create.Input): Promise<Record | undefined> {
|
|
19
|
+
const now = new Date().toISOString()
|
|
20
|
+
return db.kysely
|
|
21
|
+
.insertInto('verified_token_requests')
|
|
22
|
+
.values({
|
|
23
|
+
address: input.address,
|
|
24
|
+
chainId: input.chainId,
|
|
25
|
+
createdAt: now,
|
|
26
|
+
currency: input.currency,
|
|
27
|
+
decimals: input.decimals,
|
|
28
|
+
id: Id.generate('vtr'),
|
|
29
|
+
logo: input.logo ?? null,
|
|
30
|
+
logoUri: input.logoUri ?? null,
|
|
31
|
+
name: input.name,
|
|
32
|
+
note: input.note ?? null,
|
|
33
|
+
orgId: input.orgId,
|
|
34
|
+
requestedBy: input.requestedBy,
|
|
35
|
+
reviewNote: null,
|
|
36
|
+
reviewedAt: null,
|
|
37
|
+
reviewedBy: null,
|
|
38
|
+
status: 'pending',
|
|
39
|
+
symbol: input.symbol,
|
|
40
|
+
updatedAt: now,
|
|
41
|
+
})
|
|
42
|
+
.onConflict((oc) =>
|
|
43
|
+
oc
|
|
44
|
+
.columns(['chainId', 'address'])
|
|
45
|
+
// `sql.lit` keeps the predicate literal; a bound parameter would
|
|
46
|
+
// defeat partial-index inference for the conflict target.
|
|
47
|
+
.where('status', '=', sql.lit('pending'))
|
|
48
|
+
.doNothing(),
|
|
49
|
+
)
|
|
50
|
+
.returningAll()
|
|
51
|
+
.executeTakeFirst()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export declare namespace create {
|
|
55
|
+
/** Fields accepted when inserting a request. */
|
|
56
|
+
type Input = {
|
|
57
|
+
/** Requested token contract address (lowercase). */
|
|
58
|
+
address: string
|
|
59
|
+
/** Chain the token belongs to. */
|
|
60
|
+
chainId: number
|
|
61
|
+
/** Display currency, e.g. `USD`. */
|
|
62
|
+
currency: string
|
|
63
|
+
/** Decimal precision. */
|
|
64
|
+
decimals: number
|
|
65
|
+
/** Inline SVG logo markup uploaded by the requester. */
|
|
66
|
+
logo?: string | undefined
|
|
67
|
+
/** HTTPS logo URL from chain metadata. */
|
|
68
|
+
logoUri?: string | undefined
|
|
69
|
+
/** Display name. */
|
|
70
|
+
name: string
|
|
71
|
+
/** Requester note to reviewers. */
|
|
72
|
+
note?: string | undefined
|
|
73
|
+
/** Requesting organization id (`org_…`). */
|
|
74
|
+
orgId: string
|
|
75
|
+
/** User id that submitted the request, or `super_admin`. */
|
|
76
|
+
requestedBy: string
|
|
77
|
+
/** Ticker symbol. */
|
|
78
|
+
symbol: string
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Lists an organization's requests, newest first. */
|
|
83
|
+
export function list(db: Db.Db, orgId: string): Promise<Record[]> {
|
|
84
|
+
return db.kysely
|
|
85
|
+
.selectFrom('verified_token_requests')
|
|
86
|
+
.selectAll()
|
|
87
|
+
.where('orgId', '=', orgId)
|
|
88
|
+
.orderBy('createdAt', 'desc')
|
|
89
|
+
.execute()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Lists requests in one status globally, oldest first (review-queue order),
|
|
94
|
+
* with the requesting organization's current name when it still exists.
|
|
95
|
+
*/
|
|
96
|
+
export function listByStatus(
|
|
97
|
+
db: Db.Db,
|
|
98
|
+
status: Status,
|
|
99
|
+
): Promise<(Record & { orgName: string | null })[]> {
|
|
100
|
+
return db.kysely
|
|
101
|
+
.selectFrom('verified_token_requests')
|
|
102
|
+
.leftJoin('organizations', 'organizations.id', 'verified_token_requests.orgId')
|
|
103
|
+
.selectAll('verified_token_requests')
|
|
104
|
+
.select('organizations.name as orgName')
|
|
105
|
+
.where('status', '=', status)
|
|
106
|
+
.orderBy('verified_token_requests.createdAt', 'asc')
|
|
107
|
+
.execute()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Gets a request by id. */
|
|
111
|
+
export function get(db: Db.Db, id: string): Promise<Record | undefined> {
|
|
112
|
+
return db.kysely
|
|
113
|
+
.selectFrom('verified_token_requests')
|
|
114
|
+
.selectAll()
|
|
115
|
+
.where('id', '=', id)
|
|
116
|
+
.executeTakeFirst()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Counts an organization's pending requests. */
|
|
120
|
+
export async function countPending(db: Db.Db, orgId: string): Promise<number> {
|
|
121
|
+
const row = await db.kysely
|
|
122
|
+
.selectFrom('verified_token_requests')
|
|
123
|
+
.select(({ fn }) => fn.countAll<string>().as('count'))
|
|
124
|
+
.where('orgId', '=', orgId)
|
|
125
|
+
.where('status', '=', 'pending')
|
|
126
|
+
.executeTakeFirstOrThrow()
|
|
127
|
+
return Number(row.count)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Hard-deletes an org-scoped request while pending; `undefined` otherwise. */
|
|
131
|
+
export function remove(db: Db.Db, orgId: string, id: string): Promise<Record | undefined> {
|
|
132
|
+
return db.kysely
|
|
133
|
+
.deleteFrom('verified_token_requests')
|
|
134
|
+
.where('id', '=', id)
|
|
135
|
+
.where('orgId', '=', orgId)
|
|
136
|
+
.where('status', '=', 'pending')
|
|
137
|
+
.returningAll()
|
|
138
|
+
.executeTakeFirst()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Marks a pending request approved or denied with reviewer identity;
|
|
143
|
+
* `undefined` when absent or already reviewed.
|
|
144
|
+
*/
|
|
145
|
+
export function review(db: Db.Db, id: string, input: review.Input): Promise<Record | undefined> {
|
|
146
|
+
const now = new Date().toISOString()
|
|
147
|
+
return db.kysely
|
|
148
|
+
.updateTable('verified_token_requests')
|
|
149
|
+
.set({
|
|
150
|
+
reviewNote: input.reviewNote ?? null,
|
|
151
|
+
reviewedAt: now,
|
|
152
|
+
reviewedBy: input.reviewedBy,
|
|
153
|
+
status: input.status,
|
|
154
|
+
updatedAt: now,
|
|
155
|
+
})
|
|
156
|
+
.where('id', '=', id)
|
|
157
|
+
.where('status', '=', 'pending')
|
|
158
|
+
.returningAll()
|
|
159
|
+
.executeTakeFirst()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export declare namespace review {
|
|
163
|
+
/** Review outcome fields. */
|
|
164
|
+
type Input = {
|
|
165
|
+
/** Reviewer note shown on denial. */
|
|
166
|
+
reviewNote?: string | undefined
|
|
167
|
+
/** Reviewing admin email. */
|
|
168
|
+
reviewedBy: string
|
|
169
|
+
/** Review outcome. */
|
|
170
|
+
status: 'approved' | 'denied'
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Maps a stored row to the wire shape (null `logo`/`logoUri` map to absent). */
|
|
175
|
+
export function toRequest(record: Record) {
|
|
176
|
+
const { logo, logoUri, ...rest } = record
|
|
177
|
+
return {
|
|
178
|
+
...rest,
|
|
179
|
+
...(logo === null ? {} : { logo }),
|
|
180
|
+
...(logoUri === null ? {} : { logoUri }),
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -5,6 +5,8 @@ import type * as Webhooks from '../../internal/Webhooks.js'
|
|
|
5
5
|
import type * as Db from '../Db.js'
|
|
6
6
|
import type * as db_Schema from '../Schema.js'
|
|
7
7
|
|
|
8
|
+
const subscriptionLimitLockClass = 1464353612
|
|
9
|
+
|
|
8
10
|
/** Columns of the `webhook_subscriptions` table, derived from `Schema.WebhookSubscription`. */
|
|
9
11
|
export type Table = db_Schema.WebhookSubscription
|
|
10
12
|
|
|
@@ -69,6 +71,35 @@ export async function insert(db: Db.Db, subscription: Webhooks.Subscription): Pr
|
|
|
69
71
|
.execute()
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Inserts a subscription when its owner remains below the configured cap.
|
|
76
|
+
* An owner-scoped transaction lock serializes the count and insert.
|
|
77
|
+
*/
|
|
78
|
+
export async function insertWithinLimit(
|
|
79
|
+
db: Db.Db,
|
|
80
|
+
subscription: Webhooks.Subscription,
|
|
81
|
+
options: insertWithinLimit.Options,
|
|
82
|
+
): Promise<boolean> {
|
|
83
|
+
return db.transaction(async (tx) => {
|
|
84
|
+
const owner = `${subscription.owner.type}:${ownerId(subscription.owner)}`
|
|
85
|
+
await sql`select pg_advisory_xact_lock(${subscriptionLimitLockClass}, hashtext(${owner}))`.execute(tx.kysely) // prettier-ignore
|
|
86
|
+
const existing = await count(tx, subscription.owner, options.now)
|
|
87
|
+
if (existing >= options.maxPerOwner) return false
|
|
88
|
+
await insert(tx, subscription)
|
|
89
|
+
return true
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export declare namespace insertWithinLimit {
|
|
94
|
+
/** Options for an atomic capped insert. */
|
|
95
|
+
type Options = {
|
|
96
|
+
/** Maximum live subscriptions the owner may hold. */
|
|
97
|
+
maxPerOwner: number
|
|
98
|
+
/** ISO timestamp used to exclude expired rows from the count. */
|
|
99
|
+
now: string
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
72
103
|
/**
|
|
73
104
|
* Reads an owner's subscription by id; expired rows never match (query-time
|
|
74
105
|
* expiry, same read semantics as the old store TTL).
|
|
@@ -3,6 +3,8 @@ import { KeyAuthorization, Transaction as core_Transaction, TxEnvelopeTempo } fr
|
|
|
3
3
|
import { type Client, BaseError } from 'viem'
|
|
4
4
|
import * as z from 'zod/mini'
|
|
5
5
|
|
|
6
|
+
import * as ApiResponse from '../../internal/Response.js'
|
|
7
|
+
|
|
6
8
|
export function resolveChainId(value: unknown) {
|
|
7
9
|
if (typeof value === 'number') return value
|
|
8
10
|
if (typeof value === 'bigint') return Number(value)
|
|
@@ -97,24 +99,38 @@ export function rpcErrorJson(request: RpcRequest.RpcRequest, error: unknown) {
|
|
|
97
99
|
{ request },
|
|
98
100
|
)
|
|
99
101
|
|
|
100
|
-
if (
|
|
102
|
+
if (error instanceof z.core.$ZodError)
|
|
101
103
|
return RpcResponse.from(
|
|
102
104
|
{
|
|
103
105
|
error: {
|
|
104
106
|
code: RpcResponse.InvalidParamsError.code,
|
|
105
|
-
|
|
107
|
+
data: {
|
|
108
|
+
code: 'invalid_params',
|
|
109
|
+
issues: ApiResponse.validationDetails(error.issues.slice(0, 10)),
|
|
110
|
+
},
|
|
111
|
+
message: 'Invalid params.',
|
|
106
112
|
},
|
|
107
113
|
},
|
|
108
114
|
{ request },
|
|
109
115
|
)
|
|
110
116
|
|
|
111
117
|
const inner = resolveError(error)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
if (inner.code === undefined || inner.message === undefined)
|
|
119
|
+
return RpcResponse.from(
|
|
120
|
+
{
|
|
121
|
+
error: {
|
|
122
|
+
code: RpcResponse.InternalError.code,
|
|
123
|
+
data: { code: 'internal_error' },
|
|
124
|
+
message: 'Internal error',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{ request },
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
const { code, data, message } = inner
|
|
115
131
|
return RpcResponse.from(
|
|
116
132
|
{
|
|
117
|
-
error: { code, message, ...(data ? {
|
|
133
|
+
error: { code, message, ...(data === undefined ? {} : { data }) },
|
|
118
134
|
},
|
|
119
135
|
{ request },
|
|
120
136
|
)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import * as z from 'zod/mini'
|
|
2
|
+
|
|
3
|
+
import * as Relay from '../../test/Relay.js'
|
|
4
|
+
|
|
5
|
+
describe('request errors', () => {
|
|
6
|
+
test('returns Parse error for invalid JSON', async () => {
|
|
7
|
+
const server = await Relay.createServer((await Relay.relay()).listener)
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const response = await fetch(server.url, {
|
|
11
|
+
body: '{',
|
|
12
|
+
headers: { 'Content-Type': 'application/json' },
|
|
13
|
+
method: 'POST',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
17
|
+
{
|
|
18
|
+
"error": {
|
|
19
|
+
"code": -32700,
|
|
20
|
+
"message": "Parse error",
|
|
21
|
+
},
|
|
22
|
+
"id": null,
|
|
23
|
+
"jsonrpc": "2.0",
|
|
24
|
+
}
|
|
25
|
+
`)
|
|
26
|
+
} finally {
|
|
27
|
+
await server.closeAsync()
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test.each([
|
|
32
|
+
['non-object request', 'null'],
|
|
33
|
+
['invalid version', JSON.stringify({ id: 1, jsonrpc: '1.0', method: 'eth_chainId' })],
|
|
34
|
+
['empty batch', '[]'],
|
|
35
|
+
])('returns Invalid Request for %s', async (_name, body) => {
|
|
36
|
+
const server = await Relay.createServer((await Relay.relay()).listener)
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(server.url, {
|
|
40
|
+
body,
|
|
41
|
+
headers: { 'Content-Type': 'application/json' },
|
|
42
|
+
method: 'POST',
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
46
|
+
{
|
|
47
|
+
"error": {
|
|
48
|
+
"code": -32600,
|
|
49
|
+
"message": "Invalid Request",
|
|
50
|
+
},
|
|
51
|
+
"id": null,
|
|
52
|
+
"jsonrpc": "2.0",
|
|
53
|
+
}
|
|
54
|
+
`)
|
|
55
|
+
} finally {
|
|
56
|
+
await server.closeAsync()
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('returns one Invalid Request error for a malformed batch member', async () => {
|
|
61
|
+
const server = await Relay.createServer(
|
|
62
|
+
(
|
|
63
|
+
await Relay.relay({
|
|
64
|
+
getClient: () => {
|
|
65
|
+
throw new Error('transport credential must not leak')
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
).listener,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const response = await fetch(server.url, {
|
|
73
|
+
body: JSON.stringify([
|
|
74
|
+
{ id: 1, jsonrpc: '2.0', method: 'eth_chainId' },
|
|
75
|
+
{ id: 2, jsonrpc: '1.0', method: 'eth_chainId' },
|
|
76
|
+
]),
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
method: 'POST',
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
82
|
+
[
|
|
83
|
+
{
|
|
84
|
+
"error": {
|
|
85
|
+
"code": -32603,
|
|
86
|
+
"data": {
|
|
87
|
+
"code": "internal_error",
|
|
88
|
+
},
|
|
89
|
+
"message": "Internal error",
|
|
90
|
+
},
|
|
91
|
+
"id": 1,
|
|
92
|
+
"jsonrpc": "2.0",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"error": {
|
|
96
|
+
"code": -32600,
|
|
97
|
+
"message": "Invalid Request",
|
|
98
|
+
},
|
|
99
|
+
"id": null,
|
|
100
|
+
"jsonrpc": "2.0",
|
|
101
|
+
},
|
|
102
|
+
]
|
|
103
|
+
`)
|
|
104
|
+
} finally {
|
|
105
|
+
await server.closeAsync()
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('returns bounded structured issues for invalid params', async () => {
|
|
110
|
+
const schema = z.object({ value: z.string() })
|
|
111
|
+
const server = await Relay.createServer(
|
|
112
|
+
(
|
|
113
|
+
await Relay.relay({
|
|
114
|
+
onRequest: async () => {
|
|
115
|
+
schema.parse({ value: 1 })
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
).listener,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const response = await fetch(server.url, {
|
|
123
|
+
body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
|
|
124
|
+
headers: { 'Content-Type': 'application/json' },
|
|
125
|
+
method: 'POST',
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
129
|
+
{
|
|
130
|
+
"error": {
|
|
131
|
+
"code": -32602,
|
|
132
|
+
"data": {
|
|
133
|
+
"code": "invalid_params",
|
|
134
|
+
"issues": [
|
|
135
|
+
{
|
|
136
|
+
"message": "Invalid input",
|
|
137
|
+
"path": [
|
|
138
|
+
"value",
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
"message": "Invalid params.",
|
|
144
|
+
},
|
|
145
|
+
"id": 1,
|
|
146
|
+
"jsonrpc": "2.0",
|
|
147
|
+
}
|
|
148
|
+
`)
|
|
149
|
+
} finally {
|
|
150
|
+
await server.closeAsync()
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
})
|