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,408 @@
|
|
|
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 Transactions from './transactions.js'
|
|
9
|
+
|
|
10
|
+
/** The decoded `type` names a humanized transaction may report. */
|
|
11
|
+
const transactionTypes = ['legacy', 'eip2930', 'eip1559', 'eip4844', 'eip7702', 'tempo', 'unknown']
|
|
12
|
+
|
|
13
|
+
let transactionHash: Hex.Hex | undefined
|
|
14
|
+
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
const runtime = Runtime.get()
|
|
17
|
+
transactionHash =
|
|
18
|
+
runtime.mode === 'localnet'
|
|
19
|
+
? runtime.fixtures!.faucetTransactionHashes[0]
|
|
20
|
+
: await findRecentTransactionHash().catch(() => undefined)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('GET /transactions/:transactionHash', () => {
|
|
24
|
+
test('humanizes the transaction, keeps the raw RPC under `meta.rpc`, and embeds the fee token on demand', async () => {
|
|
25
|
+
if (!transactionHash) {
|
|
26
|
+
console.warn('[live-rpc] no recent transaction found; skipping transaction happy path')
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const client = TestApp.client()
|
|
31
|
+
const response = await client.v1.transactions[':transactionHash'].$get(
|
|
32
|
+
{ param: { transactionHash: transactionHash }, query: {} },
|
|
33
|
+
TestApp.auth,
|
|
34
|
+
)
|
|
35
|
+
const body = await TestApp.json(response, Transactions.schema.getTransaction.Response)
|
|
36
|
+
expect(response.status).toBe(200)
|
|
37
|
+
expect(response.headers.get('server-timing')?.includes('transaction;dur=')).toBe(true)
|
|
38
|
+
expect(body.hash).toBe(transactionHash.toLowerCase())
|
|
39
|
+
// The response is humanized: decoded `type`, decimal-string amounts, plain
|
|
40
|
+
// numbers for block/nonce, and ISO-8601 timestamps.
|
|
41
|
+
expect(body.sender).toMatch(/^0x[0-9a-f]{40}$/)
|
|
42
|
+
expect(transactionTypes).toContain(body.type)
|
|
43
|
+
expect(typeof body.gas).toBe('number')
|
|
44
|
+
expect(typeof body.nonce).toBe('number')
|
|
45
|
+
// The verbatim JSON-RPC transaction is always available under `meta.rpc`,
|
|
46
|
+
// where quantities stay hex-encoded and `type` is the raw `0x…` byte.
|
|
47
|
+
expect(body.meta.rpc.hash).toBe(transactionHash.toLowerCase())
|
|
48
|
+
expect(body.meta.rpc.type).toMatch(/^0x[0-9a-f]+$/)
|
|
49
|
+
expect(body.meta.rpc.gas).toMatch(/^0x[0-9a-f]+$/)
|
|
50
|
+
// Receipt-derived fields (execution status, gas used) are not on the
|
|
51
|
+
// transaction; they live on `/transactions/:transactionHash/receipt` instead.
|
|
52
|
+
expect('status' in body).toBe(false)
|
|
53
|
+
expect('gasUsed' in body).toBe(false)
|
|
54
|
+
// Fee-token metadata is opt-in: the base response must not embed it.
|
|
55
|
+
expect(body.meta.feeToken).toBeUndefined()
|
|
56
|
+
|
|
57
|
+
// include=feeToken: resolved metadata is embedded under meta (best-effort).
|
|
58
|
+
const includedResponse = await client.v1.transactions[':transactionHash'].$get(
|
|
59
|
+
{ param: { transactionHash: transactionHash }, query: { include: 'feeToken' } },
|
|
60
|
+
TestApp.auth,
|
|
61
|
+
)
|
|
62
|
+
const includedBody = await TestApp.json(
|
|
63
|
+
includedResponse,
|
|
64
|
+
Transactions.schema.getTransaction.Response,
|
|
65
|
+
)
|
|
66
|
+
expect(includedResponse.status).toBe(200)
|
|
67
|
+
if (includedBody.feeToken) {
|
|
68
|
+
expect(includedBody.feeToken).toMatch(/^0x[0-9a-f]{40}$/)
|
|
69
|
+
// When metadata resolves, it must describe the fee token itself.
|
|
70
|
+
if (includedBody.meta?.feeToken) {
|
|
71
|
+
expect(includedBody.meta.feeToken.address).toBe(includedBody.feeToken)
|
|
72
|
+
expect(typeof includedBody.meta.feeToken.symbol).toBe('string')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('embeds the receipt on demand via include=receipt', async () => {
|
|
78
|
+
const client = TestApp.client()
|
|
79
|
+
// Seed a recent mined hash from the list page so the fixture stays stable
|
|
80
|
+
// across runs.
|
|
81
|
+
const seedResponse = await client.v1.transactions.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
82
|
+
const seed = await TestApp.json(seedResponse, Transactions.schema.getTransactions.Response)
|
|
83
|
+
expect(seedResponse.status).toBe(200)
|
|
84
|
+
const hash = seed.data[0]?.hash
|
|
85
|
+
expect(hash).toBeDefined()
|
|
86
|
+
|
|
87
|
+
const response = await client.v1.transactions[':transactionHash'].$get(
|
|
88
|
+
{ param: { transactionHash: hash! }, query: { include: 'receipt' } },
|
|
89
|
+
TestApp.auth,
|
|
90
|
+
)
|
|
91
|
+
const body = await TestApp.json(response, Transactions.schema.getTransaction.Response)
|
|
92
|
+
expect(response.status).toBe(200)
|
|
93
|
+
expect(response.headers.get('server-timing')?.includes('receipt;dur=')).toBe(true)
|
|
94
|
+
// The verbatim RPC receipt is humanized and embedded under `meta.receipt`,
|
|
95
|
+
// describing the transaction itself; its raw event logs come straight from
|
|
96
|
+
// the RPC response.
|
|
97
|
+
expect({
|
|
98
|
+
logsIsArray: Array.isArray(body.meta.receipt?.logs),
|
|
99
|
+
matchesHash: body.meta.receipt?.transactionHash === body.hash,
|
|
100
|
+
statusDecoded: ['success', 'reverted'].includes(body.meta.receipt?.status ?? ''),
|
|
101
|
+
}).toMatchInlineSnapshot(`
|
|
102
|
+
{
|
|
103
|
+
"logsIsArray": true,
|
|
104
|
+
"matchesHash": true,
|
|
105
|
+
"statusDecoded": true,
|
|
106
|
+
}
|
|
107
|
+
`)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('returns 404 for an unknown transaction', async () => {
|
|
111
|
+
const client = TestApp.client()
|
|
112
|
+
const response = await client.v1.transactions[':transactionHash'].$get(
|
|
113
|
+
{ param: { transactionHash: zeroHash }, query: {} },
|
|
114
|
+
TestApp.auth,
|
|
115
|
+
)
|
|
116
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
117
|
+
expect(response.status).toBe(404)
|
|
118
|
+
expect(body.error.code).toBe('transaction_not_found')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('returns 400 for an invalid transaction hash', async () => {
|
|
122
|
+
// The typed client requires `transaction: `Hex.Hex`` so a literal
|
|
123
|
+
// non-hash input cannot pass through it; exercise the validator via
|
|
124
|
+
// `app.request` for this negative case.
|
|
125
|
+
const response = await TestApp.create().request('/v1/transactions/not-a-hash', {
|
|
126
|
+
headers: { authorization: `Bearer ${TestApp.key.token}` },
|
|
127
|
+
})
|
|
128
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
129
|
+
expect(response.status).toBe(400)
|
|
130
|
+
expect(body.error.code).toBe('transaction_invalid')
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
describe('GET /transactions', () => {
|
|
135
|
+
test('returns a humanized transaction page, embedding fee tokens on demand', async () => {
|
|
136
|
+
const client = TestApp.client()
|
|
137
|
+
const response = await client.v1.transactions.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
138
|
+
const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
|
|
139
|
+
expect(response.status).toBe(200)
|
|
140
|
+
expect(response.headers.get('server-timing')?.includes('transactions;dur=')).toBe(true)
|
|
141
|
+
expect(Array.isArray(body.data)).toBe(true)
|
|
142
|
+
// Cursor (keyset) pagination: the head page has no `page`, but carries an
|
|
143
|
+
// opaque `nextCursor` (a string when more rows follow, else null).
|
|
144
|
+
expect('page' in body).toBe(false)
|
|
145
|
+
expect(body.nextCursor === null || typeof body.nextCursor === 'string').toBe(true)
|
|
146
|
+
expect(body.data.length).toBeLessThanOrEqual(5)
|
|
147
|
+
|
|
148
|
+
// Walking the cursor must yield a disjoint next page (no offset-style overlap).
|
|
149
|
+
if (body.nextCursor) {
|
|
150
|
+
const next = await client.v1.transactions.$get(
|
|
151
|
+
{ query: { limit: '5', cursor: body.nextCursor } },
|
|
152
|
+
TestApp.auth,
|
|
153
|
+
)
|
|
154
|
+
const nextBody = await TestApp.json(next, Transactions.schema.getTransactions.Response)
|
|
155
|
+
expect(next.status).toBe(200)
|
|
156
|
+
const firstHashes = new Set(body.data.map((t) => t.hash))
|
|
157
|
+
for (const t of nextBody.data) expect(firstHashes.has(t.hash)).toBe(false)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (body.data.length > 0) {
|
|
161
|
+
const [transaction] = body.data
|
|
162
|
+
// List rows reuse the detail endpoint's humanized shape: decoded `type`,
|
|
163
|
+
// decimal amounts, plain-number block/nonce, and the verbatim payload
|
|
164
|
+
// under `meta.rpc`.
|
|
165
|
+
expect(transaction!.hash).toMatch(/^0x[0-9a-f]{64}$/)
|
|
166
|
+
expect(transaction!.sender).toMatch(/^0x[0-9a-f]{40}$/)
|
|
167
|
+
expect(transactionTypes).toContain(transaction!.type)
|
|
168
|
+
expect(typeof transaction!.gas).toBe('number')
|
|
169
|
+
expect(typeof transaction!.nonce).toBe('number')
|
|
170
|
+
expect(transaction!.meta.rpc.type).toMatch(/^0x[0-9a-f]+$/)
|
|
171
|
+
// Receipt-derived fields are not on the list payload, matching the detail
|
|
172
|
+
// endpoint; fee-token metadata is opt-in.
|
|
173
|
+
expect('status' in transaction!).toBe(false)
|
|
174
|
+
expect(transaction!.meta.feeToken).toBeUndefined()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const includedResponse = await client.v1.transactions.$get(
|
|
178
|
+
{ query: { limit: '5', include: 'feeToken' } },
|
|
179
|
+
TestApp.auth,
|
|
180
|
+
)
|
|
181
|
+
const includedBody = await TestApp.json(
|
|
182
|
+
includedResponse,
|
|
183
|
+
Transactions.schema.getTransactions.Response,
|
|
184
|
+
)
|
|
185
|
+
expect(includedResponse.status).toBe(200)
|
|
186
|
+
const withFeeToken = includedBody.data.find((transaction) => transaction.meta?.feeToken)
|
|
187
|
+
if (withFeeToken) {
|
|
188
|
+
// When metadata resolves, it must describe the transaction's fee token.
|
|
189
|
+
expect(withFeeToken.meta.feeToken!.address).toBe(withFeeToken.feeToken)
|
|
190
|
+
expect(typeof withFeeToken.meta.feeToken!.symbol).toBe('string')
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('embeds receipts on demand via include=receipt', async () => {
|
|
195
|
+
const client = TestApp.client()
|
|
196
|
+
const response = await client.v1.transactions.$get(
|
|
197
|
+
{ query: { include: 'receipt', limit: '5' } },
|
|
198
|
+
TestApp.auth,
|
|
199
|
+
)
|
|
200
|
+
const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
|
|
201
|
+
expect(response.status).toBe(200)
|
|
202
|
+
expect(body.data.length).toBeGreaterThan(0)
|
|
203
|
+
// Every mined row carries its own receipt under `meta.receipt`, matched by
|
|
204
|
+
// hash, with a decoded status and the page's event logs reattached (the
|
|
205
|
+
// head of the feed reliably contains at least one log-emitting transfer).
|
|
206
|
+
expect({
|
|
207
|
+
everyReceiptMatchesHash: body.data.every(
|
|
208
|
+
(transaction) => transaction.meta.receipt?.transactionHash === transaction.hash,
|
|
209
|
+
),
|
|
210
|
+
everyStatusDecoded: body.data.every((transaction) =>
|
|
211
|
+
['success', 'reverted'].includes(transaction.meta.receipt?.status ?? ''),
|
|
212
|
+
),
|
|
213
|
+
someReceiptHasLogs: body.data.some(
|
|
214
|
+
(transaction) => (transaction.meta.receipt?.logs.length ?? 0) > 0,
|
|
215
|
+
),
|
|
216
|
+
}).toMatchInlineSnapshot(`
|
|
217
|
+
{
|
|
218
|
+
"everyReceiptMatchesHash": true,
|
|
219
|
+
"everyStatusDecoded": true,
|
|
220
|
+
"someReceiptHasLogs": true,
|
|
221
|
+
}
|
|
222
|
+
`)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test('filters by execution status and paginates the receipts-driven path', async () => {
|
|
226
|
+
const client = TestApp.client()
|
|
227
|
+
const response = await client.v1.transactions.$get(
|
|
228
|
+
{ query: { include: 'receipt', limit: '5', status: 'success' } },
|
|
229
|
+
TestApp.auth,
|
|
230
|
+
)
|
|
231
|
+
const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
|
|
232
|
+
expect(response.status).toBe(200)
|
|
233
|
+
expect(body.data.length).toBeGreaterThan(0)
|
|
234
|
+
for (const transaction of body.data) expect(transaction.meta.receipt?.status).toBe('success')
|
|
235
|
+
|
|
236
|
+
// The receipts `(block_num, tx_idx)` cursor pages the status path: page 2
|
|
237
|
+
// parses against the same schema and stays disjoint from page 1.
|
|
238
|
+
expect(typeof body.nextCursor).toBe('string')
|
|
239
|
+
const next = await client.v1.transactions.$get(
|
|
240
|
+
{ query: { cursor: body.nextCursor!, include: 'receipt', limit: '5', status: 'success' } },
|
|
241
|
+
TestApp.auth,
|
|
242
|
+
)
|
|
243
|
+
const nextBody = await TestApp.json(next, Transactions.schema.getTransactions.Response)
|
|
244
|
+
expect(next.status).toBe(200)
|
|
245
|
+
const firstHashes = new Set(body.data.map((transaction) => transaction.hash))
|
|
246
|
+
expect({
|
|
247
|
+
page2Disjoint: nextBody.data.every((transaction) => !firstHashes.has(transaction.hash)),
|
|
248
|
+
page2EveryReceiptSuccessful: nextBody.data.every(
|
|
249
|
+
(transaction) => transaction.meta.receipt?.status === 'success',
|
|
250
|
+
),
|
|
251
|
+
}).toMatchInlineSnapshot(`
|
|
252
|
+
{
|
|
253
|
+
"page2Disjoint": true,
|
|
254
|
+
"page2EveryReceiptSuccessful": true,
|
|
255
|
+
}
|
|
256
|
+
`)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
test('filters reverted transactions by execution status', async () => {
|
|
260
|
+
const client = TestApp.client()
|
|
261
|
+
const response = await client.v1.transactions.$get(
|
|
262
|
+
{ query: { include: 'receipt', limit: '5', status: 'reverted' } },
|
|
263
|
+
TestApp.auth,
|
|
264
|
+
)
|
|
265
|
+
const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
|
|
266
|
+
expect(response.status).toBe(200)
|
|
267
|
+
// The head of the feed may contain no reverted transactions; every returned
|
|
268
|
+
// row must be reverted (vacuously true on an empty page).
|
|
269
|
+
expect(
|
|
270
|
+
body.data.every(
|
|
271
|
+
(transaction) =>
|
|
272
|
+
transaction.meta.receipt?.status === 'reverted' &&
|
|
273
|
+
transaction.meta.receipt.transactionHash === transaction.hash,
|
|
274
|
+
),
|
|
275
|
+
).toBe(true)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test('returns 400 for an invalid filter address', async () => {
|
|
279
|
+
// The typed client requires `sender: `Hex.Hex`` so an invalid literal
|
|
280
|
+
// cannot pass through it; exercise the validator via `app.request`.
|
|
281
|
+
const response = await TestApp.create().request('/v1/transactions?sender=not-an-address', {
|
|
282
|
+
headers: { authorization: `Bearer ${TestApp.key.token}` },
|
|
283
|
+
})
|
|
284
|
+
const body = await TestApp.json(response, Schema.ErrorResponse)
|
|
285
|
+
expect(response.status).toBe(400)
|
|
286
|
+
expect(body.error.code).toBe('query_invalid')
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
test('embeds a capped total count on demand via include=totalCount', async () => {
|
|
290
|
+
const client = TestApp.client()
|
|
291
|
+
|
|
292
|
+
// Seed a real account so the count exercises the address-scope `UNION` path.
|
|
293
|
+
const seed = await client.v1.transactions.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
294
|
+
const seedBody = await TestApp.json(seed, Transactions.schema.getTransactions.Response)
|
|
295
|
+
const account = seedBody.data[0]?.sender
|
|
296
|
+
if (!account) return
|
|
297
|
+
|
|
298
|
+
const response = await client.v1.transactions.$get(
|
|
299
|
+
{ query: { address: account, include: 'totalCount', limit: '5' } },
|
|
300
|
+
TestApp.auth,
|
|
301
|
+
)
|
|
302
|
+
const body = await TestApp.json(response, Transactions.schema.getTransactions.Response)
|
|
303
|
+
expect(response.status).toBe(200)
|
|
304
|
+
expect(response.headers.get('server-timing')?.includes('transactions_count;dur=')).toBe(true)
|
|
305
|
+
expect(body.meta).toBeDefined()
|
|
306
|
+
expect(Number.isInteger(body.meta?.totalCount)).toBe(true)
|
|
307
|
+
expect(body.meta!.totalCount).toBeGreaterThanOrEqual(body.data.length)
|
|
308
|
+
expect(body.meta!.totalCount).toBeLessThanOrEqual(Schema.countCap)
|
|
309
|
+
expect(body.meta!.totalCountCapped).toBe(body.meta!.totalCount >= Schema.countCap)
|
|
310
|
+
|
|
311
|
+
// Without `include=totalCount`, the same page omits `meta` and the count.
|
|
312
|
+
const bare = await client.v1.transactions.$get(
|
|
313
|
+
{ query: { address: account, limit: '5' } },
|
|
314
|
+
TestApp.auth,
|
|
315
|
+
)
|
|
316
|
+
const bareBody = await TestApp.json(bare, Transactions.schema.getTransactions.Response)
|
|
317
|
+
expect(bare.status).toBe(200)
|
|
318
|
+
expect(bareBody.meta).toBeUndefined()
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
// Walk back from the chain head to find a recent mined transaction hash. This
|
|
323
|
+
// keeps the fixture stable across runs without hard-coding a hash that the
|
|
324
|
+
// testnet might prune. The Tempo RPC node serves the test chain
|
|
325
|
+
// (`42431` → `rpc.testnet.tempo.xyz`).
|
|
326
|
+
async function findRecentTransactionHash() {
|
|
327
|
+
const client = Viem.getClient()
|
|
328
|
+
const latest = await client.getBlockNumber()
|
|
329
|
+
for (let n = latest; n > latest - 40n && n >= 0n; n--) {
|
|
330
|
+
const block = await client.getBlock({ blockNumber: n, includeTransactions: true })
|
|
331
|
+
const hash = block.transactions[0]?.hash
|
|
332
|
+
if (hash) return hash
|
|
333
|
+
}
|
|
334
|
+
return undefined
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
describe('GET /transactions?address=…', () => {
|
|
338
|
+
test('returns transactions involving an address', async () => {
|
|
339
|
+
const client = TestApp.client()
|
|
340
|
+
// Seed a real sender from the global transaction feed.
|
|
341
|
+
const seedResponse = await client.v1.transactions.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
342
|
+
const seed = await TestApp.json(seedResponse, Transactions.schema.getTransactions.Response)
|
|
343
|
+
expect(seedResponse.status).toBe(200)
|
|
344
|
+
const account = seed.data[0]!.sender
|
|
345
|
+
expect(account).toMatch(/^0x[0-9a-f]{40}$/)
|
|
346
|
+
|
|
347
|
+
const baseResponse = await client.v1.transactions.$get(
|
|
348
|
+
{ query: { address: account, limit: '5' } },
|
|
349
|
+
TestApp.auth,
|
|
350
|
+
)
|
|
351
|
+
const base = await TestApp.json(baseResponse, Transactions.schema.getTransactions.Response)
|
|
352
|
+
|
|
353
|
+
expect(baseResponse.status).toBe(200)
|
|
354
|
+
expect(baseResponse.headers.get('server-timing')?.includes('transactions;dur=')).toBe(true)
|
|
355
|
+
expect(Array.isArray(base.data)).toBe(true)
|
|
356
|
+
expect(base.data.length).toBeLessThanOrEqual(5)
|
|
357
|
+
// Pagination fields stay at the response root; the feed is cursor (keyset)
|
|
358
|
+
// paginated, so there's no `page` but an opaque `nextCursor`.
|
|
359
|
+
expect('page' in base).toBe(false)
|
|
360
|
+
expect(base.nextCursor === null || typeof base.nextCursor === 'string').toBe(true)
|
|
361
|
+
// Each item is the humanized transaction shape, with the verbatim RPC payload
|
|
362
|
+
// under `meta.rpc`.
|
|
363
|
+
expect(
|
|
364
|
+
base.data.every(
|
|
365
|
+
(entry) =>
|
|
366
|
+
/^0x[0-9a-f]{64}$/.test(entry.hash) &&
|
|
367
|
+
/^0x[0-9a-f]{40}$/.test(entry.sender) &&
|
|
368
|
+
(entry.recipient === null || /^0x[0-9a-f]{40}$/.test(entry.recipient)) &&
|
|
369
|
+
typeof entry.meta.rpc === 'object',
|
|
370
|
+
),
|
|
371
|
+
).toBe(true)
|
|
372
|
+
// Every row touches the subject address on one side or the other.
|
|
373
|
+
expect(
|
|
374
|
+
base.data.every((entry) => entry.sender === account || entry.recipient === account),
|
|
375
|
+
).toBe(true)
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
test('serves a bounded positional page for an address scope via `page`', async () => {
|
|
379
|
+
const client = TestApp.client()
|
|
380
|
+
// Seed a real sender from the global feed. Ascending order anchors the
|
|
381
|
+
// scoped feed at the address's first transaction, so positional pages are
|
|
382
|
+
// deterministic against the head page. This exercises the offset lane of
|
|
383
|
+
// the per-side `UNION` query (branches over-fetch the full window, the
|
|
384
|
+
// outer merge applies the skip).
|
|
385
|
+
const seedResponse = await client.v1.transactions.$get({ query: { limit: '5' } }, TestApp.auth)
|
|
386
|
+
const seed = await TestApp.json(seedResponse, Transactions.schema.getTransactions.Response)
|
|
387
|
+
expect(seedResponse.status).toBe(200)
|
|
388
|
+
const account = seed.data[0]!.sender
|
|
389
|
+
|
|
390
|
+
const headResponse = await client.v1.transactions.$get(
|
|
391
|
+
{ query: { address: account, limit: '10', order: 'asc' } },
|
|
392
|
+
TestApp.auth,
|
|
393
|
+
)
|
|
394
|
+
const head = await TestApp.json(headResponse, Transactions.schema.getTransactions.Response)
|
|
395
|
+
expect(headResponse.status).toBe(200)
|
|
396
|
+
if (head.data.length < 10) return
|
|
397
|
+
|
|
398
|
+
const offsetResponse = await client.v1.transactions.$get(
|
|
399
|
+
{ query: { address: account, limit: '5', order: 'asc', page: '2' } },
|
|
400
|
+
TestApp.auth,
|
|
401
|
+
)
|
|
402
|
+
const page = await TestApp.json(offsetResponse, Transactions.schema.getTransactions.Response)
|
|
403
|
+
expect(offsetResponse.status).toBe(200)
|
|
404
|
+
expect(page.data.map((entry) => entry.hash)).toEqual(
|
|
405
|
+
head.data.slice(5, 10).map((entry) => entry.hash),
|
|
406
|
+
)
|
|
407
|
+
})
|
|
408
|
+
})
|