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,385 @@
|
|
|
1
|
+
import { Schema } from 'cadent'
|
|
2
|
+
import { Hex } from 'ox'
|
|
3
|
+
import { zeroHash } from 'viem'
|
|
4
|
+
|
|
5
|
+
import * as TestApp from '../../test/App.js'
|
|
6
|
+
import * as Runtime from '../../test/runtime.js'
|
|
7
|
+
import * as Viem from '../../test/Viem.js'
|
|
8
|
+
import * as Blocks from './blocks.js'
|
|
9
|
+
import * as Transactions from './transactions.js'
|
|
10
|
+
|
|
11
|
+
let recent: { number: bigint; hash: Hex.Hex } | undefined
|
|
12
|
+
let recentNonEmpty: { number: bigint; hash: Hex.Hex } | undefined
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
const runtime = Runtime.get()
|
|
16
|
+
if (runtime.mode === 'localnet') {
|
|
17
|
+
const blockNumber = runtime.fixtures!.faucetBlockNumbers[0]
|
|
18
|
+
if (blockNumber !== undefined) {
|
|
19
|
+
recent = await blockByNumber(BigInt(blockNumber)).catch(() => undefined)
|
|
20
|
+
recentNonEmpty = recent
|
|
21
|
+
}
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
recent = await findRecentBlock().catch(() => undefined)
|
|
26
|
+
recentNonEmpty = await findRecentNonEmptyBlock().catch(() => undefined)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('GET /blocks/:block (latest tag)', () => {
|
|
30
|
+
test('humanizes the head block and keeps the raw RPC under `meta.rpc`', async () => {
|
|
31
|
+
const client = TestApp.client()
|
|
32
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
33
|
+
{ param: { block: 'latest' }, query: {} },
|
|
34
|
+
TestApp.auth,
|
|
35
|
+
)
|
|
36
|
+
const body = await TestApp.json(response, Blocks.schema.getBlock.Response)
|
|
37
|
+
expect(response.status).toBe(200)
|
|
38
|
+
expect(response.headers.get('server-timing')?.includes('block;dur=')).toBe(true)
|
|
39
|
+
// The response is humanized: decimal-string amounts, plain numbers for
|
|
40
|
+
// block fields, and ISO-8601 timestamps.
|
|
41
|
+
expect(typeof body.number).toBe('number')
|
|
42
|
+
expect(body.number).toBeGreaterThan(0)
|
|
43
|
+
expect(body.hash).toMatch(/^0x[0-9a-f]{64}$/)
|
|
44
|
+
expect(body.parentHash).toMatch(/^0x[0-9a-f]{64}$/)
|
|
45
|
+
expect(body.miner).toMatch(/^0x[0-9a-f]{40}$/)
|
|
46
|
+
expect(typeof body.gasLimit).toBe('number')
|
|
47
|
+
expect(typeof body.gasUsed).toBe('number')
|
|
48
|
+
expect(typeof body.size).toBe('number')
|
|
49
|
+
expect(typeof body.timestamp).toBe('string')
|
|
50
|
+
expect(new Date(body.timestamp).getTime()).toBeGreaterThan(0)
|
|
51
|
+
expect(typeof body.transactionCount).toBe('number')
|
|
52
|
+
// The verbatim JSON-RPC block is always available under `meta.rpc`,
|
|
53
|
+
// where quantities stay hex-encoded.
|
|
54
|
+
expect(body.meta.rpc.number).toMatch(/^0x[0-9a-f]+$/)
|
|
55
|
+
expect(body.meta.rpc.gasUsed).toMatch(/^0x[0-9a-f]+$/)
|
|
56
|
+
expect(body.meta.rpc.timestamp).toMatch(/^0x[0-9a-f]+$/)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('GET /blocks/:block', () => {
|
|
61
|
+
test('resolves a decimal block number', async () => {
|
|
62
|
+
expect(recent).toBeDefined()
|
|
63
|
+
const client = TestApp.client()
|
|
64
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
65
|
+
{ param: { block: recent!.number.toString() }, query: {} },
|
|
66
|
+
TestApp.auth,
|
|
67
|
+
)
|
|
68
|
+
const body = await TestApp.json(response, Blocks.schema.getBlock.Response)
|
|
69
|
+
expect(response.status).toBe(200)
|
|
70
|
+
expect(body.number).toBe(Number(recent!.number))
|
|
71
|
+
expect(body.hash).toBe(recent!.hash)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('resolves a 0x-prefixed hex block number', async () => {
|
|
75
|
+
expect(recent).toBeDefined()
|
|
76
|
+
const hex = `0x${recent!.number.toString(16)}`
|
|
77
|
+
const client = TestApp.client()
|
|
78
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
79
|
+
{ param: { block: hex }, query: {} },
|
|
80
|
+
TestApp.auth,
|
|
81
|
+
)
|
|
82
|
+
const body = await TestApp.json(response, Blocks.schema.getBlock.Response)
|
|
83
|
+
expect(response.status).toBe(200)
|
|
84
|
+
expect(body.number).toBe(Number(recent!.number))
|
|
85
|
+
expect(body.hash).toBe(recent!.hash)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('resolves a 32-byte block hash', async () => {
|
|
89
|
+
expect(recent).toBeDefined()
|
|
90
|
+
const client = TestApp.client()
|
|
91
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
92
|
+
{ param: { block: recent!.hash }, query: {} },
|
|
93
|
+
TestApp.auth,
|
|
94
|
+
)
|
|
95
|
+
const body = await TestApp.json(response, Blocks.schema.getBlock.Response)
|
|
96
|
+
expect(response.status).toBe(200)
|
|
97
|
+
expect(body.number).toBe(Number(recent!.number))
|
|
98
|
+
expect(body.hash).toBe(recent!.hash)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('returns 404 for an unknown block number', async () => {
|
|
102
|
+
// 2^60 is far beyond any plausible head; the node returns null.
|
|
103
|
+
const client = TestApp.client()
|
|
104
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
105
|
+
{ param: { block: '1152921504606846976' }, query: {} },
|
|
106
|
+
TestApp.auth,
|
|
107
|
+
)
|
|
108
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
109
|
+
expect(response.status).toBe(404)
|
|
110
|
+
expect(body.error.code).toBe('block_not_found')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('returns 404 for an unknown block hash', async () => {
|
|
114
|
+
const client = TestApp.client()
|
|
115
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
116
|
+
{ param: { block: zeroHash }, query: {} },
|
|
117
|
+
TestApp.auth,
|
|
118
|
+
)
|
|
119
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
120
|
+
expect(response.status).toBe(404)
|
|
121
|
+
expect(body.error.code).toBe('block_not_found')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('returns 400 for an invalid block identifier', async () => {
|
|
125
|
+
const client = TestApp.client()
|
|
126
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
127
|
+
{ param: { block: 'not-a-block' }, query: {} },
|
|
128
|
+
TestApp.auth,
|
|
129
|
+
)
|
|
130
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
131
|
+
expect(response.status).toBe(400)
|
|
132
|
+
expect(body.error.code).toBe('block_invalid')
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
describe('GET /blocks', () => {
|
|
137
|
+
test('returns a paginated, descending list of lightweight block rows', async () => {
|
|
138
|
+
const client = TestApp.client()
|
|
139
|
+
const response = await client.v1.blocks.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
140
|
+
const body = await TestApp.json(response, Blocks.schema.getBlocks.Response)
|
|
141
|
+
expect(response.status).toBe(200)
|
|
142
|
+
expect(Array.isArray(body.data)).toBe(true)
|
|
143
|
+
expect(body.data.length).toBeGreaterThan(0)
|
|
144
|
+
expect(body.data.length).toBeLessThanOrEqual(5)
|
|
145
|
+
for (const row of body.data) {
|
|
146
|
+
expect(typeof row.number).toBe('number')
|
|
147
|
+
expect(typeof row.timestamp).toBe('string')
|
|
148
|
+
expect(typeof row.transactionCount).toBe('number')
|
|
149
|
+
// Empty blocks are included in the list and correctly report `0`.
|
|
150
|
+
expect(row.transactionCount).toBeGreaterThanOrEqual(0)
|
|
151
|
+
}
|
|
152
|
+
// Default ordering is descending by block number; rows are consecutive
|
|
153
|
+
// (no gaps, since every block — empty or not — appears).
|
|
154
|
+
for (let i = 1; i < body.data.length; i++) {
|
|
155
|
+
expect(body.data[i - 1]!.number).toBeGreaterThan(body.data[i]!.number)
|
|
156
|
+
expect(body.data[i - 1]!.number - body.data[i]!.number).toBe(1)
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('only sets a Cache-Control header for cursor pages', async () => {
|
|
161
|
+
const client = TestApp.client()
|
|
162
|
+
// Head page: a point-in-time read of the live tip, never cached and with
|
|
163
|
+
// no Cache-Control header at all.
|
|
164
|
+
const headResponse = await client.v1.blocks.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
165
|
+
const head = await TestApp.json(headResponse, Blocks.schema.getBlocks.Response)
|
|
166
|
+
expect(headResponse.status).toBe(200)
|
|
167
|
+
expect(headResponse.headers.get('Cache-Control')).toBeNull()
|
|
168
|
+
|
|
169
|
+
// Cursor page: anchored below the head and effectively immutable.
|
|
170
|
+
const cursorResponse = await client.v1.blocks.$get(
|
|
171
|
+
{ query: { limit: '5', cursor: head.nextCursor! } },
|
|
172
|
+
TestApp.auth,
|
|
173
|
+
)
|
|
174
|
+
expect(cursorResponse.status).toBe(200)
|
|
175
|
+
expect(cursorResponse.headers.get('Cache-Control')).toContain('max-age=86400')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test('round-trips an opaque cursor across pages', async () => {
|
|
179
|
+
const client = TestApp.client()
|
|
180
|
+
const firstResponse = await client.v1.blocks.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
181
|
+
const first = await TestApp.json(firstResponse, Blocks.schema.getBlocks.Response)
|
|
182
|
+
expect(typeof first.nextCursor).toBe('string')
|
|
183
|
+
|
|
184
|
+
const secondResponse = await client.v1.blocks.$get(
|
|
185
|
+
{ query: { limit: '5', cursor: first.nextCursor! } },
|
|
186
|
+
TestApp.auth,
|
|
187
|
+
)
|
|
188
|
+
const second = await TestApp.json(secondResponse, Blocks.schema.getBlocks.Response)
|
|
189
|
+
expect(second.data.length).toBeGreaterThan(0)
|
|
190
|
+
// The cursor anchors strictly below the last row of the previous page,
|
|
191
|
+
// so the first row of the next page must be older.
|
|
192
|
+
expect(second.data[0]!.number).toBeLessThan(first.data[first.data.length - 1]!.number)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('serves a bounded positional page via `page`', async () => {
|
|
196
|
+
const client = TestApp.client()
|
|
197
|
+
// Ascending order anchors the feed at the first indexed block, so
|
|
198
|
+
// positional pages are deterministic: the offset page must equal the
|
|
199
|
+
// matching slice of the head page.
|
|
200
|
+
const headResponse = await client.v1.blocks.$get(
|
|
201
|
+
{ query: { limit: '10', order: 'asc' } },
|
|
202
|
+
TestApp.auth,
|
|
203
|
+
)
|
|
204
|
+
const head = await TestApp.json(headResponse, Blocks.schema.getBlocks.Response)
|
|
205
|
+
expect(headResponse.status).toBe(200)
|
|
206
|
+
expect(head.data.length).toBe(10)
|
|
207
|
+
|
|
208
|
+
const offsetResponse = await client.v1.blocks.$get(
|
|
209
|
+
{ query: { limit: '5', order: 'asc', page: '2' } },
|
|
210
|
+
TestApp.auth,
|
|
211
|
+
)
|
|
212
|
+
const page = await TestApp.json(offsetResponse, Blocks.schema.getBlocks.Response)
|
|
213
|
+
expect(offsetResponse.status).toBe(200)
|
|
214
|
+
expect(page.data.map((row) => row.number)).toEqual(
|
|
215
|
+
head.data.slice(5, 10).map((row) => row.number),
|
|
216
|
+
)
|
|
217
|
+
// Offset pages still return a keyset cursor, so a client can jump
|
|
218
|
+
// positionally and then continue deep traversal by cursor.
|
|
219
|
+
expect(typeof page.nextCursor).toBe('string')
|
|
220
|
+
|
|
221
|
+
const continuedResponse = await client.v1.blocks.$get(
|
|
222
|
+
{ query: { cursor: page.nextCursor!, limit: '5', order: 'asc' } },
|
|
223
|
+
TestApp.auth,
|
|
224
|
+
)
|
|
225
|
+
const continued = await TestApp.json(continuedResponse, Blocks.schema.getBlocks.Response)
|
|
226
|
+
expect(continuedResponse.status).toBe(200)
|
|
227
|
+
expect(continued.data[0]!.number).toBe(page.data[page.data.length - 1]!.number + 1)
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
describe('GET /blocks/:block (ISO 8601 timestamp)', () => {
|
|
232
|
+
test('resolves an ISO 8601 timestamp to the first block at or after it', async () => {
|
|
233
|
+
expect(recent).toBeDefined()
|
|
234
|
+
const recentBlock = await Viem.getClient().getBlock({ blockNumber: recent!.number })
|
|
235
|
+
const seconds = Number(recentBlock.timestamp)
|
|
236
|
+
const iso = new Date(seconds * 1000).toISOString()
|
|
237
|
+
const client = TestApp.client()
|
|
238
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
239
|
+
{ param: { block: iso }, query: {} },
|
|
240
|
+
TestApp.auth,
|
|
241
|
+
)
|
|
242
|
+
const body = await TestApp.json(response, Blocks.schema.getBlock.Response)
|
|
243
|
+
expect(response.status).toBe(200)
|
|
244
|
+
expect(typeof body.number).toBe('number')
|
|
245
|
+
// The returned block's timestamp must be >= the requested timestamp.
|
|
246
|
+
expect(new Date(body.timestamp).getTime() / 1000).toBeGreaterThanOrEqual(seconds)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('returns 404 for a far-future timestamp with no indexed block', async () => {
|
|
250
|
+
// Year 3000+ — there will not be an indexed block past this point.
|
|
251
|
+
const client = TestApp.client()
|
|
252
|
+
const response = await client.v1.blocks[':block'].$get(
|
|
253
|
+
{ param: { block: '3000-01-01T00:00:00Z' }, query: {} },
|
|
254
|
+
TestApp.auth,
|
|
255
|
+
)
|
|
256
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
257
|
+
expect(response.status).toBe(404)
|
|
258
|
+
expect(body.error.code).toBe('block_not_found')
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe('GET /chains/:chainId/blocks/by-timestamp (legacy compatibility)', () => {
|
|
263
|
+
test('resolves the latest block at or before the timestamp (default)', async () => {
|
|
264
|
+
expect(recent).toBeDefined()
|
|
265
|
+
const recentBlock = await Viem.getClient().getBlock({ blockNumber: recent!.number })
|
|
266
|
+
const seconds = Number(recentBlock.timestamp)
|
|
267
|
+
const client = TestApp.client()
|
|
268
|
+
const response = await client.chains[':chainId'].blocks['by-timestamp'].$get(
|
|
269
|
+
{ param: { chainId: String(Runtime.get().chainId) }, query: { timestamp: String(seconds) } },
|
|
270
|
+
TestApp.auth,
|
|
271
|
+
)
|
|
272
|
+
const body = await TestApp.json(response, Blocks.schema.getBlockByTimestamp.Response)
|
|
273
|
+
expect(response.status).toBe(200)
|
|
274
|
+
expect(typeof body.block.blockNumber).toBe('number')
|
|
275
|
+
// `before` returns a block at or before the requested instant, within window.
|
|
276
|
+
expect(body.block.blockTimestamp).toBeLessThanOrEqual(seconds)
|
|
277
|
+
expect(body.block.blockTimestamp).toBeGreaterThanOrEqual(seconds - 60)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test('resolves the earliest block at or after the timestamp with closest=after', async () => {
|
|
281
|
+
expect(recent).toBeDefined()
|
|
282
|
+
const recentBlock = await Viem.getClient().getBlock({ blockNumber: recent!.number })
|
|
283
|
+
const seconds = Number(recentBlock.timestamp)
|
|
284
|
+
const client = TestApp.client()
|
|
285
|
+
const response = await client.chains[':chainId'].blocks['by-timestamp'].$get(
|
|
286
|
+
{
|
|
287
|
+
param: { chainId: String(Runtime.get().chainId) },
|
|
288
|
+
query: { timestamp: String(seconds), closest: 'after' },
|
|
289
|
+
},
|
|
290
|
+
TestApp.auth,
|
|
291
|
+
)
|
|
292
|
+
const body = await TestApp.json(response, Blocks.schema.getBlockByTimestamp.Response)
|
|
293
|
+
expect(response.status).toBe(200)
|
|
294
|
+
expect(body.block.blockTimestamp).toBeGreaterThanOrEqual(seconds)
|
|
295
|
+
expect(body.block.blockTimestamp).toBeLessThanOrEqual(seconds + 60)
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
test('returns 404 when no indexed block falls within the window', async () => {
|
|
299
|
+
// Year 3000+ — there will not be an indexed block past this point.
|
|
300
|
+
const client = TestApp.client()
|
|
301
|
+
const response = await client.chains[':chainId'].blocks['by-timestamp'].$get(
|
|
302
|
+
{ param: { chainId: String(Runtime.get().chainId) }, query: { timestamp: '32503680000' } },
|
|
303
|
+
TestApp.auth,
|
|
304
|
+
)
|
|
305
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
306
|
+
expect(response.status).toBe(404)
|
|
307
|
+
expect(body.error.code).toBe('block_not_found')
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
describe('GET /transactions (single-block listing)', () => {
|
|
312
|
+
test('lists transactions in a block via equal `blockNumber.*` bounds', async () => {
|
|
313
|
+
expect(recentNonEmpty).toBeDefined()
|
|
314
|
+
const block = recentNonEmpty!.number.toString()
|
|
315
|
+
const client = TestApp.client()
|
|
316
|
+
const response = await client.v1.transactions.$get(
|
|
317
|
+
{ query: { 'blockNumber.from': block, 'blockNumber.to': block, limit: '10' } },
|
|
318
|
+
TestApp.auth,
|
|
319
|
+
)
|
|
320
|
+
const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
|
|
321
|
+
expect(response.status).toBe(200)
|
|
322
|
+
expect(Array.isArray(body.data)).toBe(true)
|
|
323
|
+
expect(body.data.length).toBeGreaterThan(0)
|
|
324
|
+
// Every returned transaction belongs to the requested block.
|
|
325
|
+
for (const tx of body.data) expect(tx.blockNumber).toBe(Number(recentNonEmpty!.number))
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
test('a block hash composes via GET /blocks/:block, then the bounds', async () => {
|
|
329
|
+
expect(recentNonEmpty).toBeDefined()
|
|
330
|
+
const client = TestApp.client()
|
|
331
|
+
// Resolve the hash to a number through the blocks resource…
|
|
332
|
+
const blockResponse = await client.v1.blocks[':block'].$get(
|
|
333
|
+
{ param: { block: recentNonEmpty!.hash }, query: {} },
|
|
334
|
+
TestApp.auth,
|
|
335
|
+
)
|
|
336
|
+
const block = await TestApp.json(blockResponse, Blocks.schema.getBlock.Response)
|
|
337
|
+
expect(blockResponse.status).toBe(200)
|
|
338
|
+
expect(block.number).toBe(Number(recentNonEmpty!.number))
|
|
339
|
+
|
|
340
|
+
// …then list its transactions with equal bounds.
|
|
341
|
+
const number = block.number.toString()
|
|
342
|
+
const response = await client.v1.transactions.$get(
|
|
343
|
+
{ query: { 'blockNumber.from': number, 'blockNumber.to': number, limit: '10' } },
|
|
344
|
+
TestApp.auth,
|
|
345
|
+
)
|
|
346
|
+
const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
|
|
347
|
+
expect(response.status).toBe(200)
|
|
348
|
+
expect(body.data.length).toBeGreaterThan(0)
|
|
349
|
+
for (const tx of body.data) expect(tx.blockNumber).toBe(block.number)
|
|
350
|
+
})
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
// Walk back from the chain head to find a recent mined block with a stable
|
|
354
|
+
// hash + number. The latest block could change between requests, so seed a
|
|
355
|
+
// concrete identifier once for the dispatch tests. The Tempo RPC node serves
|
|
356
|
+
// the test chain (`42431` → `rpc.testnet.tempo.xyz`).
|
|
357
|
+
async function findRecentBlock() {
|
|
358
|
+
const client = Viem.getClient()
|
|
359
|
+
const latest = await client.getBlockNumber()
|
|
360
|
+
// Step a couple of blocks back from the head to avoid racing against a
|
|
361
|
+
// re-orged head.
|
|
362
|
+
const number = latest > 2n ? latest - 2n : latest
|
|
363
|
+
return await blockByNumber(number)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function blockByNumber(number: bigint) {
|
|
367
|
+
const block = await Viem.getClient().getBlock({ blockNumber: number })
|
|
368
|
+
return { number, hash: block.hash }
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Walk backwards from a small offset below the head until we find a block
|
|
372
|
+
// with at least one transaction. Stepping below the head gives the indexer
|
|
373
|
+
// time to ingest the block before tidx-backed endpoints query it.
|
|
374
|
+
async function findRecentNonEmptyBlock() {
|
|
375
|
+
const client = Viem.getClient()
|
|
376
|
+
const latest = await client.getBlockNumber()
|
|
377
|
+
let cursor = latest > 10n ? latest - 10n : latest
|
|
378
|
+
for (let i = 0; i < 500; i++) {
|
|
379
|
+
const block = await client.getBlock({ blockNumber: cursor })
|
|
380
|
+
if (block.transactions.length > 0) return { number: cursor, hash: block.hash }
|
|
381
|
+
if (cursor === 0n) break
|
|
382
|
+
cursor = cursor - 1n
|
|
383
|
+
}
|
|
384
|
+
return undefined
|
|
385
|
+
}
|