tapimo 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -5
- package/dist/ApiKey.d.ts +2 -2
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +2 -2
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +85 -11
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +218 -56
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +12543 -13912
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +4 -5
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +12 -20
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +20 -36
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +871 -828
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +14 -26
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +93 -186
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +3 -5
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/audit-logs.d.ts +18 -18
- package/dist/admin/apps/audit-logs.js +1 -1
- package/dist/admin/apps/audit-logs.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +27 -27
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +11 -5
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +17 -17
- package/dist/admin/apps/prop-amms.d.ts +4 -4
- package/dist/admin/apps/routes-support.js +1 -38
- package/dist/admin/apps/routes-support.js.map +1 -1
- package/dist/admin/apps/routes.d.ts +153 -153
- package/dist/admin/apps/sentinel.d.ts +4 -4
- package/dist/admin/apps/verified-tokens.d.ts +24 -24
- package/dist/admin/apps/zone-delegation-requests.d.ts +332 -0
- package/dist/admin/apps/zone-delegation-requests.d.ts.map +1 -0
- package/dist/admin/apps/zone-delegation-requests.js +200 -0
- package/dist/admin/apps/zone-delegation-requests.js.map +1 -0
- package/dist/admin/apps/zone-delegations.d.ts +512 -52
- package/dist/admin/apps/zone-delegations.d.ts.map +1 -1
- package/dist/admin/apps/zone-delegations.js +134 -44
- package/dist/admin/apps/zone-delegations.js.map +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.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 +341 -397
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +4 -2
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +11932 -13214
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +32 -13
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +1 -1
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
- package/dist/apps/data/ZoneSelection.js +2 -2
- package/dist/apps/data/ZoneSelection.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +60 -27
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +91 -16
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +893 -875
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +36 -36
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +3 -1
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +40 -40
- package/dist/apps/data/routes/blocks.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.js +3 -7
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +236 -236
- package/dist/apps/data/routes/earn-transactions.d.ts +14 -14
- package/dist/apps/data/routes/earn.d.ts +261 -1445
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +64 -490
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +144 -144
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +5 -15
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +3 -9
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +13 -13
- package/dist/apps/data/routes/receipts.d.ts +53 -53
- package/dist/apps/data/routes/receipts.js +1 -1
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts +52 -0
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +19 -18
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +14 -14
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +1 -3
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +7 -5
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +281 -281
- package/dist/apps/data/routes/transactions.js +3 -3
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +12 -12
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +1 -3
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +30 -30
- package/dist/apps/data/routes/valuation.d.ts.map +1 -1
- package/dist/apps/data/routes/valuation.js +1 -3
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
- package/dist/apps/data/routes/webhooks.d.ts +102 -102
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +9 -23
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zone-deposits.d.ts +16 -16
- package/dist/apps/data/routes/zone-withdrawals.d.ts +31 -31
- package/dist/apps/data/routes/zones.d.ts +28 -28
- package/dist/apps/management/App.d.ts +18 -102
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +1 -1
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +253 -339
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +25 -11
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +92 -92
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +5 -13
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/cli.d.ts.map +1 -1
- package/dist/apps/management/routes/cli.js +3 -9
- package/dist/apps/management/routes/cli.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +16 -16
- package/dist/apps/management/routes/fee-payer-usage.d.ts +38 -38
- package/dist/apps/management/routes/fee-payer-usage.d.ts.map +1 -1
- package/dist/apps/management/routes/fee-payer-usage.js +2 -4
- package/dist/apps/management/routes/fee-payer-usage.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +52 -52
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +4 -12
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +6 -14
- 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/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +3 -7
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +84 -84
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +2 -4
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +44 -44
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +2 -6
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +2 -6
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/route-usage.d.ts +34 -34
- package/dist/apps/management/routes/route-usage.d.ts.map +1 -1
- package/dist/apps/management/routes/route-usage.js +2 -4
- package/dist/apps/management/routes/route-usage.js.map +1 -1
- package/dist/apps/management/routes/routes-access-requests.d.ts +120 -91
- package/dist/apps/management/routes/routes-access-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/routes-access-requests.js +49 -9
- package/dist/apps/management/routes/routes-access-requests.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +8 -16
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +39 -39
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +3 -7
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +2089 -2089
- package/dist/apps/management/routes/zone-delegations.d.ts +14 -17
- package/dist/apps/management/routes/zone-delegations.d.ts.map +1 -1
- package/dist/apps/management/routes/zone-delegations.js +1 -6
- package/dist/apps/management/routes/zone-delegations.js.map +1 -1
- package/dist/apps/mpp/App.d.ts +44 -44
- package/dist/apps/mpp/App.js +3 -3
- package/dist/apps/mpp/App.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +1 -1
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/apps/mpp/internal/Error.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Error.js +1 -0
- package/dist/apps/mpp/internal/Error.js.map +1 -1
- package/dist/apps/mpp/internal/Policy.d.ts +5 -1
- package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Policy.js +35 -13
- package/dist/apps/mpp/internal/Policy.js.map +1 -1
- package/dist/apps/relay/App.d.ts +2 -2
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +25 -16
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/routes/App.d.ts +311 -311
- package/dist/apps/routes/routes/deposit-addresses.d.ts +1099 -1099
- package/dist/apps/routes/routes/list.d.ts +74 -74
- package/dist/apps/routes/routes/transfers.d.ts +94 -94
- package/dist/cli/ApiKeys.d.ts +19 -26
- package/dist/cli/ApiKeys.d.ts.map +1 -1
- package/dist/db/Db.d.ts +3 -3
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +27 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +28 -62
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +18 -18
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/rewardRuns.d.ts +21 -0
- package/dist/db/tables/rewardRuns.d.ts.map +1 -1
- package/dist/db/tables/rewardRuns.js +19 -0
- package/dist/db/tables/rewardRuns.js.map +1 -1
- package/dist/db/tables/routesAccessRequests.d.ts +13 -0
- package/dist/db/tables/routesAccessRequests.d.ts.map +1 -1
- package/dist/db/tables/routesAccessRequests.js +12 -0
- package/dist/db/tables/routesAccessRequests.js.map +1 -1
- package/dist/db/tables/zoneDelegationRequests.d.ts +165 -0
- package/dist/db/tables/zoneDelegationRequests.d.ts.map +1 -0
- package/dist/db/tables/{zoneKeyRequests.js → zoneDelegationRequests.js} +43 -42
- package/dist/db/tables/zoneDelegationRequests.js.map +1 -0
- package/dist/db/tables/zoneDelegations.d.ts +11 -5
- package/dist/db/tables/zoneDelegations.d.ts.map +1 -1
- package/dist/db/tables/zoneDelegations.js +21 -3
- package/dist/db/tables/zoneDelegations.js.map +1 -1
- package/dist/handlers/internal/accounts.d.ts +7 -0
- package/dist/handlers/internal/accounts.d.ts.map +1 -0
- package/dist/handlers/internal/{multisig.js → accounts.js} +10 -10
- package/dist/handlers/internal/{multisig.js.map → accounts.js.map} +1 -1
- package/dist/handlers/internal/sponsorship.js +17 -17
- package/dist/handlers/internal/sponsorship.js.map +1 -1
- package/dist/handlers/relay.d.ts +6 -6
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +15 -15
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +4 -8
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/EarnRates.d.ts.map +1 -1
- package/dist/internal/EarnRates.js +1 -3
- package/dist/internal/EarnRates.js.map +1 -1
- package/dist/internal/EarnVaults.js +2 -2
- package/dist/internal/Log.d.ts +2 -4
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +0 -3
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/Response.d.ts +1 -1
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +0 -3
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +11 -0
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +24 -1
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/ZoneAccess.d.ts +70 -35
- package/dist/internal/ZoneAccess.d.ts.map +1 -1
- package/dist/internal/ZoneAccess.js +312 -131
- package/dist/internal/ZoneAccess.js.map +1 -1
- package/dist/internal/ZoneDelegation.d.ts +7 -19
- package/dist/internal/ZoneDelegation.d.ts.map +1 -1
- package/dist/internal/ZoneDelegation.js +8 -29
- package/dist/internal/ZoneDelegation.js.map +1 -1
- package/dist/internal/{ZoneKeyApproval.d.ts → ZoneDelegationApproval.d.ts} +3 -5
- package/dist/internal/ZoneDelegationApproval.d.ts.map +1 -0
- package/dist/internal/ZoneDelegationApproval.js +16 -0
- package/dist/internal/ZoneDelegationApproval.js.map +1 -0
- package/dist/internal/ZoneKeyRewrite.d.ts +98 -0
- package/dist/internal/ZoneKeyRewrite.d.ts.map +1 -0
- package/dist/internal/ZoneKeyRewrite.js +151 -0
- package/dist/internal/ZoneKeyRewrite.js.map +1 -0
- package/dist/internal/rewards/Campaigns.d.ts +2 -0
- package/dist/internal/rewards/Campaigns.d.ts.map +1 -1
- package/dist/internal/rewards/Campaigns.js.map +1 -1
- package/dist/internal/rewards/Distributions.d.ts.map +1 -1
- package/dist/internal/rewards/Distributions.js +2 -6
- package/dist/internal/rewards/Distributions.js.map +1 -1
- package/dist/internal/rewards/Monitor.d.ts +4 -1
- package/dist/internal/rewards/Monitor.d.ts.map +1 -1
- package/dist/internal/rewards/Monitor.js +19 -2
- package/dist/internal/rewards/Monitor.js.map +1 -1
- package/dist/internal/rewards/Runner.d.ts.map +1 -1
- package/dist/internal/rewards/Runner.js +9 -1
- package/dist/internal/rewards/Runner.js.map +1 -1
- package/dist/internal/routes/Action.d.ts.map +1 -1
- package/dist/internal/routes/Action.js +2 -3
- package/dist/internal/routes/Action.js.map +1 -1
- package/dist/internal/routes/Deposit.d.ts.map +1 -1
- package/dist/internal/routes/Deposit.js +2 -6
- package/dist/internal/routes/Deposit.js.map +1 -1
- package/dist/internal/routes/DepositAddress.d.ts.map +1 -1
- package/dist/internal/routes/DepositAddress.js +4 -10
- package/dist/internal/routes/DepositAddress.js.map +1 -1
- package/dist/internal/routes/Policy.d.ts.map +1 -1
- package/dist/internal/routes/Policy.js +2 -1
- package/dist/internal/routes/Policy.js.map +1 -1
- package/dist/internal/routes/Provider.js +8 -8
- package/dist/internal/routes/Provider.js.map +1 -1
- package/dist/internal/routes/Reconciliation.d.ts.map +1 -1
- package/dist/internal/routes/Reconciliation.js +3 -2
- package/dist/internal/routes/Reconciliation.js.map +1 -1
- package/dist/internal/routes/Transfer.d.ts.map +1 -1
- package/dist/internal/routes/Transfer.js +4 -12
- package/dist/internal/routes/Transfer.js.map +1 -1
- package/dist/internal/routes/UniversalDepositAddress.d.ts.map +1 -1
- package/dist/internal/routes/UniversalDepositAddress.js +3 -6
- package/dist/internal/routes/UniversalDepositAddress.js.map +1 -1
- package/dist/internal/routes/providers/SquidOrder.d.ts.map +1 -1
- package/dist/internal/routes/providers/SquidOrder.js +3 -2
- package/dist/internal/routes/providers/SquidOrder.js.map +1 -1
- package/dist/internal/routes/providers/relay.d.ts.map +1 -1
- package/dist/internal/routes/providers/relay.js +4 -3
- package/dist/internal/routes/providers/relay.js.map +1 -1
- package/package.json +20 -5
- package/src/ApiKey.ts +2 -2
- package/src/ApiKeys.test.ts +284 -51
- package/src/ApiKeys.ts +297 -64
- package/src/App.ts +4 -5
- package/src/Client.test-d.ts +33 -126
- package/src/Scope.test.ts +26 -45
- package/src/Scope.ts +24 -37
- package/src/admin/App.test.ts +51 -4
- package/src/admin/App.ts +25 -33
- package/src/admin/Mcp.test.ts +2 -1
- package/src/admin/apps/api-keys.test.ts +90 -20
- package/src/admin/apps/api-keys.ts +5 -8
- package/src/admin/apps/audit-logs.ts +1 -1
- package/src/admin/apps/earn-vaults.test.ts +19 -19
- package/src/admin/apps/earn-vaults.ts +11 -5
- package/src/admin/apps/routes-support.test.ts +254 -3
- package/src/admin/apps/routes-support.ts +1 -44
- package/src/admin/apps/scopes.test.ts +3 -3
- package/src/admin/apps/zone-delegation-requests.test.ts +246 -0
- package/src/admin/apps/zone-delegation-requests.ts +240 -0
- package/src/admin/apps/zone-delegations.test.ts +218 -58
- package/src/admin/apps/zone-delegations.ts +146 -46
- package/src/admin/index.ts +1 -1
- package/src/admin/ui/src/components/ApiKeyForm.tsx +4 -3
- package/src/admin/ui/src/lib/config.ts +3 -3
- package/src/admin/ui/src/lib/query-client.ts +1 -1
- package/src/admin/ui/src/lib/review-queues.ts +10 -10
- package/src/admin/ui/src/lib/routes-setup.test.ts +1 -2
- package/src/admin/ui/src/lib/routes-setup.ts +3 -5
- package/src/admin/ui/src/lib/scopes.test.ts +3 -6
- package/src/admin/ui/src/lib/scopes.ts +2 -9
- package/src/admin/ui/src/lib/{zone-key-requests.ts → zone-delegation-requests.ts} +27 -31
- package/src/admin/ui/src/routeTree.gen.ts +17 -17
- package/src/admin/ui/src/routes/__root.tsx +1 -1
- package/src/admin/ui/src/routes/api-keys.tsx +2 -15
- package/src/admin/ui/src/routes/audit-logs.tsx +2 -2
- package/src/admin/ui/src/routes/index.tsx +11 -11
- package/src/admin/ui/src/routes/organizations.tsx +3 -1
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +3 -1
- package/src/admin/ui/src/routes/organizations_.$orgId_.setup.tsx +1 -3
- package/src/admin/ui/src/routes/{zone-key-requests.tsx → zone-delegation-requests.tsx} +228 -172
- package/src/admin/ui/vite.config.ts +14 -1
- package/src/admin/ui.generated.ts +412 -583
- package/src/analytics/tables/requestEvents.test.ts +13 -3
- package/src/analytics/tables/requestEvents.ts +4 -3
- package/src/apps/data/App.test.ts +72 -35
- package/src/apps/data/App.ts +29 -13
- package/src/apps/data/ZoneSelection.test.ts +53 -70
- package/src/apps/data/ZoneSelection.ts +2 -2
- package/src/apps/data/routes/activities.test.ts +140 -6
- package/src/apps/data/routes/activities.ts +124 -18
- package/src/apps/data/routes/balances.ts +5 -1
- package/src/apps/data/routes/blocks.ts +9 -13
- package/src/apps/data/routes/earn-transactions.local.test.ts +6 -1
- package/src/apps/data/routes/earn-transactions.test.ts +7 -3
- package/src/apps/data/routes/earn.bench.ts +0 -14
- package/src/apps/data/routes/earn.discovery.test.ts +4 -39
- package/src/apps/data/routes/earn.local.test.ts +27 -34
- package/src/apps/data/routes/earn.test.ts +172 -490
- package/src/apps/data/routes/earn.ts +103 -652
- package/src/apps/data/routes/exchanges.ts +20 -30
- package/src/apps/data/routes/fee-amm.ts +12 -18
- package/src/apps/data/routes/indexer.test.ts +14 -12
- package/src/apps/data/routes/receipts.ts +1 -1
- package/src/apps/data/routes/rpc.test.ts +23 -18
- package/src/apps/data/routes/rpc.ts +21 -21
- package/src/apps/data/routes/rpc.zone.test.ts +60 -6
- package/src/apps/data/routes/tokenlist.ts +4 -6
- package/src/apps/data/routes/tokens.test.ts +20 -0
- package/src/apps/data/routes/tokens.ts +10 -5
- package/src/apps/data/routes/transactions.ts +3 -3
- package/src/apps/data/routes/transfers.ts +4 -6
- package/src/apps/data/routes/valuation.test.ts +2 -4
- package/src/apps/data/routes/valuation.ts +4 -6
- package/src/apps/data/routes/webhooks.test.ts +1 -5
- package/src/apps/data/routes/webhooks.ts +30 -44
- package/src/apps/data/routes/zones.test.ts +35 -19
- package/src/apps/management/App.ts +3 -1
- package/src/apps/management/routes/api-keys.test.ts +112 -22
- package/src/apps/management/routes/api-keys.ts +30 -15
- package/src/apps/management/routes/billing.ts +17 -25
- package/src/apps/management/routes/cli.ts +12 -18
- package/src/apps/management/routes/fee-payer-usage.ts +5 -4
- package/src/apps/management/routes/invitations.ts +16 -21
- package/src/apps/management/routes/invite-links.ts +18 -26
- package/src/apps/management/routes/me.ts +9 -13
- package/src/apps/management/routes/members.ts +5 -7
- package/src/apps/management/routes/orgs.ts +8 -12
- package/src/apps/management/routes/projects.ts +8 -12
- package/src/apps/management/routes/route-usage.ts +5 -4
- package/src/apps/management/routes/routes-access-requests.test.ts +64 -2
- package/src/apps/management/routes/routes-access-requests.ts +99 -15
- package/src/apps/management/routes/scopes.test.ts +3 -3
- package/src/apps/management/routes/usage.ts +20 -28
- package/src/apps/management/routes/verified-token-requests.ts +9 -13
- package/src/apps/management/routes/zone-delegations.test.ts +3 -5
- package/src/apps/management/routes/zone-delegations.ts +4 -9
- package/src/apps/mpp/App.ts +3 -3
- package/src/apps/mpp/Mpp.zone.test.ts +49 -18
- package/src/apps/mpp/internal/Adapter.ts +1 -1
- package/src/apps/mpp/internal/Error.test.ts +3 -0
- package/src/apps/mpp/internal/Error.ts +1 -0
- package/src/apps/mpp/internal/Policy.test.ts +9 -8
- package/src/apps/mpp/internal/Policy.ts +37 -14
- package/src/apps/relay/App.sandbox.test.ts +15 -14
- package/src/apps/relay/App.test.ts +120 -61
- package/src/apps/relay/App.ts +28 -18
- package/src/apps/relay/App.zone-billing.test.ts +9 -4
- package/src/apps/routes/App.test.ts +21 -22
- package/src/db/Db.ts +34 -3
- package/src/db/Schema.ts +26 -26
- package/src/db/tables/rewardRuns.ts +39 -0
- package/src/db/tables/routesAccessRequests.test.ts +65 -0
- package/src/db/tables/routesAccessRequests.ts +25 -0
- package/src/db/tables/{zoneKeyRequests.test.ts → zoneDelegationRequests.test.ts} +36 -77
- package/src/db/tables/{zoneKeyRequests.ts → zoneDelegationRequests.ts} +69 -52
- package/src/db/tables/zoneDelegations.test.ts +5 -6
- package/src/db/tables/zoneDelegations.ts +25 -8
- package/src/handlers/internal/{multisig.store.test.ts → accounts.store.test.ts} +16 -16
- package/src/handlers/internal/{multisig.test.ts → accounts.test.ts} +85 -82
- package/src/handlers/internal/{multisig.ts → accounts.ts} +9 -9
- package/src/handlers/internal/sponsorship.ts +20 -20
- package/src/handlers/relay.test.ts +48 -46
- package/src/handlers/relay.ts +26 -26
- package/src/internal/Auth.test.ts +6 -2
- package/src/internal/Auth.ts +4 -10
- package/src/internal/EarnRates.ts +4 -6
- package/src/internal/EarnVaults.ts +2 -2
- package/src/internal/Log.test-d.ts +1 -0
- package/src/internal/Log.test.ts +4 -6
- package/src/internal/Log.ts +2 -6
- package/src/internal/Response.ts +0 -3
- package/src/internal/Schema.test.ts +42 -0
- package/src/internal/Schema.ts +36 -1
- package/src/internal/ZoneAccess.test.ts +515 -312
- package/src/internal/ZoneAccess.ts +410 -158
- package/src/internal/ZoneDelegation.test.ts +1 -41
- package/src/internal/ZoneDelegation.ts +10 -48
- package/src/internal/ZoneDelegationApproval.test.ts +22 -0
- package/src/internal/{ZoneKeyApproval.ts → ZoneDelegationApproval.ts} +4 -11
- package/src/internal/ZoneKeyRewrite.ts +231 -0
- package/src/internal/rewards/Campaigns.ts +2 -0
- package/src/internal/rewards/Distributions.ts +8 -12
- package/src/internal/rewards/Monitor.test.ts +64 -0
- package/src/internal/rewards/Monitor.ts +27 -4
- package/src/internal/rewards/Runner.ts +9 -1
- package/src/internal/routes/Action.ts +5 -6
- package/src/internal/routes/Deposit.ts +8 -12
- package/src/internal/routes/DepositAddress.ts +13 -19
- package/src/internal/routes/Policy.ts +2 -1
- package/src/internal/routes/Provider.ts +8 -8
- package/src/internal/routes/Reconciliation.ts +3 -2
- package/src/internal/routes/Transfer.ts +16 -24
- package/src/internal/routes/UniversalDepositAddress.ts +9 -12
- package/src/internal/routes/providers/SquidOrder.ts +3 -2
- package/src/internal/routes/providers/relay.ts +4 -3
- package/dist/admin/apps/zone-key-requests.d.ts +0 -406
- package/dist/admin/apps/zone-key-requests.d.ts.map +0 -1
- package/dist/admin/apps/zone-key-requests.js +0 -183
- package/dist/admin/apps/zone-key-requests.js.map +0 -1
- package/dist/db/tables/zoneKeyRequests.d.ts +0 -289
- package/dist/db/tables/zoneKeyRequests.d.ts.map +0 -1
- package/dist/db/tables/zoneKeyRequests.js.map +0 -1
- package/dist/handlers/internal/multisig.d.ts +0 -7
- package/dist/handlers/internal/multisig.d.ts.map +0 -1
- package/dist/internal/ZoneKeyApproval.d.ts.map +0 -1
- package/dist/internal/ZoneKeyApproval.js +0 -21
- package/dist/internal/ZoneKeyApproval.js.map +0 -1
- package/src/admin/apps/zone-key-requests.test.ts +0 -272
- package/src/admin/apps/zone-key-requests.ts +0 -231
- package/src/internal/ZoneKeyApproval.test.ts +0 -46
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import type { Context } from 'hono'
|
|
2
|
+
import { HTTPException } from 'hono/http-exception'
|
|
2
3
|
|
|
3
4
|
import * as Db from '../db/Db.js'
|
|
4
5
|
import * as ZoneDelegations from '../db/tables/zoneDelegations.js'
|
|
5
6
|
import type * as Metrics from '../Metrics.js'
|
|
6
|
-
import * as Scope from '../Scope.js'
|
|
7
7
|
import * as Auth from './Auth.js'
|
|
8
8
|
import * as ExecutionContext from './ExecutionContext.js'
|
|
9
|
+
import * as Response from './Response.js'
|
|
9
10
|
import * as Store from './Store.js'
|
|
10
11
|
import * as Timeout from './Timeout.js'
|
|
12
|
+
import * as Timing from './Timing.js'
|
|
11
13
|
import * as ZoneDelegation from './ZoneDelegation.js'
|
|
12
14
|
|
|
13
|
-
/** Hono variables written by {@link
|
|
15
|
+
/** Hono variables written by {@link ensure}, {@link authorize}, and {@link deny}, read by the request logger. */
|
|
14
16
|
export type Variables = {
|
|
15
17
|
/** Zone access decision for the Zone the request is about, recorded even when denied. */
|
|
16
18
|
zoneAccess?: Access | undefined
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
+
/** Every decision {@link authorize} recorded for this request, metered once by {@link settle}. */
|
|
20
|
+
zoneAccessDecisions?: Access[] | undefined
|
|
21
|
+
/** The caller organization's active delegations once {@link ensure} settled; absent or failed denies every Zone read. */
|
|
22
|
+
zoneDelegations?: Delegations | undefined
|
|
23
|
+
/** The delegation read {@link ensure} started for this request, awaited by every later caller. */
|
|
24
|
+
zoneDelegationsPending?: Promise<Delegations> | undefined
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
/** Hono environment required by {@link check}, {@link authorize}, and {@link settle}. */
|
|
27
|
+
/** Hono environment required by {@link ensure}, {@link check}, {@link authorize}, and {@link settle}. */
|
|
22
28
|
export type Environment = {
|
|
23
29
|
Variables: Auth.Variables &
|
|
30
|
+
Timing.Variables &
|
|
24
31
|
Variables & {
|
|
25
32
|
/** Authoritative store holding Zone delegations. */
|
|
26
33
|
db: Db.Source
|
|
@@ -28,74 +35,136 @@ export type Environment = {
|
|
|
28
35
|
metrics: Metrics.Metrics | undefined
|
|
29
36
|
/** Cache store for memoized delegation reads. */
|
|
30
37
|
store: Store.Store
|
|
38
|
+
/** Hosted Zones keyed by chain id; no Zones means no delegation reads. */
|
|
39
|
+
zones: ReadonlyMap<number, unknown>
|
|
31
40
|
}
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
/** Bounded counter of Zone decisions by Zone,
|
|
43
|
+
/** Bounded counter of Zone decisions by Zone, outcome, and grant. */
|
|
35
44
|
export const decisionMetric = 'zone_access_decision_count'
|
|
36
45
|
|
|
37
|
-
/** Counter of delegation reads
|
|
46
|
+
/** Counter of failed delegation reads: `phase="request"` refused a request with a `503`, `phase="refresh"` was a background refresh that left the cached copy serving. */
|
|
38
47
|
export const lookupFailureMetric = 'zone_delegation_lookup_failure_count'
|
|
39
48
|
|
|
40
|
-
/**
|
|
49
|
+
/** Age after which a cached delegation read is refreshed in the background while still answering. */
|
|
41
50
|
export const delegationTtl = 30_000
|
|
42
51
|
|
|
43
|
-
/**
|
|
44
|
-
export const
|
|
52
|
+
/** Oldest cached delegation read that may answer; beyond it the request reads Postgres. Bounds revocation latency per colo. */
|
|
53
|
+
export const delegationMaxAge = 60_000
|
|
45
54
|
|
|
46
|
-
/** Longest
|
|
47
|
-
export const
|
|
55
|
+
/** Longest a delegation read (cache and Postgres together) may run before it fails. */
|
|
56
|
+
export const delegationTimeout = 5_000
|
|
48
57
|
|
|
49
|
-
/** Operation a Zone request performs; a write scope never reads and a
|
|
58
|
+
/** Operation a Zone request performs; a write scope never reads and a delegation never broadcasts. */
|
|
50
59
|
export type Verb = 'read' | 'write'
|
|
51
60
|
|
|
52
61
|
/** How a Zone request was admitted. */
|
|
53
|
-
export type Grant = '
|
|
62
|
+
export type Grant = 'delegation' | 'scope' | 'zone_token'
|
|
63
|
+
|
|
64
|
+
/** The caller organization's active delegations as {@link ensure} left them. */
|
|
65
|
+
export type Delegations =
|
|
66
|
+
| {
|
|
67
|
+
/** Active delegations across the organization's Zones in the key's environment. */
|
|
68
|
+
records: readonly ZoneDelegations.Record[]
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
/** The read failed or timed out with no usable cached copy, so every delegated Zone read is unavailable. */
|
|
72
|
+
failed: true
|
|
73
|
+
}
|
|
54
74
|
|
|
55
75
|
/** Recorded Zone access decision. */
|
|
56
76
|
export type Access = {
|
|
57
77
|
/** Lowercase account addresses the request is about; empty when not account-scoped. */
|
|
58
78
|
accounts: readonly string[]
|
|
59
|
-
/** Whether the delegation decision agrees with the scope decision; absent until {@link settle} compares them. */
|
|
60
|
-
agreement?: boolean | undefined
|
|
61
79
|
/** Zone chain id. */
|
|
62
80
|
chainId: number
|
|
63
|
-
/** Delegation decision
|
|
81
|
+
/** Delegation decision for a read the key was otherwise eligible for; absent for writes, tokens, and ineligible keys. */
|
|
64
82
|
delegation?: ZoneDelegation.Result | undefined
|
|
65
83
|
/** Grant that admitted the request; absent when denied, including by a later check such as quota or sandbox. */
|
|
66
84
|
grant?: Grant | undefined
|
|
67
|
-
/** Grant the scope decision produced, kept through {@link deny} because delegations replace scopes only. */
|
|
68
|
-
scopeGrant?: Grant | undefined
|
|
69
85
|
/** Lowercase token addresses the request is about; empty when not token-scoped. */
|
|
70
86
|
tokens: readonly string[]
|
|
71
87
|
/** Requested operation. */
|
|
72
88
|
verb: Verb
|
|
73
89
|
}
|
|
74
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Loads the caller organization's active delegations on first demand and
|
|
93
|
+
* shares the read across the request, for API keys that can read or broadcast
|
|
94
|
+
* to a Zone at all. Call it before {@link check} or {@link eligible} decide;
|
|
95
|
+
* requests never touching a Zone pay nothing. Client cache directives are
|
|
96
|
+
* ignored. A read Postgres cannot answer refuses delegated Zone access with a
|
|
97
|
+
* retriable `503`.
|
|
98
|
+
*/
|
|
99
|
+
export function ensure<environment extends Environment>(
|
|
100
|
+
c: Context<environment>,
|
|
101
|
+
): Promise<Delegations | undefined> {
|
|
102
|
+
const pending = c.get('zoneDelegationsPending' as never) as Promise<Delegations> | undefined
|
|
103
|
+
if (pending) return pending
|
|
104
|
+
const principal = Auth.getPrincipal(c)
|
|
105
|
+
if (principal?.type !== 'api_key' || !zoneCapable(principal)) return Promise.resolve(undefined)
|
|
106
|
+
const zones = c.get('zones' as never) as ReadonlyMap<number, unknown> | undefined
|
|
107
|
+
if (!zones?.size) return Promise.resolve(undefined)
|
|
108
|
+
const owner = { environment: principal.environment, orgId: principal.orgId }
|
|
109
|
+
const started = Timing.time(c, 'zone_delegations', () => Cached.read(c, owner)).then(
|
|
110
|
+
(records): Delegations => ({ records }),
|
|
111
|
+
(): Delegations => {
|
|
112
|
+
metrics(c)?.count(lookupFailureMetric, 1, { phase: 'request' })
|
|
113
|
+
return { failed: true }
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
void started.then((delegations) => c.set('zoneDelegations' as never, delegations as never))
|
|
117
|
+
c.set('zoneDelegationsPending' as never, started as never)
|
|
118
|
+
return started
|
|
119
|
+
}
|
|
120
|
+
|
|
75
121
|
/**
|
|
76
122
|
* Decides whether the caller may access one Zone without recording anything.
|
|
77
|
-
* Filters narrowing a Zone list use this
|
|
78
|
-
* about uses {@link authorize}.
|
|
123
|
+
* Filters narrowing a Zone list use this after awaiting {@link ensure}; the gate
|
|
124
|
+
* for the Zone a request is about uses {@link authorize}. Without the request's
|
|
125
|
+
* addresses, only a delegation covering every account and token admits a read.
|
|
79
126
|
*/
|
|
80
127
|
export function check<environment extends Environment>(
|
|
81
128
|
c: Context<environment>,
|
|
82
129
|
options: check.Options,
|
|
83
130
|
): check.Result {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
131
|
+
const gated = gate(c, options)
|
|
132
|
+
if (!('eligible' in gated)) return gated
|
|
133
|
+
const delegation = ZoneDelegation.decide({
|
|
134
|
+
accounts: [],
|
|
135
|
+
delegation: coverage(gated.record, { accounts: [], tokens: [] }),
|
|
136
|
+
tokens: [],
|
|
137
|
+
})
|
|
138
|
+
return conclude(options, delegation)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Whether the caller could read one Zone given a covering request: the key is
|
|
143
|
+
* tagged and scoped for it and its organization holds an active delegation.
|
|
144
|
+
* Selection filters use this before {@link authorize} decides each Zone with
|
|
145
|
+
* the request's addresses; an unreadable delegation counts, so `authorize` answers `503`.
|
|
146
|
+
*/
|
|
147
|
+
export function eligible<environment extends Environment>(
|
|
148
|
+
c: Context<environment>,
|
|
149
|
+
options: Pick<check.Options, 'chainId'>,
|
|
150
|
+
): boolean {
|
|
151
|
+
const gated = gate(c, { chainId: options.chainId, verb: 'read' })
|
|
152
|
+
return 'eligible' in gated || gated.allowed || gated.error.status === 503
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Whether a listing may include one Zone the caller reads, after awaiting
|
|
157
|
+
* {@link ensure}. Throws a `503` when the delegations could not be read, so a
|
|
158
|
+
* listing never silently omits Zones it could not verify.
|
|
159
|
+
*/
|
|
160
|
+
export function visible<environment extends Environment>(
|
|
161
|
+
c: Context<environment>,
|
|
162
|
+
options: Pick<check.Options, 'chainId'>,
|
|
163
|
+
): boolean {
|
|
164
|
+
const result = check(c, { chainId: options.chainId, verb: 'read' })
|
|
165
|
+
if (!result.allowed && result.error.status === 503)
|
|
166
|
+
throw new HTTPException(503, { res: Response.error(c, result.error) })
|
|
167
|
+
return result.allowed
|
|
99
168
|
}
|
|
100
169
|
|
|
101
170
|
export declare namespace check {
|
|
@@ -126,42 +195,65 @@ export declare namespace check {
|
|
|
126
195
|
error: Denial
|
|
127
196
|
}
|
|
128
197
|
|
|
129
|
-
/** Denial error envelope fields. */
|
|
130
|
-
type Denial =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
198
|
+
/** Denial error envelope fields: a missing grant, or delegations that could not be read. */
|
|
199
|
+
type Denial =
|
|
200
|
+
| {
|
|
201
|
+
/** Stable error code. */
|
|
202
|
+
code: 'api_key_forbidden'
|
|
203
|
+
/** Corrective guidance naming the missing grant. */
|
|
204
|
+
message: string
|
|
205
|
+
/** HTTP status. */
|
|
206
|
+
status: 403
|
|
207
|
+
}
|
|
208
|
+
| {
|
|
209
|
+
/** Stable error code. */
|
|
210
|
+
code: 'zone_access_unavailable'
|
|
211
|
+
/** Guidance to retry. */
|
|
212
|
+
message: string
|
|
213
|
+
/** HTTP status. */
|
|
214
|
+
status: 503
|
|
215
|
+
}
|
|
138
216
|
}
|
|
139
217
|
|
|
140
218
|
/**
|
|
141
219
|
* Decides whether the caller may access the Zone the request is about and
|
|
142
|
-
* records the decision on the context for the request log.
|
|
143
|
-
*
|
|
144
|
-
*
|
|
220
|
+
* records the decision on the context for the request log. A read loads the
|
|
221
|
+
* delegations, then checks the requested accounts and tokens against the
|
|
222
|
+
* delegation's members; a failed read refuses the request with a retriable `503`.
|
|
145
223
|
*/
|
|
146
|
-
export function authorize<environment extends Environment>(
|
|
224
|
+
export async function authorize<environment extends Environment>(
|
|
147
225
|
c: Context<environment>,
|
|
148
226
|
options: authorize.Options,
|
|
149
|
-
): authorize.Result {
|
|
150
|
-
const
|
|
227
|
+
): Promise<authorize.Result> {
|
|
228
|
+
const addresses = { accounts: normalize(options.accounts), tokens: normalize(options.tokens) }
|
|
229
|
+
// A disconnect while the delegation or member reads are pending logs after the decision, so denials stay unbilled.
|
|
230
|
+
const { delegation, result } = await Auth.check(c, {
|
|
231
|
+
allows: (decided) => decided.result.allowed,
|
|
232
|
+
fn: async () => {
|
|
233
|
+
// Generic writes need the delegation too; a forwarded Zone token never does.
|
|
234
|
+
if (!options.zoneToken) await ensure(c)
|
|
235
|
+
const gated = gate(c, options)
|
|
236
|
+
if (!('eligible' in gated)) return { delegation: undefined, result: gated }
|
|
237
|
+
const delegation = await evaluate(c, gated, addresses)
|
|
238
|
+
if (delegation) return { delegation, result: conclude(options, delegation) }
|
|
239
|
+
const denied: ZoneDelegation.Result = { decision: 'deny', id: gated.record.id }
|
|
240
|
+
return { delegation: denied, result: unavailable(options) }
|
|
241
|
+
},
|
|
242
|
+
})
|
|
151
243
|
const access: Access = {
|
|
152
|
-
|
|
244
|
+
...addresses,
|
|
153
245
|
chainId: options.chainId,
|
|
154
|
-
...(
|
|
155
|
-
|
|
246
|
+
...(delegation === undefined ? {} : { delegation }),
|
|
247
|
+
...(result.allowed ? { grant: result.grant } : {}),
|
|
156
248
|
verb: options.verb,
|
|
157
249
|
}
|
|
158
250
|
c.set('zoneAccess' as never, access as never)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
c.set('
|
|
251
|
+
// A later decision for the same Zone (raw RPC per method, relay per item) supersedes the earlier one.
|
|
252
|
+
const decisions = (c.get('zoneAccessDecisions' as never) as Access[] | undefined) ?? []
|
|
253
|
+
const index = decisions.findIndex((decision) => decision.chainId === access.chainId)
|
|
254
|
+
if (index === -1) decisions.push(access)
|
|
255
|
+
else decisions[index] = access
|
|
256
|
+
c.set('zoneAccessDecisions' as never, decisions as never)
|
|
165
257
|
return result
|
|
166
258
|
}
|
|
167
259
|
|
|
@@ -184,117 +276,174 @@ export function deny<environment extends Environment>(c: Context<environment>) {
|
|
|
184
276
|
if (access?.grant === undefined) return
|
|
185
277
|
const { grant: _grant, ...denied } = access
|
|
186
278
|
c.set('zoneAccess' as never, denied as never)
|
|
279
|
+
const decisions = c.get('zoneAccessDecisions' as never) as Access[] | undefined
|
|
280
|
+
if (decisions) {
|
|
281
|
+
const index = decisions.indexOf(access)
|
|
282
|
+
if (index !== -1) decisions[index] = denied
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Meters every Zone decision recorded for the request once the handler finished, after later refusals applied. */
|
|
287
|
+
export function settle<environment extends Environment>(c: Context<environment>): void {
|
|
288
|
+
const decisions = c.get('zoneAccessDecisions' as never) as Access[] | undefined
|
|
289
|
+
// A disconnected client already emitted its log entry; its decisions were never final.
|
|
290
|
+
if (!decisions?.length || c.req.raw.signal.aborted) return
|
|
291
|
+
for (const access of decisions)
|
|
292
|
+
metrics(c)?.count(decisionMetric, 1, {
|
|
293
|
+
chain_id: access.chainId,
|
|
294
|
+
grant: access.grant ?? 'none',
|
|
295
|
+
outcome: access.grant === undefined ? 'deny' : 'allow',
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** A read the key may perform if its organization's delegation covers the request. */
|
|
300
|
+
type Eligible = {
|
|
301
|
+
/** Marks the gate outcome as awaiting the delegation decision. */
|
|
302
|
+
eligible: true
|
|
303
|
+
/** The organization's active delegation for the Zone. */
|
|
304
|
+
record: ZoneDelegations.Record
|
|
187
305
|
}
|
|
188
306
|
|
|
189
307
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* (quota, method allowlist, sandbox, workload identity) stay in place when
|
|
194
|
-
* delegations replace scopes, so they are not disagreements. Admission is
|
|
195
|
-
* already decided; a failed delegation read only leaves the comparison out.
|
|
308
|
+
* Applies the credential checks that need no addresses: a forwarded Zone token
|
|
309
|
+
* outranks everything, writes stay on `zone:<chainId>:write`, and a read needs
|
|
310
|
+
* `zone:read`, a tag naming the Zone, and an active delegation. `*` grants no Zone.
|
|
196
311
|
*/
|
|
197
|
-
|
|
312
|
+
function gate<environment extends Environment>(
|
|
198
313
|
c: Context<environment>,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
pending.then(({ value }) => {
|
|
223
|
-
if (value === undefined) metrics?.count(lookupFailureMetric, 1)
|
|
224
|
-
}),
|
|
225
|
-
)
|
|
226
|
-
metrics?.count(decisionMetric, 1, { ...tags, agreement: 'pending' })
|
|
227
|
-
return
|
|
228
|
-
}
|
|
229
|
-
const delegation = outcome.value
|
|
230
|
-
if (!delegation) {
|
|
231
|
-
metrics?.count(lookupFailureMetric, 1)
|
|
232
|
-
metrics?.count(decisionMetric, 1, { ...tags, agreement: 'unavailable' })
|
|
233
|
-
return
|
|
314
|
+
options: check.Options,
|
|
315
|
+
): check.Result | Eligible {
|
|
316
|
+
const { chainId, verb, zoneToken = false } = options
|
|
317
|
+
const principal = Auth.getPrincipal(c)
|
|
318
|
+
if (zoneToken) return { allowed: true, grant: 'zone_token' }
|
|
319
|
+
const refuse = (): check.Result => ({
|
|
320
|
+
allowed: false,
|
|
321
|
+
error: { code: 'api_key_forbidden', message: denialMessage(options), status: 403 },
|
|
322
|
+
})
|
|
323
|
+
if (principal?.type !== 'api_key') return refuse()
|
|
324
|
+
const { scopes } = principal.apiKey
|
|
325
|
+
// Absent tags mean no Zone, so a later delegation never widens an existing key.
|
|
326
|
+
const tagged = principal.zoneTags?.includes(chainId) ?? false
|
|
327
|
+
if (verb === 'write') {
|
|
328
|
+
// A stored per-Zone write scope keeps admitting its own Zone this release, exactly as before,
|
|
329
|
+
// until the write rewrite has run and the cleanup release strips it.
|
|
330
|
+
if (scopes.includes(`zone:${chainId}:write`)) return { allowed: true, grant: 'scope' }
|
|
331
|
+
// Broadcasts are account-signed, so the delegation's existence is the whole bound: revoking it
|
|
332
|
+
// stops the key, accounts and tokens are never checked, and the wildcard grants no Zone.
|
|
333
|
+
if (!scopes.includes('zone:write') || !tagged) return refuse()
|
|
334
|
+
if (lookupFailed(c)) return unavailable(options)
|
|
335
|
+
if (!delegationFor(c, chainId)) return refuse()
|
|
336
|
+
return { allowed: true, grant: 'scope' }
|
|
234
337
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
338
|
+
if (!readable(principal) || !tagged) return refuse()
|
|
339
|
+
// Only keys a delegation could admit learn the read failed; others keep their 403.
|
|
340
|
+
if (lookupFailed(c)) return unavailable(options)
|
|
341
|
+
const record = delegationFor(c, chainId)
|
|
342
|
+
if (!record) return refuse()
|
|
343
|
+
return { eligible: true, record }
|
|
238
344
|
}
|
|
239
345
|
|
|
240
|
-
/**
|
|
241
|
-
|
|
242
|
-
* undefined when no delegation applies: writes, anonymous and session callers
|
|
243
|
-
* (no organization), and forwarded Zone tokens (the operative credential).
|
|
244
|
-
*/
|
|
245
|
-
function delegationScope<environment extends Environment>(
|
|
346
|
+
/** The organization's active delegation for one Zone; undefined when none, or when `ensure` never ran or failed, so callers fail closed. */
|
|
347
|
+
function delegationFor<environment extends Environment>(
|
|
246
348
|
c: Context<environment>,
|
|
247
|
-
|
|
248
|
-
): ZoneDelegations.
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
349
|
+
chainId: number,
|
|
350
|
+
): ZoneDelegations.Record | undefined {
|
|
351
|
+
const delegations = c.get('zoneDelegations' as never) as Delegations | undefined
|
|
352
|
+
if (!delegations || 'failed' in delegations) return undefined
|
|
353
|
+
return delegations.records.find((candidate) => candidate.chainId === chainId)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** Whether {@link ensure} ran and its delegation read failed with no recent copy to answer from. */
|
|
357
|
+
function lookupFailed<environment extends Environment>(c: Context<environment>): boolean {
|
|
358
|
+
const delegations = c.get('zoneDelegations' as never) as Delegations | undefined
|
|
359
|
+
return delegations !== undefined && 'failed' in delegations
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Refusal for a key a delegation might admit when the delegations could not be read. */
|
|
363
|
+
function unavailable(options: check.Options): check.Result {
|
|
364
|
+
return {
|
|
365
|
+
allowed: false,
|
|
366
|
+
error: {
|
|
367
|
+
code: 'zone_access_unavailable',
|
|
368
|
+
message: `Zone ${options.chainId} access could not be verified. Retry the request.`,
|
|
369
|
+
status: 503,
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Whether the key's scopes and tags let it read any Zone at all: `zone:read` plus at least one tag. */
|
|
375
|
+
function readable(principal: Extract<Auth.Principal, { type: 'api_key' }>): boolean {
|
|
376
|
+
return principal.apiKey.scopes.includes('zone:read') && (principal.zoneTags?.length ?? 0) > 0
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Whether the key's scopes and tags let it use any Zone at all, reading or broadcasting. */
|
|
380
|
+
function zoneCapable(principal: Extract<Auth.Principal, { type: 'api_key' }>): boolean {
|
|
381
|
+
const { scopes } = principal.apiKey
|
|
382
|
+
return (
|
|
383
|
+
(scopes.includes('zone:read') || scopes.includes('zone:write')) &&
|
|
384
|
+
(principal.zoneTags?.length ?? 0) > 0
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** Maps a delegation decision onto the gate result for the requested Zone. */
|
|
389
|
+
function conclude(options: check.Options, delegation: ZoneDelegation.Result): check.Result {
|
|
390
|
+
if (delegation.decision === 'allow') return { allowed: true, grant: 'delegation' }
|
|
391
|
+
return {
|
|
392
|
+
allowed: false,
|
|
393
|
+
error: { code: 'api_key_forbidden', message: denialMessage(options), status: 403 },
|
|
394
|
+
}
|
|
254
395
|
}
|
|
255
396
|
|
|
256
397
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* Client cache directives are ignored throughout. Rejections surface in {@link settle}.
|
|
398
|
+
* Decides an eligible read against the delegation's members: the requested
|
|
399
|
+
* accounts and tokens are checked for membership in one statement each, cached
|
|
400
|
+
* per address, so a request never loads a whole member set. Returns undefined
|
|
401
|
+
* when the member read failed, which the caller refuses as unavailable.
|
|
262
402
|
*/
|
|
263
403
|
async function evaluate<environment extends Environment>(
|
|
264
404
|
c: Context<environment>,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
): Promise<ZoneDelegation.Result> {
|
|
268
|
-
const
|
|
405
|
+
eligible: Eligible,
|
|
406
|
+
addresses: Addresses,
|
|
407
|
+
): Promise<ZoneDelegation.Result | undefined> {
|
|
408
|
+
const { record } = eligible
|
|
409
|
+
const decide = (delegation: ZoneDelegation.Delegation) =>
|
|
410
|
+
ZoneDelegation.decide({ ...addresses, delegation })
|
|
411
|
+
const listed = { accounts: !record.accountsAll, tokens: !record.tokensAll }
|
|
412
|
+
if (
|
|
413
|
+
(!listed.accounts || !addresses.accounts.length) &&
|
|
414
|
+
(!listed.tokens || !addresses.tokens.length)
|
|
415
|
+
)
|
|
416
|
+
return decide(coverage(record, { accounts: [], tokens: [] }))
|
|
269
417
|
const db = Db.get(c.get('db' as never) as Db.Source)
|
|
270
418
|
const store = Store.authoritative(c.get('store' as never) as Store.Store)
|
|
271
|
-
const records = await Store.memoize(() => ZoneDelegations.listActive(db, scope), {
|
|
272
|
-
flightTimeout: delegationTimeout,
|
|
273
|
-
key: `zone_delegations:v1:${scope.orgId}:${scope.environment}`,
|
|
274
|
-
store,
|
|
275
|
-
ttl: delegationTtl,
|
|
276
|
-
})
|
|
277
|
-
const record = records.find((candidate) => candidate.chainId === access.chainId)
|
|
278
|
-
const grant = access.scopeGrant
|
|
279
|
-
const decide = (delegation?: ZoneDelegation.Delegation) =>
|
|
280
|
-
ZoneDelegation.decide({
|
|
281
|
-
accounts: access.accounts,
|
|
282
|
-
delegation,
|
|
283
|
-
grant,
|
|
284
|
-
projectId: principal?.type === 'api_key' ? principal.projectId : undefined,
|
|
285
|
-
tokens: access.tokens,
|
|
286
|
-
})
|
|
287
|
-
// A key without its own grant is denied before coverage matters, so its members are never read.
|
|
288
|
-
if (!record || grant === undefined)
|
|
289
|
-
return decide(record && { accounts: [], id: record.id, projects: record.projects, tokens: [] })
|
|
290
419
|
const wiring = { c, db, id: record.id, store }
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
? ('
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
420
|
+
try {
|
|
421
|
+
const [accounts, tokens] = await Promise.all([
|
|
422
|
+
listed.accounts ? covered({ ...wiring, addresses: addresses.accounts, kind: 'account' }) : [],
|
|
423
|
+
listed.tokens ? covered({ ...wiring, addresses: addresses.tokens, kind: 'token' }) : [],
|
|
424
|
+
])
|
|
425
|
+
return decide(coverage(record, { accounts, tokens }))
|
|
426
|
+
} catch {
|
|
427
|
+
metrics(c)?.count(lookupFailureMetric, 1, { phase: 'request' })
|
|
428
|
+
return undefined
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Builds the gate's view of a delegation from its row and the members already resolved. */
|
|
433
|
+
function coverage(record: ZoneDelegations.Record, members: Addresses): ZoneDelegation.Delegation {
|
|
434
|
+
return {
|
|
435
|
+
accounts: record.accountsAll ? 'all' : members.accounts,
|
|
436
|
+
id: record.id,
|
|
437
|
+
tokens: record.tokensAll ? 'all' : members.tokens,
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** Lowercase accounts and tokens a request names. */
|
|
442
|
+
type Addresses = {
|
|
443
|
+
/** Account addresses. */
|
|
444
|
+
accounts: readonly string[]
|
|
445
|
+
/** Token contract addresses. */
|
|
446
|
+
tokens: readonly string[]
|
|
298
447
|
}
|
|
299
448
|
|
|
300
449
|
/**
|
|
@@ -331,6 +480,106 @@ async function covered<environment extends Environment>(
|
|
|
331
480
|
return addresses.filter((address) => hits.get(address) === '1' || found.has(address))
|
|
332
481
|
}
|
|
333
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Per-colo delegation cache with stale-while-revalidate: entries younger than
|
|
485
|
+
* {@link delegationTtl} answer directly, older ones up to {@link delegationMaxAge}
|
|
486
|
+
* answer while a background read refreshes them, and anything older reads Postgres.
|
|
487
|
+
*/
|
|
488
|
+
namespace Cached {
|
|
489
|
+
type Entry = {
|
|
490
|
+
/** When Postgres returned the records, in epoch milliseconds. */
|
|
491
|
+
at: number
|
|
492
|
+
/** Active delegations at that time. */
|
|
493
|
+
records: readonly ZoneDelegations.Record[]
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Keys whose background refresh this isolate started, by start time, so concurrent requests start one. */
|
|
497
|
+
const refreshing = new Map<string, number>()
|
|
498
|
+
|
|
499
|
+
export async function read<environment extends Environment>(
|
|
500
|
+
c: Context<environment>,
|
|
501
|
+
owner: ZoneDelegations.Owner,
|
|
502
|
+
): Promise<readonly ZoneDelegations.Record[]> {
|
|
503
|
+
const deadline = performance.now() + delegationTimeout
|
|
504
|
+
const db = Db.get(c.get('db' as never) as Db.Source)
|
|
505
|
+
// Client cache directives never bypass or refresh authorization data.
|
|
506
|
+
const store = Store.authoritative(c.get('store' as never) as Store.Store)
|
|
507
|
+
// v2 entries carry their read time; v1 entries were bare records.
|
|
508
|
+
const key = `zone_delegations:v2:${owner.orgId}:${owner.environment}`
|
|
509
|
+
const entry = await Timing.time(c, 'zone_delegations_cache', () =>
|
|
510
|
+
Timeout.run(() => store.get(key), { timeout: delegationTimeout }).then(
|
|
511
|
+
decode,
|
|
512
|
+
() => undefined,
|
|
513
|
+
),
|
|
514
|
+
)
|
|
515
|
+
const age = entry ? Date.now() - entry.at : Infinity
|
|
516
|
+
const wiring = { db, key, owner, store, waitUntil: ExecutionContext.waitUntil(c) }
|
|
517
|
+
if (entry && age <= delegationMaxAge) {
|
|
518
|
+
if (age > delegationTtl) refresh(c, wiring)
|
|
519
|
+
return entry.records
|
|
520
|
+
}
|
|
521
|
+
// A cache read that spent the budget never starts a query the caller no longer awaits.
|
|
522
|
+
const remaining = deadline - performance.now()
|
|
523
|
+
if (remaining <= 0) throw new Error('Zone delegation read exceeded its budget.')
|
|
524
|
+
// Present on a failed request, this stage shows how long Postgres (through Hyperdrive) took.
|
|
525
|
+
return Timing.time(c, 'zone_delegations_query', () => load({ ...wiring, timeout: remaining }))
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
type Options = {
|
|
529
|
+
/** Database to read. */
|
|
530
|
+
db: Db.Db
|
|
531
|
+
/** Cache key. */
|
|
532
|
+
key: string
|
|
533
|
+
/** Organization and environment whose delegations are read. */
|
|
534
|
+
owner: ZoneDelegations.Owner
|
|
535
|
+
/** Colo cache store. */
|
|
536
|
+
store: Store.Store
|
|
537
|
+
/** Keeps background work alive after the response; absent outside Workers. */
|
|
538
|
+
waitUntil: ExecutionContext.waitUntil.ReturnType
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** Reads Postgres within the timeout and caches the result; cache writes are best-effort and never delay it. */
|
|
542
|
+
async function load(options: Options & { timeout: number }) {
|
|
543
|
+
const { db, key, owner, store, timeout, waitUntil } = options
|
|
544
|
+
const records = await Timeout.run(() => ZoneDelegations.listActive(db, owner), { timeout })
|
|
545
|
+
const entry: Entry = { at: Date.now(), records }
|
|
546
|
+
const persist = store
|
|
547
|
+
.put(key, JSON.stringify(entry), { ttl: delegationMaxAge })
|
|
548
|
+
.catch(() => undefined)
|
|
549
|
+
waitUntil?.(persist)
|
|
550
|
+
return records
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** Refreshes an aging entry after the response; a failure leaves the entry answering until it is too old. */
|
|
554
|
+
function refresh<environment extends Environment>(c: Context<environment>, options: Options) {
|
|
555
|
+
const started = refreshing.get(options.key)
|
|
556
|
+
if (started !== undefined && Date.now() - started < delegationTimeout) return
|
|
557
|
+
refreshing.set(options.key, Date.now())
|
|
558
|
+
const work = load({ ...options, timeout: delegationTimeout })
|
|
559
|
+
.catch(() => {
|
|
560
|
+
// The request's metrics already flushed, so a late count flushes itself.
|
|
561
|
+
metrics(c)?.count(lookupFailureMetric, 1, { phase: 'refresh' })
|
|
562
|
+
metrics(c)?.flush()
|
|
563
|
+
})
|
|
564
|
+
.finally(() => refreshing.delete(options.key))
|
|
565
|
+
options.waitUntil?.(work)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function decode(raw: string | null): Entry | undefined {
|
|
569
|
+
if (raw === null) return undefined
|
|
570
|
+
try {
|
|
571
|
+
const entry = JSON.parse(raw) as Entry
|
|
572
|
+
return typeof entry.at === 'number' && Array.isArray(entry.records) ? entry : undefined
|
|
573
|
+
} catch {
|
|
574
|
+
return undefined
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function metrics<environment extends Environment>(c: Context<environment>) {
|
|
580
|
+
return c.get('metrics' as never) as Metrics.Metrics | undefined
|
|
581
|
+
}
|
|
582
|
+
|
|
334
583
|
/** Lowercases addresses so logged accounts and tokens compare exactly. */
|
|
335
584
|
function normalize(addresses: readonly string[] | undefined): readonly string[] {
|
|
336
585
|
return addresses?.map((address) => address.toLowerCase()) ?? []
|
|
@@ -339,11 +588,14 @@ function normalize(addresses: readonly string[] | undefined): readonly string[]
|
|
|
339
588
|
/** Denial guidance per verb, keeping each route's published wording. */
|
|
340
589
|
function denialMessage(options: check.Options): string {
|
|
341
590
|
const { chainId, suggestZoneToken = false, verb } = options
|
|
342
|
-
if (verb === 'write')
|
|
591
|
+
if (verb === 'write') {
|
|
592
|
+
const grant = `Broadcasting to zone ${chainId} requires \`zone:write\` with a tag for Zone ${chainId}, issued to an organization delegated that Zone`
|
|
343
593
|
return suggestZoneToken
|
|
344
|
-
?
|
|
345
|
-
:
|
|
594
|
+
? `${grant}, or a Zone token via \`X-Authorization-Token\`.`
|
|
595
|
+
: `${grant}.`
|
|
596
|
+
}
|
|
597
|
+
const grant = `Use an API key granting \`zone:read\` and tagged for Zone ${chainId}, issued to an organization delegated that Zone`
|
|
346
598
|
return suggestZoneToken
|
|
347
|
-
? `Chain
|
|
348
|
-
: `Chain ID ${chainId} is a Zone.
|
|
599
|
+
? `Chain ID ${chainId} is a Zone. ${grant}, or pass a Zone token via \`X-Authorization-Token\`.`
|
|
600
|
+
: `Chain ID ${chainId} is a Zone. ${grant}.`
|
|
349
601
|
}
|