tapimo 0.0.0 → 0.0.2
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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/ApiKey.d.ts +65 -0
- package/dist/ApiKey.d.ts.map +1 -0
- package/dist/ApiKey.js +105 -0
- package/dist/ApiKey.js.map +1 -0
- package/dist/ApiKeySource.d.ts +166 -0
- package/dist/ApiKeySource.d.ts.map +1 -0
- package/dist/ApiKeySource.js +234 -0
- package/dist/ApiKeySource.js.map +1 -0
- package/dist/App.d.ts +19317 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +612 -0
- package/dist/App.js.map +1 -0
- package/dist/Assets.d.ts +44 -0
- package/dist/Assets.d.ts.map +1 -0
- package/dist/Assets.js +23 -0
- package/dist/Assets.js.map +1 -0
- package/dist/Auth.d.ts +2 -0
- package/dist/Auth.d.ts.map +1 -0
- package/dist/Auth.js +2 -0
- package/dist/Auth.js.map +1 -0
- package/dist/Cli.d.ts +21 -0
- package/dist/Cli.d.ts.map +1 -0
- package/dist/Cli.js +13 -0
- package/dist/Cli.js.map +1 -0
- package/dist/Client.d.ts +20 -0
- package/dist/Client.d.ts.map +1 -0
- package/dist/Client.js +24 -0
- package/dist/Client.js.map +1 -0
- package/dist/Metrics.d.ts +70 -0
- package/dist/Metrics.d.ts.map +1 -0
- package/dist/Metrics.js +91 -0
- package/dist/Metrics.js.map +1 -0
- package/dist/Scope.d.ts +35 -0
- package/dist/Scope.d.ts.map +1 -0
- package/dist/Scope.js +38 -0
- package/dist/Scope.js.map +1 -0
- package/dist/Webhooks.d.ts +79 -0
- package/dist/Webhooks.d.ts.map +1 -0
- package/dist/Webhooks.js +107 -0
- package/dist/Webhooks.js.map +1 -0
- package/dist/admin/Access.d.ts +41 -0
- package/dist/admin/Access.d.ts.map +1 -0
- package/dist/admin/Access.js +75 -0
- package/dist/admin/Access.js.map +1 -0
- package/dist/admin/App.d.ts +1221 -0
- package/dist/admin/App.d.ts.map +1 -0
- package/dist/admin/App.js +113 -0
- package/dist/admin/App.js.map +1 -0
- package/dist/admin/apps/api-keys.d.ts +209 -0
- package/dist/admin/apps/api-keys.d.ts.map +1 -0
- package/dist/admin/apps/api-keys.js +129 -0
- package/dist/admin/apps/api-keys.js.map +1 -0
- package/dist/admin/apps/scopes.d.ts +46 -0
- package/dist/admin/apps/scopes.d.ts.map +1 -0
- package/dist/admin/apps/scopes.js +41 -0
- package/dist/admin/apps/scopes.js.map +1 -0
- package/dist/admin/apps/verified-tokens.d.ts +1206 -0
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -0
- package/dist/admin/apps/verified-tokens.js +485 -0
- package/dist/admin/apps/verified-tokens.js.map +1 -0
- package/dist/admin/index.d.ts +3 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/ui.generated.d.ts +11 -0
- package/dist/admin/ui.generated.d.ts.map +1 -0
- package/dist/admin/ui.generated.js +43 -0
- package/dist/admin/ui.generated.js.map +1 -0
- package/dist/apps/activities.d.ts +18725 -0
- package/dist/apps/activities.d.ts.map +1 -0
- package/dist/apps/activities.js +3138 -0
- package/dist/apps/activities.js.map +1 -0
- package/dist/apps/addresses.d.ts +3662 -0
- package/dist/apps/addresses.d.ts.map +1 -0
- package/dist/apps/addresses.js +22 -0
- package/dist/apps/addresses.js.map +1 -0
- package/dist/apps/balances.d.ts +342 -0
- package/dist/apps/balances.d.ts.map +1 -0
- package/dist/apps/balances.js +393 -0
- package/dist/apps/balances.js.map +1 -0
- package/dist/apps/blocks.d.ts +931 -0
- package/dist/apps/blocks.d.ts.map +1 -0
- package/dist/apps/blocks.js +689 -0
- package/dist/apps/blocks.js.map +1 -0
- package/dist/apps/coingecko.d.ts +1053 -0
- package/dist/apps/coingecko.d.ts.map +1 -0
- package/dist/apps/coingecko.js +1015 -0
- package/dist/apps/coingecko.js.map +1 -0
- package/dist/apps/exchanges.d.ts +3000 -0
- package/dist/apps/exchanges.d.ts.map +1 -0
- package/dist/apps/exchanges.js +3570 -0
- package/dist/apps/exchanges.js.map +1 -0
- package/dist/apps/fee-amm.d.ts +640 -0
- package/dist/apps/fee-amm.d.ts.map +1 -0
- package/dist/apps/fee-amm.js +815 -0
- package/dist/apps/fee-amm.js.map +1 -0
- package/dist/apps/indexer.d.ts +116 -0
- package/dist/apps/indexer.d.ts.map +1 -0
- package/dist/apps/indexer.js +270 -0
- package/dist/apps/indexer.js.map +1 -0
- package/dist/apps/receipts.d.ts +1438 -0
- package/dist/apps/receipts.d.ts.map +1 -0
- package/dist/apps/receipts.js +874 -0
- package/dist/apps/receipts.js.map +1 -0
- package/dist/apps/rpc.d.ts +173 -0
- package/dist/apps/rpc.d.ts.map +1 -0
- package/dist/apps/rpc.js +191 -0
- package/dist/apps/rpc.js.map +1 -0
- package/dist/apps/tokenlist.d.ts +192 -0
- package/dist/apps/tokenlist.d.ts.map +1 -0
- package/dist/apps/tokenlist.js +153 -0
- package/dist/apps/tokenlist.js.map +1 -0
- package/dist/apps/tokens.d.ts +2246 -0
- package/dist/apps/tokens.d.ts.map +1 -0
- package/dist/apps/tokens.js +1789 -0
- package/dist/apps/tokens.js.map +1 -0
- package/dist/apps/transactions.d.ts +2327 -0
- package/dist/apps/transactions.d.ts.map +1 -0
- package/dist/apps/transactions.js +975 -0
- package/dist/apps/transactions.js.map +1 -0
- package/dist/apps/transfers.d.ts +589 -0
- package/dist/apps/transfers.d.ts.map +1 -0
- package/dist/apps/transfers.js +769 -0
- package/dist/apps/transfers.js.map +1 -0
- package/dist/apps/verified-tokens.d.ts +544 -0
- package/dist/apps/verified-tokens.d.ts.map +1 -0
- package/dist/apps/verified-tokens.js +234 -0
- package/dist/apps/verified-tokens.js.map +1 -0
- package/dist/apps/webhooks.d.ts +7684 -0
- package/dist/apps/webhooks.d.ts.map +1 -0
- package/dist/apps/webhooks.js +2112 -0
- package/dist/apps/webhooks.js.map +1 -0
- package/dist/cloudflare.d.ts +32 -0
- package/dist/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare.js +45 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Auth.d.ts +298 -0
- package/dist/internal/Auth.d.ts.map +1 -0
- package/dist/internal/Auth.js +618 -0
- package/dist/internal/Auth.js.map +1 -0
- package/dist/internal/Cache.d.ts +74 -0
- package/dist/internal/Cache.d.ts.map +1 -0
- package/dist/internal/Cache.js +215 -0
- package/dist/internal/Cache.js.map +1 -0
- package/dist/internal/Cursor.d.ts +79 -0
- package/dist/internal/Cursor.d.ts.map +1 -0
- package/dist/internal/Cursor.js +116 -0
- package/dist/internal/Cursor.js.map +1 -0
- package/dist/internal/EdgeCache.d.ts +59 -0
- package/dist/internal/EdgeCache.d.ts.map +1 -0
- package/dist/internal/EdgeCache.js +174 -0
- package/dist/internal/EdgeCache.js.map +1 -0
- package/dist/internal/Log.d.ts +104 -0
- package/dist/internal/Log.d.ts.map +1 -0
- package/dist/internal/Log.js +118 -0
- package/dist/internal/Log.js.map +1 -0
- package/dist/internal/MetricSink.d.ts +65 -0
- package/dist/internal/MetricSink.d.ts.map +1 -0
- package/dist/internal/MetricSink.js +146 -0
- package/dist/internal/MetricSink.js.map +1 -0
- package/dist/internal/Mpp.d.ts +71 -0
- package/dist/internal/Mpp.d.ts.map +1 -0
- package/dist/internal/Mpp.js +105 -0
- package/dist/internal/Mpp.js.map +1 -0
- package/dist/internal/OpenApi.d.ts +313 -0
- package/dist/internal/OpenApi.d.ts.map +1 -0
- package/dist/internal/OpenApi.fonts.d.ts +2 -0
- package/dist/internal/OpenApi.fonts.d.ts.map +1 -0
- package/dist/internal/OpenApi.fonts.js +39 -0
- package/dist/internal/OpenApi.fonts.js.map +1 -0
- package/dist/internal/OpenApi.js +335 -0
- package/dist/internal/OpenApi.js.map +1 -0
- package/dist/internal/OpenApi.logo.d.ts +6 -0
- package/dist/internal/OpenApi.logo.d.ts.map +1 -0
- package/dist/internal/OpenApi.logo.js +19 -0
- package/dist/internal/OpenApi.logo.js.map +1 -0
- package/dist/internal/Path.d.ts +5 -0
- package/dist/internal/Path.d.ts.map +1 -0
- package/dist/internal/Path.js +16 -0
- package/dist/internal/Path.js.map +1 -0
- package/dist/internal/RateLimit.d.ts +60 -0
- package/dist/internal/RateLimit.d.ts.map +1 -0
- package/dist/internal/RateLimit.js +48 -0
- package/dist/internal/RateLimit.js.map +1 -0
- package/dist/internal/Response.d.ts +132 -0
- package/dist/internal/Response.d.ts.map +1 -0
- package/dist/internal/Response.js +72 -0
- package/dist/internal/Response.js.map +1 -0
- package/dist/internal/Schema.d.ts +257 -0
- package/dist/internal/Schema.d.ts.map +1 -0
- package/dist/internal/Schema.js +360 -0
- package/dist/internal/Schema.js.map +1 -0
- package/dist/internal/Store.d.ts +204 -0
- package/dist/internal/Store.d.ts.map +1 -0
- package/dist/internal/Store.js +291 -0
- package/dist/internal/Store.js.map +1 -0
- package/dist/internal/Tidx.d.ts +120 -0
- package/dist/internal/Tidx.d.ts.map +1 -0
- package/dist/internal/Tidx.js +156 -0
- package/dist/internal/Tidx.js.map +1 -0
- package/dist/internal/Timing.d.ts +22 -0
- package/dist/internal/Timing.d.ts.map +1 -0
- package/dist/internal/Timing.js +32 -0
- package/dist/internal/Timing.js.map +1 -0
- package/dist/internal/Ttl.d.ts +25 -0
- package/dist/internal/Ttl.d.ts.map +1 -0
- package/dist/internal/Ttl.js +49 -0
- package/dist/internal/Ttl.js.map +1 -0
- package/dist/internal/Value.d.ts +47 -0
- package/dist/internal/Value.d.ts.map +1 -0
- package/dist/internal/Value.js +122 -0
- package/dist/internal/Value.js.map +1 -0
- package/dist/internal/VerifiedTokens.d.ts +228 -0
- package/dist/internal/VerifiedTokens.d.ts.map +1 -0
- package/dist/internal/VerifiedTokens.js +450 -0
- package/dist/internal/VerifiedTokens.js.map +1 -0
- package/dist/internal/Viem.d.ts +2082 -0
- package/dist/internal/Viem.d.ts.map +1 -0
- package/dist/internal/Viem.js +63 -0
- package/dist/internal/Viem.js.map +1 -0
- package/dist/internal/WebhookDestination.d.ts +198 -0
- package/dist/internal/WebhookDestination.d.ts.map +1 -0
- package/dist/internal/WebhookDestination.js +170 -0
- package/dist/internal/WebhookDestination.js.map +1 -0
- package/dist/internal/Webhooks.d.ts +433 -0
- package/dist/internal/Webhooks.d.ts.map +1 -0
- package/dist/internal/Webhooks.js +489 -0
- package/dist/internal/Webhooks.js.map +1 -0
- package/dist/internal/webhookDestinations/betterStack.d.ts +26 -0
- package/dist/internal/webhookDestinations/betterStack.d.ts.map +1 -0
- package/dist/internal/webhookDestinations/betterStack.js +76 -0
- package/dist/internal/webhookDestinations/betterStack.js.map +1 -0
- package/dist/internal/webhookDestinations/slack.d.ts +59 -0
- package/dist/internal/webhookDestinations/slack.d.ts.map +1 -0
- package/dist/internal/webhookDestinations/slack.js +422 -0
- package/dist/internal/webhookDestinations/slack.js.map +1 -0
- package/dist/internal/webhookDestinations/url.d.ts +17 -0
- package/dist/internal/webhookDestinations/url.d.ts.map +1 -0
- package/dist/internal/webhookDestinations/url.js +130 -0
- package/dist/internal/webhookDestinations/url.js.map +1 -0
- package/dist/openrpc.json +5302 -0
- package/package.json +46 -4
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +1 -0
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
- package/seeds/31318/verified-tokens.json +30 -0
- package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
- package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +4 -0
- package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +30 -0
- package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +11 -0
- package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +1 -0
- package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
- package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
- package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +5 -0
- package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
- package/seeds/4217/verified-tokens.json +177 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
- package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +1 -0
- package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +1 -0
- package/seeds/42431/verified-tokens.json +44 -0
- package/seeds/openrpc/README.md +27 -0
- package/seeds/openrpc/execution-apis.json +8989 -0
- package/src/ApiKey.test.ts +91 -0
- package/src/ApiKey.ts +122 -0
- package/src/ApiKeySource.test-d.ts +11 -0
- package/src/ApiKeySource.test.ts +295 -0
- package/src/ApiKeySource.ts +303 -0
- package/src/App.test-d.ts +38 -0
- package/src/App.test.ts +65927 -0
- package/src/App.ts +855 -0
- package/src/Assets.ts +58 -0
- package/src/Auth.ts +1 -0
- package/src/Cli.test.ts +81 -0
- package/src/Cli.ts +38 -0
- package/src/Client.test-d.ts +1438 -0
- package/src/Client.test.ts +134 -0
- package/src/Client.ts +45 -0
- package/src/Metrics.ts +154 -0
- package/src/Scope.ts +50 -0
- package/src/Webhooks.test.ts +49 -0
- package/src/Webhooks.ts +172 -0
- package/src/admin/Access.test.ts +130 -0
- package/src/admin/Access.ts +87 -0
- package/src/admin/App.test.ts +225 -0
- package/src/admin/App.ts +216 -0
- package/src/admin/apps/api-keys.test.ts +156 -0
- package/src/admin/apps/api-keys.ts +157 -0
- package/src/admin/apps/scopes.test.ts +52 -0
- package/src/admin/apps/scopes.ts +47 -0
- package/src/admin/apps/verified-tokens.test.ts +415 -0
- package/src/admin/apps/verified-tokens.ts +566 -0
- package/src/admin/index.ts +2 -0
- package/src/admin/ui/plugin.ts +135 -0
- package/src/admin/ui/src/components/Checkbox.tsx +22 -0
- package/src/admin/ui/src/components/Dialog.tsx +32 -0
- package/src/admin/ui/src/components/MobileProfile.tsx +31 -0
- package/src/admin/ui/src/components/Page.tsx +53 -0
- package/src/admin/ui/src/components/TempoLogo.tsx +37 -0
- package/src/admin/ui/src/fonts/Pilat-Demi.woff2 +0 -0
- package/src/admin/ui/src/fonts/Pilat-Regular.woff2 +0 -0
- package/src/admin/ui/src/lib/api-keys.ts +63 -0
- package/src/admin/ui/src/lib/client.ts +15 -0
- package/src/admin/ui/src/lib/config.ts +57 -0
- package/src/admin/ui/src/lib/query-client.ts +26 -0
- package/src/admin/ui/src/lib/scopes.ts +25 -0
- package/src/admin/ui/src/lib/verified-tokens.ts +89 -0
- package/src/admin/ui/src/lib/wagmi.ts +18 -0
- package/src/admin/ui/src/routeTree.gen.ts +104 -0
- package/src/admin/ui/src/router.tsx +25 -0
- package/src/admin/ui/src/routes/__root.tsx +219 -0
- package/src/admin/ui/src/routes/api-keys.tsx +329 -0
- package/src/admin/ui/src/routes/index.tsx +7 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +331 -0
- package/src/admin/ui/src/styles.css +20 -0
- package/src/admin/ui/tsconfig.json +16 -0
- package/src/admin/ui/vite.config.ts +29 -0
- package/src/admin/ui.generated.ts +60 -0
- package/src/apps/activities.test.ts +1020 -0
- package/src/apps/activities.ts +3934 -0
- package/src/apps/addresses.ts +24 -0
- package/src/apps/balances.test.ts +274 -0
- package/src/apps/balances.ts +533 -0
- package/src/apps/blocks.test.ts +385 -0
- package/src/apps/blocks.ts +896 -0
- package/src/apps/coingecko.test.ts +218 -0
- package/src/apps/coingecko.ts +1383 -0
- package/src/apps/exchanges.test.ts +958 -0
- package/src/apps/exchanges.ts +4773 -0
- package/src/apps/fee-amm.test.ts +321 -0
- package/src/apps/fee-amm.ts +1121 -0
- package/src/apps/indexer.test.ts +153 -0
- package/src/apps/indexer.ts +330 -0
- package/src/apps/receipts.test.ts +285 -0
- package/src/apps/receipts.ts +1193 -0
- package/src/apps/rpc.test.ts +302 -0
- package/src/apps/rpc.ts +222 -0
- package/src/apps/tokenlist.test.ts +80 -0
- package/src/apps/tokenlist.ts +207 -0
- package/src/apps/tokens.test.ts +1062 -0
- package/src/apps/tokens.ts +2292 -0
- package/src/apps/transactions.test.ts +408 -0
- package/src/apps/transactions.ts +1363 -0
- package/src/apps/transfers.test.ts +371 -0
- package/src/apps/transfers.ts +1116 -0
- package/src/apps/verified-tokens.test.ts +114 -0
- package/src/apps/verified-tokens.ts +295 -0
- package/src/apps/webhooks.test.ts +1937 -0
- package/src/apps/webhooks.ts +2926 -0
- package/src/cloudflare.ts +55 -0
- package/src/index.ts +15 -0
- package/src/internal/Auth.test.ts +980 -0
- package/src/internal/Auth.ts +1012 -0
- package/src/internal/Cache.test.ts +240 -0
- package/src/internal/Cache.ts +262 -0
- package/src/internal/Cursor.test.ts +100 -0
- package/src/internal/Cursor.ts +149 -0
- package/src/internal/EdgeCache.test.ts +234 -0
- package/src/internal/EdgeCache.ts +224 -0
- package/src/internal/Errors.ts +14 -0
- package/src/internal/Log.test.ts +244 -0
- package/src/internal/Log.ts +216 -0
- package/src/internal/MetricSink.ts +220 -0
- package/src/internal/Mpp.test.ts +64 -0
- package/src/internal/Mpp.ts +141 -0
- package/src/internal/OpenApi.fonts.ts +38 -0
- package/src/internal/OpenApi.logo.ts +18 -0
- package/src/internal/OpenApi.ts +488 -0
- package/src/internal/Path.test.ts +14 -0
- package/src/internal/Path.ts +15 -0
- package/src/internal/RateLimit.test.ts +93 -0
- package/src/internal/RateLimit.ts +93 -0
- package/src/internal/Response.ts +131 -0
- package/src/internal/Schema.test.ts +34 -0
- package/src/internal/Schema.ts +523 -0
- package/src/internal/Store.test.ts +545 -0
- package/src/internal/Store.ts +526 -0
- package/src/internal/Tidx.local.test.ts +33 -0
- package/src/internal/Tidx.test.ts +294 -0
- package/src/internal/Tidx.ts +238 -0
- package/src/internal/Timing.test.ts +22 -0
- package/src/internal/Timing.ts +62 -0
- package/src/internal/Ttl.test.ts +18 -0
- package/src/internal/Ttl.ts +59 -0
- package/src/internal/Value.ts +114 -0
- package/src/internal/VerifiedTokens.test.ts +333 -0
- package/src/internal/VerifiedTokens.ts +645 -0
- package/src/internal/Viem.test.ts +115 -0
- package/src/internal/Viem.ts +165 -0
- package/src/internal/WebhookDestination.test.ts +648 -0
- package/src/internal/WebhookDestination.ts +331 -0
- package/src/internal/Webhooks.test.ts +668 -0
- package/src/internal/Webhooks.ts +940 -0
- package/src/internal/webhookDestinations/betterStack.ts +91 -0
- package/src/internal/webhookDestinations/slack.ts +496 -0
- package/src/internal/webhookDestinations/url.ts +128 -0
- package/src/openrpc.json +5302 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { type Context, Hono } from 'hono'
|
|
2
|
+
import * as z from 'zod/mini'
|
|
3
|
+
|
|
4
|
+
import * as OpenApi from '../../internal/OpenApi.js'
|
|
5
|
+
import * as Response from '../../internal/Response.js'
|
|
6
|
+
import * as Schema from '../../internal/Schema.js'
|
|
7
|
+
import type * as Store from '../../internal/Store.js'
|
|
8
|
+
import * as VerifiedTokens from '../../internal/VerifiedTokens.js'
|
|
9
|
+
import { schema as readSchema } from '../../apps/verified-tokens.js'
|
|
10
|
+
import type * as App from '../App.js'
|
|
11
|
+
|
|
12
|
+
/** Required `chainId` query: admin mutations always address a chain explicitly. */
|
|
13
|
+
const ChainIdQuery = Schema.ChainId
|
|
14
|
+
|
|
15
|
+
/** Zod schemas owned by the admin verified-tokens handler. */
|
|
16
|
+
export namespace schema {
|
|
17
|
+
/** A curated verified TIP-20 token entry (shared with the public read API). */
|
|
18
|
+
export const Token = readSchema.Token
|
|
19
|
+
|
|
20
|
+
/** Verified-token write/input fields without the server-derived `id`. */
|
|
21
|
+
export const Input = readSchema.Input
|
|
22
|
+
|
|
23
|
+
/** Path parameters addressing a single verified token. */
|
|
24
|
+
export const Params = readSchema.Params
|
|
25
|
+
|
|
26
|
+
/** Schemas for the listVerifiedTokens operation. */
|
|
27
|
+
export namespace listVerifiedTokens {
|
|
28
|
+
/** Query parameters for the verified-token list. */
|
|
29
|
+
export const Query = z
|
|
30
|
+
.strictObject({ chainId: ChainIdQuery })
|
|
31
|
+
.check(z.describe('Query parameters for the verified-token list.'))
|
|
32
|
+
|
|
33
|
+
/** Response: the curated list and the current snapshot version. */
|
|
34
|
+
export const Response = Schema.describe(
|
|
35
|
+
z.object({
|
|
36
|
+
data: z.array(Token).check(z.describe('The curated verified list for the chain.')),
|
|
37
|
+
version: z
|
|
38
|
+
.nullable(z.string())
|
|
39
|
+
.check(z.describe('Current snapshot version, or null when the list is empty.')),
|
|
40
|
+
}),
|
|
41
|
+
'Curated verified tokens and the current snapshot version.',
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Schemas for the createVerifiedToken operation. */
|
|
46
|
+
export namespace createVerifiedToken {
|
|
47
|
+
/** Request body for creating a verified token. */
|
|
48
|
+
export const Body = z
|
|
49
|
+
.extend(Input, {
|
|
50
|
+
chainId: Schema.ChainId,
|
|
51
|
+
logo: z.optional(
|
|
52
|
+
z
|
|
53
|
+
.string()
|
|
54
|
+
.check(z.describe('Inline SVG logo markup, uploaded to the asset store on create.')),
|
|
55
|
+
),
|
|
56
|
+
})
|
|
57
|
+
.check(z.describe('Request body for creating a verified token.'))
|
|
58
|
+
|
|
59
|
+
/** Response after a successful create. */
|
|
60
|
+
export const Response = Schema.describe(
|
|
61
|
+
z.object({
|
|
62
|
+
data: Token,
|
|
63
|
+
version: z.string().check(z.describe('New snapshot version after the write.')),
|
|
64
|
+
}),
|
|
65
|
+
'Created verified token and the new snapshot version.',
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Schemas for the patchVerifiedToken operation. */
|
|
70
|
+
export namespace patchVerifiedToken {
|
|
71
|
+
/** Query parameters for a verified-token patch. */
|
|
72
|
+
export const Query = z
|
|
73
|
+
.strictObject({ chainId: ChainIdQuery })
|
|
74
|
+
.check(z.describe('Query parameters for a verified-token patch.'))
|
|
75
|
+
|
|
76
|
+
/** Request body for partially updating a verified token. */
|
|
77
|
+
export const Body = z
|
|
78
|
+
.partial(z.omit(Input, { address: true }))
|
|
79
|
+
.check(z.describe('Request body for partially updating a verified token.'))
|
|
80
|
+
|
|
81
|
+
/** Response after a successful patch. */
|
|
82
|
+
export const Response = createVerifiedToken.Response
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Schemas for the deleteVerifiedToken operation. */
|
|
86
|
+
export namespace deleteVerifiedToken {
|
|
87
|
+
/** Query parameters for a verified-token delete. */
|
|
88
|
+
export const Query = z
|
|
89
|
+
.strictObject({ chainId: ChainIdQuery })
|
|
90
|
+
.check(z.describe('Query parameters for a verified-token delete.'))
|
|
91
|
+
|
|
92
|
+
/** Response after a successful delete. */
|
|
93
|
+
export const Response = Schema.describe(
|
|
94
|
+
z.object({
|
|
95
|
+
version: z.string().check(z.describe('New snapshot version after the removal.')),
|
|
96
|
+
}),
|
|
97
|
+
'New snapshot version after a delete.',
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Schemas for the uploadVerifiedTokenLogo operation. */
|
|
102
|
+
export namespace uploadVerifiedTokenLogo {
|
|
103
|
+
/** Query parameters for a logo upload. */
|
|
104
|
+
export const Query = z
|
|
105
|
+
.strictObject({ chainId: ChainIdQuery })
|
|
106
|
+
.check(z.describe('Query parameters for a logo upload.'))
|
|
107
|
+
|
|
108
|
+
/** Response after a successful logo upload. */
|
|
109
|
+
export const Response = Schema.describe(
|
|
110
|
+
z.object({
|
|
111
|
+
key: z.string().check(z.describe('Asset-store key the logo was written to.')),
|
|
112
|
+
path: z.string().check(z.describe('Public path of the logo under the main API origin.')),
|
|
113
|
+
}),
|
|
114
|
+
'Stored logo location.',
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Schemas for the getVerifiedTokenLogo operation. */
|
|
119
|
+
export namespace getVerifiedTokenLogo {
|
|
120
|
+
/** Query parameters for a logo read. */
|
|
121
|
+
export const Query = z
|
|
122
|
+
.strictObject({ chainId: ChainIdQuery })
|
|
123
|
+
.check(z.describe('Query parameters for a logo read.'))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Schemas for the replaceVerifiedTokens operation. */
|
|
127
|
+
export namespace replaceVerifiedTokens {
|
|
128
|
+
/** Query parameters for a bulk replace. */
|
|
129
|
+
export const Query = z
|
|
130
|
+
.strictObject({ chainId: ChainIdQuery })
|
|
131
|
+
.check(z.describe('Query parameters for a bulk replace.'))
|
|
132
|
+
|
|
133
|
+
/** Request body for replacing a chain's entire verified list. */
|
|
134
|
+
export const Body = z
|
|
135
|
+
.object({
|
|
136
|
+
tokens: z.array(Input).check(z.describe('The full verified list to publish for the chain.')), // prettier-ignore
|
|
137
|
+
})
|
|
138
|
+
.check(z.describe('Request body for replacing a chain verified list.'))
|
|
139
|
+
|
|
140
|
+
/** Response after a successful bulk replace. */
|
|
141
|
+
export const Response = Schema.describe(
|
|
142
|
+
z.object({
|
|
143
|
+
data: z.array(Token).check(z.describe('The published verified list.')),
|
|
144
|
+
version: z.string().check(z.describe('New snapshot version after the replace.')),
|
|
145
|
+
}),
|
|
146
|
+
'Published verified list and the new snapshot version.',
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Management routes for the curated verified-token list, backed by the request's
|
|
153
|
+
* `verifiedTokens` store; all `404` when no store is configured. Mutations accept
|
|
154
|
+
* an optional `If-Match` precondition (`412` on mismatch).
|
|
155
|
+
*
|
|
156
|
+
* - `POST /` creates a token (`409` on a duplicate address or symbol).
|
|
157
|
+
* - `PUT /` replaces a chain's entire list (seed/rollback).
|
|
158
|
+
* - `PATCH /:address` partially updates a token (`404` if absent).
|
|
159
|
+
* - `DELETE /:address` removes a token (`404` if absent).
|
|
160
|
+
* - `PUT /:address/logo` uploads a curated logo SVG (gated on `assets`).
|
|
161
|
+
*/
|
|
162
|
+
export function verifiedTokens() {
|
|
163
|
+
return new Hono<App.Environment>()
|
|
164
|
+
.get(
|
|
165
|
+
'/',
|
|
166
|
+
OpenApi.validate('query', schema.listVerifiedTokens.Query, {
|
|
167
|
+
code: 'query_invalid',
|
|
168
|
+
message: 'Invalid query parameters',
|
|
169
|
+
}),
|
|
170
|
+
OpenApi.describeRoute({
|
|
171
|
+
operationId: 'listVerifiedTokens',
|
|
172
|
+
responses: OpenApi.responses({
|
|
173
|
+
errors: { 404: featureDisabledDescription },
|
|
174
|
+
success: {
|
|
175
|
+
description: 'Curated verified tokens.',
|
|
176
|
+
schema: schema.listVerifiedTokens.Response,
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
summary: 'List verified tokens',
|
|
180
|
+
tags: ['Verified Tokens'],
|
|
181
|
+
}),
|
|
182
|
+
async (c) => {
|
|
183
|
+
const store = c.get('verifiedTokens')?.store
|
|
184
|
+
if (!store) return notEnabled(c)
|
|
185
|
+
const { chainId } = c.req.valid('query')
|
|
186
|
+
const snapshot = await VerifiedTokens.read(store, chainId)
|
|
187
|
+
return c.json(
|
|
188
|
+
Response.validated(schema.listVerifiedTokens.Response, {
|
|
189
|
+
data: snapshot?.list ?? [],
|
|
190
|
+
version: snapshot?.version ?? null,
|
|
191
|
+
}),
|
|
192
|
+
)
|
|
193
|
+
},
|
|
194
|
+
)
|
|
195
|
+
.post(
|
|
196
|
+
'/',
|
|
197
|
+
OpenApi.validate('json', schema.createVerifiedToken.Body, {
|
|
198
|
+
code: 'body_invalid',
|
|
199
|
+
message: 'Invalid request body',
|
|
200
|
+
}),
|
|
201
|
+
OpenApi.describeRoute({
|
|
202
|
+
operationId: 'createVerifiedToken',
|
|
203
|
+
responses: OpenApi.responses({
|
|
204
|
+
errors: {
|
|
205
|
+
404: featureDisabledDescription,
|
|
206
|
+
409: 'Duplicate address or symbol.',
|
|
207
|
+
413: 'Logo exceeds the size limit.',
|
|
208
|
+
415: 'Logo is not valid SVG.',
|
|
209
|
+
},
|
|
210
|
+
success: {
|
|
211
|
+
description: 'Created verified token.',
|
|
212
|
+
schema: schema.createVerifiedToken.Response,
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
215
|
+
summary: 'Create verified token',
|
|
216
|
+
tags: ['Verified Tokens'],
|
|
217
|
+
}),
|
|
218
|
+
async (c) => {
|
|
219
|
+
const store = c.get('verifiedTokens')?.store
|
|
220
|
+
if (!store) return notEnabled(c)
|
|
221
|
+
const { logo, ...body } = c.req.valid('json')
|
|
222
|
+
try {
|
|
223
|
+
if (logo !== undefined) {
|
|
224
|
+
const assets = c.get('assets')
|
|
225
|
+
if (!assets?.put) return assetsNotEnabled(c)
|
|
226
|
+
const svg = encodeLogo(logo)
|
|
227
|
+
const duplicate = await findDuplicate(store, body)
|
|
228
|
+
if (duplicate) throw duplicate
|
|
229
|
+
await assets.put(logoKey(body.chainId, body.address), {
|
|
230
|
+
body: svg,
|
|
231
|
+
contentType: 'image/svg+xml',
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
const { snapshot, token } = await VerifiedTokens.create(store, body)
|
|
235
|
+
return c.json(
|
|
236
|
+
Response.validated(schema.createVerifiedToken.Response, {
|
|
237
|
+
data: token,
|
|
238
|
+
version: snapshot.version,
|
|
239
|
+
}),
|
|
240
|
+
)
|
|
241
|
+
} catch (cause) {
|
|
242
|
+
return mutationError(c, cause)
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
)
|
|
246
|
+
.put(
|
|
247
|
+
'/',
|
|
248
|
+
OpenApi.validate('query', schema.replaceVerifiedTokens.Query, {
|
|
249
|
+
code: 'query_invalid',
|
|
250
|
+
message: 'Invalid query parameters',
|
|
251
|
+
}),
|
|
252
|
+
OpenApi.validate('json', schema.replaceVerifiedTokens.Body, {
|
|
253
|
+
code: 'body_invalid',
|
|
254
|
+
message: 'Invalid request body',
|
|
255
|
+
}),
|
|
256
|
+
OpenApi.describeRoute({
|
|
257
|
+
operationId: 'replaceVerifiedTokens',
|
|
258
|
+
responses: OpenApi.responses({
|
|
259
|
+
errors: {
|
|
260
|
+
404: featureDisabledDescription,
|
|
261
|
+
409: 'Duplicate address or symbol.',
|
|
262
|
+
412: 'Version precondition failed.',
|
|
263
|
+
},
|
|
264
|
+
success: {
|
|
265
|
+
description: 'Published verified list.',
|
|
266
|
+
schema: schema.replaceVerifiedTokens.Response,
|
|
267
|
+
},
|
|
268
|
+
}),
|
|
269
|
+
summary: 'Replace verified tokens',
|
|
270
|
+
tags: ['Verified Tokens'],
|
|
271
|
+
}),
|
|
272
|
+
async (c) => {
|
|
273
|
+
const store = c.get('verifiedTokens')?.store
|
|
274
|
+
if (!store) return notEnabled(c)
|
|
275
|
+
const { chainId } = c.req.valid('query')
|
|
276
|
+
const { tokens } = c.req.valid('json')
|
|
277
|
+
try {
|
|
278
|
+
const { snapshot } = await VerifiedTokens.replace(store, chainId, tokens, {
|
|
279
|
+
...ifMatch(c),
|
|
280
|
+
})
|
|
281
|
+
return c.json(
|
|
282
|
+
Response.validated(schema.replaceVerifiedTokens.Response, {
|
|
283
|
+
data: snapshot.list,
|
|
284
|
+
version: snapshot.version,
|
|
285
|
+
}),
|
|
286
|
+
)
|
|
287
|
+
} catch (cause) {
|
|
288
|
+
return mutationError(c, cause)
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
)
|
|
292
|
+
.patch(
|
|
293
|
+
'/:address',
|
|
294
|
+
OpenApi.validate('param', schema.Params, {
|
|
295
|
+
code: 'address_invalid',
|
|
296
|
+
message: 'Invalid token address',
|
|
297
|
+
}),
|
|
298
|
+
OpenApi.validate('query', schema.patchVerifiedToken.Query, {
|
|
299
|
+
code: 'query_invalid',
|
|
300
|
+
message: 'Invalid query parameters',
|
|
301
|
+
}),
|
|
302
|
+
OpenApi.validate('json', schema.patchVerifiedToken.Body, {
|
|
303
|
+
code: 'body_invalid',
|
|
304
|
+
message: 'Invalid request body',
|
|
305
|
+
}),
|
|
306
|
+
OpenApi.describeRoute({
|
|
307
|
+
operationId: 'patchVerifiedToken',
|
|
308
|
+
responses: OpenApi.responses({
|
|
309
|
+
errors: {
|
|
310
|
+
404: 'Verified token not found (or feature disabled).',
|
|
311
|
+
409: 'Duplicate symbol.',
|
|
312
|
+
412: 'Version precondition failed.',
|
|
313
|
+
},
|
|
314
|
+
success: {
|
|
315
|
+
description: 'Updated verified token.',
|
|
316
|
+
schema: schema.patchVerifiedToken.Response,
|
|
317
|
+
},
|
|
318
|
+
}),
|
|
319
|
+
summary: 'Update verified token',
|
|
320
|
+
tags: ['Verified Tokens'],
|
|
321
|
+
}),
|
|
322
|
+
async (c) => {
|
|
323
|
+
const store = c.get('verifiedTokens')?.store
|
|
324
|
+
if (!store) return notEnabled(c)
|
|
325
|
+
const { address } = c.req.valid('param')
|
|
326
|
+
const { chainId } = c.req.valid('query')
|
|
327
|
+
const body = c.req.valid('json')
|
|
328
|
+
try {
|
|
329
|
+
const { snapshot, token } = await VerifiedTokens.patch(store, chainId, address, body, {
|
|
330
|
+
...ifMatch(c),
|
|
331
|
+
})
|
|
332
|
+
return c.json(
|
|
333
|
+
Response.validated(schema.patchVerifiedToken.Response, {
|
|
334
|
+
data: token,
|
|
335
|
+
version: snapshot.version,
|
|
336
|
+
}),
|
|
337
|
+
)
|
|
338
|
+
} catch (cause) {
|
|
339
|
+
return mutationError(c, cause)
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
)
|
|
343
|
+
.delete(
|
|
344
|
+
'/:address',
|
|
345
|
+
OpenApi.validate('param', schema.Params, {
|
|
346
|
+
code: 'address_invalid',
|
|
347
|
+
message: 'Invalid token address',
|
|
348
|
+
}),
|
|
349
|
+
OpenApi.validate('query', schema.deleteVerifiedToken.Query, {
|
|
350
|
+
code: 'query_invalid',
|
|
351
|
+
message: 'Invalid query parameters',
|
|
352
|
+
}),
|
|
353
|
+
OpenApi.describeRoute({
|
|
354
|
+
operationId: 'deleteVerifiedToken',
|
|
355
|
+
responses: OpenApi.responses({
|
|
356
|
+
errors: {
|
|
357
|
+
404: 'Verified token not found (or feature disabled).',
|
|
358
|
+
412: 'Version precondition failed.',
|
|
359
|
+
},
|
|
360
|
+
success: {
|
|
361
|
+
description: 'Removed; returns the new snapshot version.',
|
|
362
|
+
schema: schema.deleteVerifiedToken.Response,
|
|
363
|
+
},
|
|
364
|
+
}),
|
|
365
|
+
summary: 'Delete verified token',
|
|
366
|
+
tags: ['Verified Tokens'],
|
|
367
|
+
}),
|
|
368
|
+
async (c) => {
|
|
369
|
+
const store = c.get('verifiedTokens')?.store
|
|
370
|
+
if (!store) return notEnabled(c)
|
|
371
|
+
const { address } = c.req.valid('param')
|
|
372
|
+
const { chainId } = c.req.valid('query')
|
|
373
|
+
try {
|
|
374
|
+
const { snapshot } = await VerifiedTokens.remove(store, chainId, address, {
|
|
375
|
+
...ifMatch(c),
|
|
376
|
+
})
|
|
377
|
+
return c.json(
|
|
378
|
+
Response.validated(schema.deleteVerifiedToken.Response, {
|
|
379
|
+
version: snapshot.version,
|
|
380
|
+
}),
|
|
381
|
+
)
|
|
382
|
+
} catch (cause) {
|
|
383
|
+
return mutationError(c, cause)
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
)
|
|
387
|
+
.get(
|
|
388
|
+
'/:address/logo',
|
|
389
|
+
OpenApi.validate('param', schema.Params, {
|
|
390
|
+
code: 'address_invalid',
|
|
391
|
+
message: 'Invalid token address',
|
|
392
|
+
}),
|
|
393
|
+
OpenApi.validate('query', schema.getVerifiedTokenLogo.Query, {
|
|
394
|
+
code: 'query_invalid',
|
|
395
|
+
message: 'Invalid query parameters',
|
|
396
|
+
}),
|
|
397
|
+
async (c) => {
|
|
398
|
+
const assets = c.get('assets')
|
|
399
|
+
const { address } = c.req.valid('param')
|
|
400
|
+
const { chainId } = c.req.valid('query')
|
|
401
|
+
const logo = await assets?.get(logoKey(chainId, address))
|
|
402
|
+
if (!logo)
|
|
403
|
+
return Response.error(c, {
|
|
404
|
+
code: 'logo_not_found',
|
|
405
|
+
message: 'Logo not found.',
|
|
406
|
+
status: 404,
|
|
407
|
+
})
|
|
408
|
+
c.header('Content-Type', logo.contentType)
|
|
409
|
+
return c.body(logo.body)
|
|
410
|
+
},
|
|
411
|
+
)
|
|
412
|
+
.put(
|
|
413
|
+
'/:address/logo',
|
|
414
|
+
OpenApi.validate('param', schema.Params, {
|
|
415
|
+
code: 'address_invalid',
|
|
416
|
+
message: 'Invalid token address',
|
|
417
|
+
}),
|
|
418
|
+
OpenApi.validate('query', schema.uploadVerifiedTokenLogo.Query, {
|
|
419
|
+
code: 'query_invalid',
|
|
420
|
+
message: 'Invalid query parameters',
|
|
421
|
+
}),
|
|
422
|
+
OpenApi.describeRoute({
|
|
423
|
+
operationId: 'uploadVerifiedTokenLogo',
|
|
424
|
+
responses: OpenApi.responses({
|
|
425
|
+
errors: {
|
|
426
|
+
404: 'Asset uploads are not enabled.',
|
|
427
|
+
413: 'Logo exceeds the size limit.',
|
|
428
|
+
415: 'Body is not `image/svg+xml`.',
|
|
429
|
+
},
|
|
430
|
+
success: {
|
|
431
|
+
description: 'Stored logo location.',
|
|
432
|
+
schema: schema.uploadVerifiedTokenLogo.Response,
|
|
433
|
+
},
|
|
434
|
+
}),
|
|
435
|
+
summary: 'Upload verified-token logo',
|
|
436
|
+
tags: ['Verified Tokens'],
|
|
437
|
+
}),
|
|
438
|
+
async (c) => {
|
|
439
|
+
const assets = c.get('assets')
|
|
440
|
+
if (!assets?.put) return assetsNotEnabled(c)
|
|
441
|
+
const { address } = c.req.valid('param')
|
|
442
|
+
const { chainId } = c.req.valid('query')
|
|
443
|
+
const contentType = c.req.header('content-type')?.split(';')[0]?.trim().toLowerCase()
|
|
444
|
+
if (contentType !== 'image/svg+xml')
|
|
445
|
+
return Response.error(c, {
|
|
446
|
+
code: 'unsupported_media_type',
|
|
447
|
+
message: 'Logo uploads must be `image/svg+xml`.',
|
|
448
|
+
status: 415,
|
|
449
|
+
})
|
|
450
|
+
const body = await c.req.arrayBuffer()
|
|
451
|
+
if (body.byteLength === 0)
|
|
452
|
+
return Response.error(c, {
|
|
453
|
+
code: 'body_invalid',
|
|
454
|
+
message: 'Logo body is empty.',
|
|
455
|
+
status: 400,
|
|
456
|
+
})
|
|
457
|
+
if (body.byteLength > maxLogoBytes)
|
|
458
|
+
return Response.error(c, {
|
|
459
|
+
code: 'payload_too_large',
|
|
460
|
+
message: `Logo exceeds the ${maxLogoBytes / 1024} KiB limit.`,
|
|
461
|
+
status: 413,
|
|
462
|
+
})
|
|
463
|
+
// Keyed as the main API's loader reads it (curated icon takes precedence).
|
|
464
|
+
const key = logoKey(chainId, address)
|
|
465
|
+
await assets.put(key, { body, contentType: 'image/svg+xml' })
|
|
466
|
+
return c.json(
|
|
467
|
+
Response.validated(schema.uploadVerifiedTokenLogo.Response, {
|
|
468
|
+
key,
|
|
469
|
+
path: `/assets/${key}`,
|
|
470
|
+
}),
|
|
471
|
+
)
|
|
472
|
+
},
|
|
473
|
+
)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** Largest accepted logo upload (SVG icons are tiny; this is generous). */
|
|
477
|
+
const maxLogoBytes = 256 * 1024
|
|
478
|
+
|
|
479
|
+
/** OpenAPI description shared by write routes' 404 when the feature is disabled. */
|
|
480
|
+
const featureDisabledDescription = 'Verified-token management is not enabled (or not found).'
|
|
481
|
+
|
|
482
|
+
/** Asset-store key the main API's loader reads for a token's curated icon. */
|
|
483
|
+
function logoKey(chainId: number, address: string): string {
|
|
484
|
+
return `${chainId}/icons/${address.toLowerCase()}`
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Thrown by `encodeLogo` for an invalid inline SVG; mapped to its status below.
|
|
488
|
+
class LogoError extends Error {
|
|
489
|
+
constructor(
|
|
490
|
+
readonly status: 400 | 413 | 415,
|
|
491
|
+
readonly code: string,
|
|
492
|
+
message: string,
|
|
493
|
+
) {
|
|
494
|
+
super(message)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Validates inline SVG markup and returns its bytes; throws `LogoError` when the
|
|
499
|
+
// body is empty, not SVG, or over the size limit.
|
|
500
|
+
function encodeLogo(logo: string): ArrayBuffer {
|
|
501
|
+
const trimmed = logo.trimStart()
|
|
502
|
+
if (trimmed.length === 0) throw new LogoError(400, 'body_invalid', 'Logo body is empty.')
|
|
503
|
+
if (!/^<(\?xml|svg)[\s/>]/i.test(trimmed))
|
|
504
|
+
throw new LogoError(415, 'unsupported_media_type', 'Logo must be SVG markup.')
|
|
505
|
+
const bytes = new TextEncoder().encode(logo)
|
|
506
|
+
if (bytes.byteLength > maxLogoBytes)
|
|
507
|
+
throw new LogoError(413, 'payload_too_large', `Logo exceeds the ${maxLogoBytes / 1024} KiB limit.`) // prettier-ignore
|
|
508
|
+
return bytes.buffer as ArrayBuffer
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Returns the duplicate-address/symbol error a create would throw, or undefined,
|
|
512
|
+
// so the logo upload can be skipped before it overwrites an existing icon.
|
|
513
|
+
async function findDuplicate(
|
|
514
|
+
store: Store.State,
|
|
515
|
+
token: { address: string; chainId: VerifiedTokens.ChainId; symbol: string },
|
|
516
|
+
): Promise<Error | undefined> {
|
|
517
|
+
const snapshot = await VerifiedTokens.read(store, token.chainId)
|
|
518
|
+
if (!snapshot) return undefined
|
|
519
|
+
if (snapshot.byAddress.has(token.address.toLowerCase()))
|
|
520
|
+
return new VerifiedTokens.DuplicateAddressError({ address: token.address, chainId: token.chainId }) // prettier-ignore
|
|
521
|
+
if (snapshot.bySymbol.has(token.symbol.toLowerCase()))
|
|
522
|
+
return new VerifiedTokens.DuplicateSymbolError({ chainId: token.chainId, symbol: token.symbol })
|
|
523
|
+
return undefined
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function assetsNotEnabled(c: Context<App.Environment>) {
|
|
527
|
+
return Response.error(c, {
|
|
528
|
+
code: 'assets_not_enabled',
|
|
529
|
+
message: 'Asset uploads are not enabled for this deployment.',
|
|
530
|
+
status: 404,
|
|
531
|
+
})
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Reads the optional `If-Match` precondition, stripping ETag-style quotes.
|
|
535
|
+
function ifMatch(c: Context<App.Environment>): { ifMatch?: string } {
|
|
536
|
+
const header = c.req.header('If-Match')
|
|
537
|
+
if (!header) return {}
|
|
538
|
+
return { ifMatch: header.replace(/^"|"$/g, '') }
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Maps `VerifiedTokens` write failures to the API's JSON error envelope.
|
|
542
|
+
function mutationError(c: Context<App.Environment>, cause: unknown) {
|
|
543
|
+
if (cause instanceof LogoError)
|
|
544
|
+
return Response.error(c, { code: cause.code, message: cause.message, status: cause.status })
|
|
545
|
+
if (cause instanceof VerifiedTokens.DuplicateAddressError)
|
|
546
|
+
return Response.error(c, { code: 'duplicate_address', message: cause.message, status: 409 })
|
|
547
|
+
if (cause instanceof VerifiedTokens.DuplicateSymbolError)
|
|
548
|
+
return Response.error(c, { code: 'duplicate_symbol', message: cause.message, status: 409 })
|
|
549
|
+
if (cause instanceof VerifiedTokens.NotFoundError)
|
|
550
|
+
return Response.error(c, {
|
|
551
|
+
code: 'verified_token_not_found',
|
|
552
|
+
message: 'Verified token not found',
|
|
553
|
+
status: 404,
|
|
554
|
+
})
|
|
555
|
+
if (cause instanceof VerifiedTokens.VersionMismatchError)
|
|
556
|
+
return Response.error(c, { code: 'version_mismatch', message: cause.message, status: 412 })
|
|
557
|
+
return Response.upstream(c, cause)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function notEnabled(c: Context<App.Environment>) {
|
|
561
|
+
return Response.error(c, {
|
|
562
|
+
code: 'verified_tokens_not_enabled',
|
|
563
|
+
message: 'Verified-token management is not enabled for this deployment.',
|
|
564
|
+
status: 404,
|
|
565
|
+
})
|
|
566
|
+
}
|