diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md new file mode 100644 index 0000000000..cc0f2c5528 --- /dev/null +++ b/.agents/AGENTS.md @@ -0,0 +1,26 @@ +# Frontend application for Blockscout + +## Architecture + +See `./rules/architecture.mdc`. + +## Design System Rules + +See `./rules/design-system.mdc`. + +## Code Style & Quality + +See `./rules/code-quality.mdc`. + +## TypeScript Conventions + +See `./rules/typescript.mdc`. + +## Environment Variables + +See `./rules/env-vars.mdc`. + +## Testing + +- Unit tests (`*.spec.ts` / `*.spec.tsx`): See `./rules/tests-unit.mdc`. +- Visual component tests (`*.pw.tsx`): See `./rules/tests-visual.mdc`. diff --git a/.agents/rules/architecture.mdc b/.agents/rules/architecture.mdc new file mode 100644 index 0000000000..818eea2644 --- /dev/null +++ b/.agents/rules/architecture.mdc @@ -0,0 +1,84 @@ +--- +description: Project overview, tech stack, directory layout, data flow, and ongoing client/ architecture migration +globs: +alwaysApply: true +--- +# Project Architecture + +## What this is + +Blockscout frontend — a blockchain explorer UI. Distributed as a Docker image; configured entirely via environment variables at runtime (see `.agents/rules/env-vars.mdc`). + +## Tech stack + +| Layer | Technology | +|---|---| +| Framework | Next.js 16 — **Pages Router only** (not App Router) | +| UI | React 19 | +| Component library | Chakra UI v3 (see `.agents/rules/design-system.mdc`) | +| Server state | React Query 5 | +| Web3 | Wagmi 2 / Viem 2 | +| Schema validation | Valibot | +| Unit tests | Vitest | +| Visual tests | Playwright | +| Package manager | pnpm (Node >=22.14.0) | + +## Domain terminology + +Feature and product codenames used throughout the codebase are defined in `docs/GLOSSARY.md`. Consult it whenever you encounter an unfamiliar term — e.g. `tac`, `bens`, `cctx`, `kettle`, `epoch`, `blobs`. + +## Directory layout + +``` +pages/ Next.js file-based routes — thin wrappers only (dynamic import + getServerSideProps) +ui/ Legacy React UI components organized by feature (~65 subdirectories) → being migrated to client/ +lib/ Legacy business logic, API utilities, custom hooks, context providers → being migrated to client/ +client/ New home for all product code (see Migration section below) +toolkit/ Design system — Chakra wrappers, theme tokens, shared hooks/components (stays here permanently) +configs/app/ Runtime app configuration; must not import from client/ +nextjs/ Next.js config utilities: headers, rewrites, redirects, type-safe routes +mocks/ Shared mock data for tests → being co-located under client/ slices/features +deploy/ Docker scripts, env validator, build tools +docs/ ENVS.md, GLOSSARY.md, CONTRIBUTING.md +``` + +## Data flow + +- **Getting data:** pages fetch data via React Query. Query keys and fetcher functions live in `lib/api/` (moving to `client/api/`). +- **Global UI state:** React Context providers initialized in `pages/_app.tsx` — `AppContextProvider`, `SettingsContextProvider`, `MarketplaceContextProvider`, etc. +- **Real-time data:** WebSocket via `SocketProvider` (moving to `client/api/socket/`). +- **App config:** always read via `configs/app/` (which reads `window.__envs` at runtime) — never `process.env.*` directly in component code. + +--- + +## Ongoing migration: `client/` architecture + +The codebase is being progressively restructured. The long-term target moves all product code from `ui/` and `lib/` into a new `client/` directory with a clear domain-based layout. + +**Blueprint:** `client/ARCH_REDESIGN.md` +**Task backlog and current status:** `client/MIGRATION_TASKS.md` + +### Target layout inside `client/` + +| Directory | Contents | +|---|---| +| `client/shell/` | App chrome: layout, header, footer, navigation, top-bar, root contexts | +| `client/api/` | API transport, fetch utilities, query client, WebSocket; migrated from `lib/api/` and `lib/socket/` | +| `client/slices/` | Core explorer entities always present on any EVM chain (tx, block, address, token, contract, search, …) | +| `client/features/` | Optional / config-gated areas: rollups, chain variants, account, rewards, marketplace, stats, … | +| `client/shared/` | Cross-cutting utilities with no single domain owner (analytics, errors, hooks, router helpers, text utils, …) | + +### Slice vs feature + +- **Slice** — present on every vanilla EVM chain, no feature flag required. Examples: `tx`, `block`, `address`, `token`, `contract`. +- **Feature** — config-gated or chain-specific. Examples: `rollup/optimism`, `chain-variants/celo`, `account`, `stats`, `gas-tracker`. + +### Key rules for migrated code + +- **No barrel `index.ts` files** inside `client/` unless the file defines a genuine public facade (not just `export * from ...`). +- **`client/api` must not have runtime imports** from `client/slices/*` or `client/features/*`; `import type` is allowed. +- **`configs/` never imports `client/`.** +- **`pages/` files are thin wrappers** — a dynamic import and optionally `getServerSideProps`; no UI components or business logic inline. +- **Naming:** kebab-case for directories and non-component modules; `PascalCase.tsx` for React components; `useCamelCase.ts` for hooks. +- **Types:** each slice/feature owns its API response types in `/types/api.ts`. Do not import from deeper internal paths across domain boundaries. +- **When editing legacy `lib/` or `ui/` code:** check `client/MIGRATION_TASKS.md` to see if that area has a migration task in progress or planned. If so, note the target destination in your PR description. diff --git a/.agents/rules/code-quality.mdc b/.agents/rules/code-quality.mdc new file mode 100644 index 0000000000..9971165cb5 --- /dev/null +++ b/.agents/rules/code-quality.mdc @@ -0,0 +1,152 @@ +--- +description: Code quality rules for the Blockscout frontend +globs: *.tsx,*.ts +alwaysApply: false +--- +# Code Quality + +We use ESLint, cSpell and Typescript to maintain code quality and consistency across the project. +In order to check code quality run the following commands: +```bash +pnpm lint:eslint:fix +pnpm lint:tsc +pnpm lint:cspell +``` + +Moreover, please find below the general sense rules that linters do not cover. + +## Code Style and Structure + +### General Principles + +- Write concise, readable TypeScript code +- Use functional and declarative programming patterns; avoid classes +- Prefer iteration and modularization over code duplication +- Implement early returns for better readability +- Structure components logically: exports, subcomponents, helpers, types + +### Naming Conventions + +- Prefer descriptive names with auxiliary verbs (isLoading, hasError) +- Prefix event handlers with "handle" (handleClick, handleSubmit) +- Favor default exports for React components + +Names should be specific and self-documenting. Vague names hide intent and make the codebase harder to navigate. + + +```ts +// BAD +const useWidgets = () => { ... }; + +// GOOD +const useAddress3rdPartyWidgets = () => { ... }; +``` + +This applies to hooks, components, functions, and variables alike.. + +## Specific rules + +### Magic numbers + +Extract magic numbers into named `UPPER_SNAKE_CASE` constants, placed above the component or function that uses them. This makes intent clear and avoids silent duplication. + +```ts +// BAD +const visibleItems = items.slice(0, 4); + +// GOOD +const MAX_VISIBLE_ITEMS = 4; +const visibleItems = items.slice(0, MAX_VISIBLE_ITEMS); +``` + +The same applies to magic strings used as discriminators, keys, or thresholds. In tests, unexplained magic values should also be extracted into named constants so their meaning is clear. + +### Static empty defaults + +Never define empty arrays or objects inline as default values — a new reference is created on every render, causing unnecessary re-renders or stale hook dependencies. + +```ts +// BAD +const items = data ?? []; + +// GOOD +const EMPTY_ITEMS: Array = []; +const items = data ?? EMPTY_ITEMS; +``` + +Define the constant outside the component or hook. + +### useMemo for derived arrays + +Wrap `.filter()`, `.map()`, or `.reduce()` results in `useMemo` when the result is passed as a prop or used as a hook dependency. Without memoisation, a new array reference is produced on every render. + +```ts +// BAD +const filtered = items.filter(isActive); +return ; + +// GOOD +const filtered = useMemo(() => items.filter(isActive), [items]); +return ; +``` + +### eslint-disable comments + +Every `eslint-disable` comment must include an explanation. Without one, the reason for the suppression is lost and the comment becomes a maintenance hazard. + +```ts +// BAD +// eslint-disable-next-line @typescript-eslint/no-explicit-any + +// GOOD +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- API response shape is dynamic and validated at runtime +``` + +### File path string interpolation + +Prefer explicit file names over template literals in asset paths. Explicit names are easier to locate with search tools and eliminate accidental runtime errors. + +```ts +// BAD +const src = `streak_${days}.png`; + +// GOOD — map variants explicitly +const STREAK_IMAGES: Record = { + 30: 'streak_30.png', + 60: 'streak_60.png', +}; +``` + +### Prefer es-toolkit utilities + +Before writing custom utility logic (clamping, deep cloning, grouping, etc.), check whether `es-toolkit` already provides it. Prefer the library function over a manual implementation. Documentation: https://es-toolkit.dev/llms-full.txt + +### Commented-out code + +Remove commented-out code blocks. The git history preserves anything that might be needed later. TODOs are acceptable only when paired with a clear follow-up plan or issue reference. + +### Links + +- Use `toolkit/chakra/link` instead of `next/link`. Never import `Link` from `next/link` directly. +- When links to **application pages** are constructed, verify that `nextjs-routes` or `nextjs/routes` utilities are used instead of string concatenation or template literals. The full list of application routes is available in `nextjs/nextjs-routes.d.ts`. + +### Date and time + +- Import `dayjs` only via `lib/date/dayjs.ts` — never directly from the `dayjs` package. +- Render all dates and times through the shared `Time` or `TimeWithTooltip` components. Do not format timestamps inline. + +### Strict comparison + +Use strict equality (`===`, `!==`) only — never loose equality (`==`, `!=`). Strict operators avoid type coercion surprises and align with TypeScript expectations. + +```ts +// BAD +if (count == 0) { ... } +if (status != 'ok') { ... } + +// GOOD +if (count === 0) { ... } +if (status !== 'ok') { ... } +``` + +When you need to treat both `null` and `undefined` as missing, write `value === null || value === undefined` instead of `value == null`. \ No newline at end of file diff --git a/.agents/rules/design-system.mdc b/.agents/rules/design-system.mdc new file mode 100644 index 0000000000..9a054b1097 --- /dev/null +++ b/.agents/rules/design-system.mdc @@ -0,0 +1,124 @@ +--- +description: Design system and styling rules for the Blockscout frontend +globs: *.tsx,*.ts +alwaysApply: false +--- +# Design System + +## Stack + +The app uses **Chakra UI v3** as its component and styling foundation. + +**Documentation:** https://chakra-ui.com/llms.txt — consult this to understand what components Chakra provides and how its styling system works. + +## Project configuration + +The design system is layered on top of Chakra UI inside `toolkit/`: + +| Path | Purpose | +|---|---| +| `toolkit/chakra/` | Custom wrappers for Chakra components — always prefer these over bare Chakra imports | +| `toolkit/theme/theme.ts` | Theme entry point; uses Chakra v3's `createSystem` API to merge defaults with project config | +| `toolkit/theme/foundations/semanticTokens.ts` | Full list of semantic color tokens (text, bg, border, icon, component-level tokens) | +| `toolkit/theme/foundations/colors.ts` | Raw color palette referenced by semantic tokens | +| `toolkit/theme/recipes/` | Component style recipes (slot recipes and simple recipes) | +| `toolkit/components/` | Custom business components (forms, charts, tabs, etc.) built on top of Chakra | +| `toolkit/hooks/` | Shared React hooks (useDisclosure, useClipboard, etc.) | + +The `Provider` component at `toolkit/chakra/provider.tsx` wraps `ChakraProvider` with the custom theme and color mode support. It must be mounted at the app root. + +## Component import priority + +Always check `toolkit/chakra/` before importing from Chakra UI directly. If a custom wrapper exists there, use it — never the bare Chakra component. + +```ts +// BAD +import { Button } from '@chakra-ui/react'; + +// GOOD +import { Button } from 'toolkit/chakra/button'; +``` + + +## Colors + +Never use raw color values (hex, RGB, HSL). Always reference a token. Three sources are valid: + +1. **Semantic tokens** — context-aware, light/dark aware. Full list in `toolkit/theme/foundations/semanticTokens.ts`. Prefer these whenever a semantic meaning exists. + + ```tsx + + + ``` + + Common groups: `text.*`, `bg.*`, `border.*`, `icon.*`, `link.*`, `button.*`, `badge.*`. + +2. **Project color palette** — scale and alpha colors defined in `toolkit/theme/foundations/colors.ts`: `gray`, `blue`, `red`, `orange`, `yellow`, `green`, `teal`, `cyan`, `purple`, `pink` (steps 50–900), `black`, `white`, `whiteAlpha.*`, `blackAlpha.*`. + + ```tsx + + ``` + +3. **Brand colors** — also defined in `toolkit/theme/foundations/colors.ts`: `github`, `telegram`, `linkedin`, `discord`, `slack`, `twitter`, `opensea`, `facebook`, `medium`, `reddit`, `celo`, `clusters`. + + ```tsx + + ``` + +If a raw color value is truly unavoidable (e.g. a third-party embed), leave a comment explaining why. + +## Design tokens + +The project customizes the following Chakra token categories in `toolkit/theme/`. Always use these tokens instead of raw CSS values: + +| Token type | File | Example | +|---|---|---| +| Border radius | `foundations/borders.ts` | `borderRadius="md"` instead of `borderRadius="12px"` | +| Shadows | `foundations/shadows.ts` | `boxShadow="size.md"` instead of a custom `box-shadow` | +| Z-index | `foundations/zIndex.ts` | `zIndex="modal"` instead of a raw number | +| Font weights | `theme.ts` (inline) | `fontWeight="semibold"` instead of `fontWeight={600}` | +| Durations | `foundations/durations.ts` | Use duration tokens for CSS transitions | +| Keyframes | `foundations/animations.ts` | Reference named keyframes for custom animations | + +Available `radii` tokens: `none`, `sm` (4px), `base` (8px), `md` (12px), `lg` (16px), `xl` (24px), `full`. + +Available `shadows` tokens: `size.xs`, `size.sm`, `size.base`, `size.md`, `size.lg`, `size.xl`, `size.2xl`, `action_bar`, `dark-lg`. + +## Text styles + +Do not set `fontSize` or `lineHeight` directly. Apply the appropriate `textStyle` token instead — it encodes both properties together along with `fontWeight` and `fontFamily`. + +```tsx +// BAD +Label + +// GOOD +Label +``` + +Available text styles (defined in `toolkit/theme/foundations/typography.ts`): + +| Token | fontSize / lineHeight | +|---|---| +| `heading.xl` | 32px / 40px | +| `heading.lg` | 24px / 32px | +| `heading.md` | 18px / 24px | +| `heading.sm` | 16px / 24px | +| `heading.xs` | 14px / 20px | +| `text.xl` | 20px / 28px | +| `text.md` | 16px / 24px | +| `text.sm` | 14px / 20px | +| `text.xs` | 12px / 16px | + +For a regular text block, the `text.` prefix can be omitted. + +## Compound component spacing + +Do not override the default spacing of **internal parts** of compound components (e.g. adding custom padding to `DialogHeader` inside a `Dialog`, or to a `MenuList` item). The root component itself may be spaced freely; its sub-parts may not. + +This rule applies to all components from `toolkit/chakra/`. + +## Duplicated style props + +Before adding a style prop, check whether the same property is already set by an inherited style or a parent component. Flag and remove redundant re-declarations. + diff --git a/.agents/rules/env-vars.mdc b/.agents/rules/env-vars.mdc new file mode 100644 index 0000000000..61b3145578 --- /dev/null +++ b/.agents/rules/env-vars.mdc @@ -0,0 +1,83 @@ +--- +description: Environment variables — where they live, how they're delivered at runtime, validated, and how to add or deprecate them +globs: +alwaysApply: false +--- +# Environment Variables + +## Reference + +All environment variables are documented in `docs/ENVS.md`. This is the authoritative list — every variable must be present there. + +## Runtime delivery (not build-time) + +Almost all variables are injected at **runtime**, not baked into the Next.js build. Only three variables are true build-time constants, compiled into the image via Docker `ARG`: `NEXT_PUBLIC_GIT_COMMIT_SHA`, `NEXT_PUBLIC_GIT_TAG`, `NEXT_OPEN_TELEMETRY_ENABLED`. + +Everything else follows this pipeline: + +**1. Build time — `deploy/scripts/collect_envs.sh`** + +Scans `docs/ENVS.md` for every `NEXT_PUBLIC_*` name and writes `.env.registry` (a list of expected variable names with placeholder values). This registry is copied into the Docker image and tells the runtime which variables the app expects. **If a variable is not in `docs/ENVS.md`, it will not appear in the registry and will not be delivered to the browser.** + +**2. Container startup — `deploy/scripts/entrypoint.sh`** + +Runs in sequence: +1. Optionally loads a named preset from `configs/envs/` +2. Downloads external asset files (images, JSON configs) via `download_assets.sh` +3. Validates environment values against the schema (`validate_envs.sh` → `deploy/tools/envs-validator/`) +4. Writes `public/assets/envs.js` via `make_envs_script.sh`: + ```js + window.__envs = { + NEXT_PUBLIC_API_HOST: "...", + ... + } + ``` + +**3. In the app — `configs/app/utils.ts` `getEnvValue()`** + +Reads `window.__envs` when running in the browser, and `process.env` during SSR / build-time evaluation. + +## Accessing variables in code + +Never read `process.env.NEXT_PUBLIC_*` directly in component or library code. Always go through `configs/app/`, which exposes a frozen, typed config object. The config is split into sections: + +| Section | Purpose | +|---|---| +| `app` | App-level settings (host, protocol, env flags) | +| `apis` | Main API and related service endpoints | +| `chain` | Blockchain parameters | +| `UI` | Visual customizations (colors, fonts, layout) | +| `features` | Feature flags | +| `services` | Third-party service integrations | +| `meta` | SEO and meta-tag settings | + +## Validation + +At container startup, `deploy/tools/envs-validator/` validates the current environment against a [Valibot](https://valibot.dev/) schema defined in `deploy/tools/envs-validator/schema.ts`. The app will not start if validation fails (unless `SKIP_ENVS_VALIDATION=true`). + +For complex config shapes, define a **separate sub-schema** (see `tacSchema`, `beaconChainSchema` for examples) rather than inlining everything. + +## How to add a new variable + +Follow all steps — skipping any one of them will cause the variable to be missing at runtime or fail CI validation. + +1. **`docs/ENVS.md`** — document the variable: name, expected type, required/optional, default value, example value. This step is mandatory; without it the runtime script will not deliver the value to the browser. + +2. **`configs/app/`** — add a property in the appropriate section file (`app.ts`, `apis.ts`, `chain.ts`, `ui.ts`, `features/`, `services.ts`, `meta.ts`). Read the value via `getEnvValue('NEXT_PUBLIC_...')`. Never use the env var name directly outside `configs/app/`. + +3. **`deploy/tools/envs-validator/schema.ts`** — add or update the validation rule. For complex structures, create a dedicated sub-schema. + +4. **`deploy/tools/envs-validator/test/.env.base`** — add the variable to the base test preset. If the variable supports alternative configurations, add examples to `.env.alt` as well. Verify locally: + ```bash + cd deploy/tools/envs-validator && pnpm test + ``` + +5. **CSP** (if the variable holds an external non-asset URL) — add the domain to the appropriate policy in `nextjs/csp/policies/`. Only add the policy when the relevant config option is enabled. + +6. **`deploy/scripts/download_assets.sh`** (if the variable holds an asset URL — image or JSON config) — extend the `ASSETS_ENVS` array with the variable name. + +7. **JSON config URL only** — add an example file to `deploy/tools/envs-validator/test/assets/configs/` (filename derived by stripping `NEXT_PUBLIC_` prefix and `_URL` suffix, lowercased — e.g. `NEXT_PUBLIC_MARKETPLACE_CONFIG_URL` → `marketplace_config.json`) and add the variable name to the `envsWithJsonConfig` array in `deploy/tools/envs-validator/index.ts`. + +## TBD: Deprecating a variable + +> This section is not yet defined. Topics to cover: how to announce deprecation, the grace period before removal, how to update `docs/ENVS.md` and `schema.ts`, and how to handle backwards compatibility for existing deployments. diff --git a/.agents/rules/glossary.mdc b/.agents/rules/glossary.mdc new file mode 100644 index 0000000000..fd04c18e02 --- /dev/null +++ b/.agents/rules/glossary.mdc @@ -0,0 +1,8 @@ +--- +description: Project domain terms and ubiquitous language — consult docs/GLOSSARY.md +alwaysApply: true +--- + +# Ubiquitous language (Glossary) + +Product and feature codenames used in this codebase are defined in `docs/GLOSSARY.md`. Consult it when you encounter an unfamiliar term (e.g. `tac`, `bens`, `cctx`, `kettle`, `epoch`). diff --git a/.agents/rules/tests-unit.mdc b/.agents/rules/tests-unit.mdc new file mode 100644 index 0000000000..b866de4d6d --- /dev/null +++ b/.agents/rules/tests-unit.mdc @@ -0,0 +1,75 @@ +--- +description: Vitest unit tests — purpose, setup, utilities, and conventions +globs: "*.spec.ts,*.spec.tsx" +alwaysApply: false +--- +# Unit Tests (Vitest) + +## Purpose + +Unit tests cover logic that is independent of visual presentation: utility functions, custom hooks, and component behavior (state transitions, conditional rendering, event handling). If a change has no visual output to verify, prefer a Vitest test over a Playwright one — it is faster and cheaper. + +## File naming and location + +Test files must be named `*.spec.ts` or `*.spec.tsx` and placed alongside the code they test. Run all tests: + +```bash +pnpm test:vitest +``` + +Run a single file: + +```bash +pnpm test:vitest path/to/file.spec.ts +``` + +## Setup + +`vitest/setup.ts` runs before each test file and provides: +- **Environment variables** — loaded from `.env.vitest` via dotenv; accessible as `window.__envs` in test code. +- **Fetch mocking** — `vitest-fetch-mock` is initialized globally; all `fetch` calls are interceptable. + +`vitest/global-setup.ts` runs once before the entire test suite and loads `.env.vitest`. + +## Rendering components + +**Never import from `@testing-library/react` directly.** Use the project's custom wrapper instead: + +```tsx +import { render, screen } from 'vitest/lib'; +``` + +`vitest/lib.tsx` re-exports everything from `@testing-library/react` and replaces `render` with a custom version that wraps the component in the full app context: +- `QueryClientProvider` (no retry, no window-focus refetch) +- `AppContextProvider` +- `GrowthBookProvider` +- `SocketProvider` + +The `wrapper` export is also available if you need to pass it separately to RTL hooks: + +```tsx +import { wrapper } from 'vitest/lib'; +const { result } = renderHook(() => useMyHook(), { wrapper }); +``` + +## Utilities + +**`vitest/utils/flushPromises.ts`** — call after an action that triggers async effects (e.g. a state update followed by a data fetch) to flush all pending microtasks before asserting: + +```tsx +import flushPromises from 'vitest/utils/flushPromises'; + +await userEvent.click(button); +await flushPromises(); +expect(screen.getByText('Loaded')).toBeInTheDocument(); +``` + +## Mocking fetch responses + +`vitest-fetch-mock` is active globally. Mock responses before the code under test runs: + +```tsx +fetchMock.mockResponseOnce(JSON.stringify({ data: 'value' })); +``` + +Reset mocks between tests using `beforeEach` / `afterEach` if needed. diff --git a/.agents/rules/tests-visual.mdc b/.agents/rules/tests-visual.mdc new file mode 100644 index 0000000000..81d902b473 --- /dev/null +++ b/.agents/rules/tests-visual.mdc @@ -0,0 +1,189 @@ +--- +description: Playwright component visual tests — purpose, setup, fixtures, and conventions +globs: "*.pw.tsx" +alwaysApply: false +--- +# Visual Component Tests (Playwright) + +## Purpose + +Playwright tests verify the visual presentation and interactive behavior of UI components across multiple viewports and color modes. Use them when a change affects what the user sees — layout, styling, conditional UI states. + +## File naming and location + +Test files must be named `*.pw.tsx` and placed alongside the component they test. Run all tests: + +```bash +pnpm test:pw # locally (fast, no Docker) +pnpm test:pw:docker # in Docker (required for screenshot updates) +pnpm test:pw:docker path/to/Component.pw.ts --update-snapshots # Run single file in Docker and update all corresponding screenshots +``` + +**Never commit screenshots generated by `pnpm test:pw` (local).** Screenshots used in CI must be generated via `pnpm test:pw:docker`, otherwise tests will fail in CI due to rendering differences. + +## Test projects + +Three projects run against every test file: + +| Project | Tag | Browser | Viewport | +|---|---|---|---| +| `default` | _(runs unless excluded with `-@default`)_ | Desktop Chrome | 1200×750 | +| `mobile` | `+@mobile` | iPhone 13 Pro (Safari) | 375×812 | +| `dark-color-mode` | `+@dark-mode` | Desktop Chrome | 1200×750, forced dark | + +Tag a test to include or exclude it from a project: + +```tsx +test('renders correctly +@mobile', async ({ render }) => { ... }); +test('mobile only -@default +@mobile', async ({ render }) => { ... }); +test('dark mode variant +@dark-mode', async ({ render }) => { ... }); +``` + +## Imports + +**Never import directly from `@playwright/experimental-ct-react`.** Use the project's extended test: + +```tsx +import { test, expect } from 'playwright/index'; +``` + +`playwright/index.ts` extends Playwright's `test` with all project fixtures and re-exports everything from `@playwright/experimental-ct-react`. + +## Component mounting + +Use the `render` fixture — it mounts the component inside `TestApp`, which provides the full provider stack: ChakraProvider, QueryClientProvider, SocketProvider, AppContextProvider, SettingsContextProvider, GrowthBookProvider, WagmiProvider, RewardsContextProvider, and CsvExportContextProvider. + +```tsx +test('example', async ({ render }) => { + const component = await render(); + await expect(component).toHaveScreenshot(); +}); +``` + +`render` accepts optional second and third arguments to customize `hooksConfig` (mainly used for mocking Next.js router object), `appContext` and `marketplaceContext` props passed to `TestApp`. + +## Fixtures + +All fixtures are injected via the `test` function. Available fixtures: + +### `mockApiResponse` +Intercepts Blockscout API calls. Returns the mocked URL. + +```tsx +await mockApiResponse('token', tokenMock, { + pathParams: { hash: '0x...' }, + queryParams: { tab: 'holders' }, + status: 200, // optional, default 200 + times: 1, // optional, intercept N times +}); +``` + +### `mockEnvs` +Sets environment variables for the test (stored in localStorage, read by `getEnvValue`). Use the built-in `ENVS_MAP` for common configs: + +```tsx +await mockEnvs([ + ['NEXT_PUBLIC_ROLLUP_TYPE', 'optimistic'], + ['NEXT_PUBLIC_API_HOST', 'localhost'], +]); +``` + +### `mockFeatures` +Sets feature flags in localStorage: + +```tsx +await mockFeatures([ + ['account', true], + ['rewards', false], +]); +``` + +### `mockAssetResponse` +Routes any URL to serve a file from disk: + +```tsx +await mockAssetResponse('https://example.com/logo.png', './playwright/mocks/duck.png'); +``` + +### `mockConfigResponse` +Routes an external config asset URL (JSON or image) derived from an env var name: + +```tsx +await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://example.com/config.json', content); +``` + +### `mockRpcResponse` +Mocks RPC calls (typed against viem's `PublicRpcSchema`): + +```tsx +await mockRpcResponse([{ method: 'eth_blockNumber', result: '0x1' }]); +``` + +### `mockContractReadResponse` +Mocks `eth_call` for a specific contract read: + +```tsx +await mockContractReadResponse({ + abiItem: erc20ABI[0], + args: ['0x123'], + address: '0x00ab', + result: '1000000', +}); +``` + +### `createSocket` +Creates a local WebSocket server on port 3200. Resolves when a client connects: + +```tsx +const socket = await createSocket(); +// push events via socket +``` + +### `mockTextAd` +Mocks the text ad provider (Sevio + duck ad data). Call this in tests that render ad-containing layouts to avoid network requests and visual noise. + +### `injectMetaMaskProvider` +Injects `window.ethereum` with `isMetaMask: true`: + +```tsx +await injectMetaMaskProvider(); +``` + +### `auth` helpers +For tests requiring an authenticated user, use the `contextWithAuth` fixture or call `authenticateUser(context)` to set the API token cookie. + +### `mockMultichainConfig` +Injects `window.__multichainConfig` with 5 mock chains (chainA–E). Use in tests that render multichain UI. + +## Selectors + +Always use semantic selectors — never CSS class names. CSS classes are implementation details and break when styling changes. + +```tsx +// BAD +page.locator('.tx-hash-link') + +// GOOD +page.getByRole('link', { name: /transaction/i }) +page.getByLabel('Address menu') +page.getByText('0x1234…') +page.getByTestId('tx-hash') +``` + +## Environment and date + +`playwright/lib.tsx` runs before every component mount and: +- Mocks `next/router.useRouter()` with a configurable router object. +- Fixes the current date to `2022-11-11T12:00:00Z` via MockDate — all `new Date()` / `Date.now()` calls return this value. + +## Mock data + +Use existing files from `mocks/` and `playwright/mocks/` rather than hardcoding values inline. The `mocks/` directory contains shared mock data (API responses, entities) used across both Vitest and Playwright tests. `playwright/mocks/` contains Playwright-specific assets (images, JSON files, module mocks). + +Extract any non-obvious test values (IDs, amounts, hashes) into named constants above the test suite. + +## Conventions + +- Do not use the `testFn: TestFixture<...>` pattern. It makes refactoring harder and can bypass Playwright lint rules. Use it only when genuinely sharing fixture logic across multiple suites. +- Keep each test focused on one visual state or interaction. +- Screenshots are the contract — keep them up to date and review diffs carefully in PRs. diff --git a/.cursor/rules/typescript.mdc b/.agents/rules/typescript.mdc similarity index 84% rename from .cursor/rules/typescript.mdc rename to .agents/rules/typescript.mdc index 632369e7e6..dcfb6acd3b 100644 --- a/.cursor/rules/typescript.mdc +++ b/.agents/rules/typescript.mdc @@ -8,7 +8,6 @@ alwaysApply: false - Use TypeScript for all code - Prefer interfaces over types - Implement proper type safety and inference -- Use `satisfies` operator for type validation ## Import types @@ -80,7 +79,21 @@ const youSayGoodbyeISayHello = < }; ``` -Outside of generic functions, use `any` extremely sparingly. +Outside of generic functions, use `any` extremely sparingly. Prefer `unknown` with proper narrowing instead: + +```ts +// BAD +function process(value: any) { + return value.name; +} + +// GOOD +function process(value: unknown) { + if (typeof value === 'object' && value !== null && 'name' in value) { + return (value as { name: string }).name; + } +} +``` ## Default exports @@ -444,3 +457,54 @@ if (result.ok) { console.error(result.error); } ``` + + +## Type assertions and satisfies + +Prefer `satisfies` over `as` assertions for type validation. `as` silently widens or narrows the type; `satisfies` validates without losing inference. + +```ts +// BAD +const items = response.data as MyType[]; + +// GOOD +const items = response.data satisfies Array; +``` + +Avoid double-cast coercions (`as unknown as MyType`). If a cast is unavoidable, add a comment explaining why it is safe: + +```ts +// BAD +const el = ref.current as unknown as HTMLInputElement; + +// GOOD — explain why the double cast is safe +// The portal always renders an , so this cast is guaranteed at runtime. +const el = ref.current as unknown as HTMLInputElement; +``` + + +## Window globals + +Never access third-party globals via `(window as any)`. Declare the property inside the existing `declare global { interface Window { ... } }` block in `global.d.ts`: + +```ts +// BAD +const analytics = (window as any).analytics; + +// GOOD — in global.d.ts +declare global { + interface Window { + analytics: SegmentAnalytics.AnalyticsJS; + } +} +``` + + +## Module type declarations + +Use `decs.d.ts` exclusively for untyped third-party module declarations. Do not put other type definitions there. + +```ts +// decs.d.ts +declare module 'some-untyped-package'; +``` diff --git a/.agents/skills/arch-migrate/SKILL.md b/.agents/skills/arch-migrate/SKILL.md new file mode 100644 index 0000000000..b86b301c25 --- /dev/null +++ b/.agents/skills/arch-migrate/SKILL.md @@ -0,0 +1,112 @@ +--- +name: arch-migrate +description: Execute a migration task from a GitHub issue through to a PR, or fix unresolved review comments on an existing migration PR. Reads scope and acceptance criteria from the issue body. +--- + +You are executing a step of the Blockscout frontend client architecture migration. + +## Invocation +The skill can **ONLY** be invoked as: `/arch-migrate ("execute" mode)` or `/arch-migrate fix ("fix review comments" mode)`. + +## Prerequisites + +Follow the **check-github-cli** skill first to ensure `gh` is available and authenticated. + +## Mode + +**Execute** (`/arch-migrate `): fetch the GitHub issue and execute the migration through to a PR. + +**Fix** (`/arch-migrate fix `): address unresolved review comments on an existing PR. + +--- + +## Execute mode + +### 1. Read context + +```bash +gh issue view --repo blockscout/frontend +``` + +Also read: +- `client/ARCH_REDESIGN.md` — naming conventions (§2), dependency rules (§8), execution rules (§10) +- `docs/GLOSSARY.md` — domain terminology + +The issue body contains the **Scope**, **Findings**, and **Acceptance criteria** for this task. That is your working spec. + +### 2. Explore before touching +Read all source files listed in the issue **Scope**. Understand what they export and what tests exist. + +### 3. Move files +- Destination comes from the issue Scope and `ARCH_REDESIGN.md §6` migration map. +- Rename files to kebab-case **at move time** — no separate rename PR. +- Component files stay PascalCase. Hook files stay `useCamelCase.ts`. Everything else: kebab-case. +- Extract types/interfaces flagged in the issue **Findings** section into their new homes. + + +### 4. Update all imports in the same PR +- Update every import path across the entire repo. +- For high-fanout files, write a codemod script, run it, then **delete the script** — do not commit it to the repo. Commit only the resulting file changes. +- No re-export shims. No long-lived compatibility aliases. + +### 5. Verify dependency rules +- `client/api` must not have runtime imports from `client/slices/*` or `client/features/*`. `import type` is allowed. +- No new import cycles within `client/`. Fix all ESLint errors inside `client/`. +- Warnings in legacy `lib/` and `ui/` paths are expected and do not block the PR. + +### 6. Run checks +``` +pnpm lint:tsc # must pass +pnpm lint:eslint:fix # must pass; legacy warnings acceptable +``` + +### 7. Cross-slice dependencies left at old paths +If a dependency is not yet migrated, leave its import at the old path and list it explicitly in the PR description as a follow-up for the relevant future task. + +## Branch and PR + +- Extract the task ID from the issue title (format: `[Migration] : ...`). +- Branch name: `migration/-` (e.g. `migration/1-1-client-api`) +- Branch from `main`. +- PR title: `[Migration ] ` +- PR targets `main`. +- PR body must include: + - `Closes #` — on the first line, so GitHub auto-closes the issue on merge + - What moved and where + - Any codemods run (include the command / script body used, but do not commit the script itself) + - Cross-slice deps left at legacy paths (list file + old import path) + - Checklist: `pnpm lint:tsc` passing, `pnpm lint:eslint` clean within `client/` +- PR labels: `refactoring` + +--- + +## Fix mode + +`/arch-migrate fix ` — address unresolved review comments only. + +1. Fetch review threads, filtering to unresolved only: + +```bash +gh api graphql -f query=' +{ + repository(owner: "blockscout", name: "frontend") { + pullRequest(number: ) { + reviewThreads(first: 50) { + nodes { + isResolved + path + line + comments(first: 5) { + nodes { body author { login } } + } + } + } + } + } +}' +``` + +Keep only threads where `isResolved: false`. Skip everything else — the reviewer marks threads resolved when they are no longer relevant. + +2. Address each unresolved thread. Push fixes to the existing branch. +3. Summarise which threads were addressed and what changed. diff --git a/.agents/skills/arch-research/SKILL.md b/.agents/skills/arch-research/SKILL.md new file mode 100644 index 0000000000..8d1d927234 --- /dev/null +++ b/.agents/skills/arch-research/SKILL.md @@ -0,0 +1,196 @@ +--- +name: arch-research +description: Research a migration task, create a GitHub sub-issue with scope and acceptance criteria, and update the task backlog. Takes a task ID from docs/MIGRATION_TASKS.md, explores the codebase, creates an issue in blockscout/frontend, links it to the parent migration issue, and commits the status update. +--- + +You are preparing a migration task for execution by exploring the codebase and producing a well-scoped GitHub issue. + +## Invocation +The skill can **ONLY** be invoked as: `/arch-research ` (e.g. `/arch-research 1-1`) + +## Prerequisites + +Follow the **check-github-cli** skill first to ensure `gh` is available and authenticated. + +## Steps + +### 1. Find the task + +Read `client/MIGRATION_TASKS.md`. Locate the task by the given ID (e.g. `1-1`). Read its **Scope** section. + +If the task already has `[~]` or `[x]` status, stop and report that it has already been researched or completed. + +Also read: +- `client/ARCH_REDESIGN.md` — conventions and migration map (§2, §4, §6) +- `docs/GLOSSARY.md` — domain terminology + +### 2. Plan the areas + +Do a quick top-level scan of the task Scope and the standard source directories to understand what exists. Then present the user with the list of areas you will cover, in order, with a one-line description of what each contains for this specific task. Skip any area where you found nothing relevant. + +**Standard areas (always in this order):** + +1. **Types** — `types/api/*.ts`, `types/client/*.ts`, `types/views/*.ts`, and any related param/shared type files +2. **Stubs and mocks** — `stubs/*.ts`, `mocks/*.ts` +3. **Hooks, utilities, and contexts** — `lib/hooks/`, `lib//`, `lib/contexts/`, and any `utils/` files within `ui//` +4. **Shared components** — `ui/shared/entities//`, `ui/shared//` +5. **Pages** — `ui/pages/*.tsx`, `ui//` (the detail page and all its tabs, plus any index/list pages) +6. **Feature impact** — all features identified as affected across the previous areas + +Present as a short numbered list. For each area, note the rough file count or key directories found. End with: *"I'll work through them one at a time. Let me know if you want to skip or reorder any, otherwise I'll start with Area 1."* + +Wait for a go-ahead (or redirect) before proceeding. + +### 3. Analyze each area, one at a time + +Work through each area in sequence. For each one: + +**a. Analyze** — read the relevant files and apply the rules below that apply to this area. + +**b. Present a mini-plan** in this format: + +``` +### Area [N/total]: [Name] + +[One sentence describing what this area covers for this task.] + +**Plan** +| Source | Destination | +|--------|-------------| +| ... | ... | + +**Findings** +- [extractions, splits, mixed-concern files, conflicts, or "None."] +``` + +**c. Wait for explicit approval.** End each area with: *"Any corrections, or shall I move to Area [N+1]: [name]?"* + +Apply any corrections. If the changes are significant, re-show the updated mini-plan before proceeding. Do not move to the next area until the user approves the current one. + +--- + +**Rules to apply per area:** + +**Types (Area 1)** +- Two strictly separate type layers: + - `types/api.ts` destination: API/DTO shapes from `types/api/*.ts`. For each optional field group that maps to a feature (e.g. `celo?`, `arbitrum?`), extract it to `client/features//types/api.ts`. The slice's own `types/api.ts` composes them via `interface Entity extends FeatureTypeA, FeatureTypeB, ...`. + - `types/client.ts` destination: frontend-only derived types from `types/client/*.ts` and `types/views/*.ts`. Same feature decomposition applies — feature-specific client types go to `client/features//types/client.ts`. Never mix API shapes and client types in the same file. +- Verify the path exists for each source file; note any missing files. +- Check whether destination `types/api.ts` / `types/client.ts` already exists in `client/` (conflict check). + +**Stubs and mocks (Area 2)** +- Identify which stubs entries are feature-specific vs. entity-core. Feature-specific entries go to `client/features//stubs/.ts` (named after the slice, not the feature). Slice keeps only entity-core entries. +- Flag any high-fanout stub constants (e.g. `ENTITY_HASH`, `ENTITY_PARAMS`) that are imported across many test files — these require a codemod; note this in Findings. + +**Hooks, utilities, and contexts (Area 3)** +- For each hook file: check if it reads `config.features.`. If yes → `client/features//hooks/`. If no → `client/slices//hooks/` (or wherever the slice keeps its hooks). +- `lib/contexts/` scan: look for files whose name contains the entity name. Destination is `client/slices//contexts/` or `client/features//contexts/` — a dedicated sub-folder, not the root. +- Mixed-concern utility files: open any `utils.ts` in scope and check if it mixes logic from different domains. If yes, split rather than move; list what each new file should contain. + +**Shared components (Area 4)** +- `ui/shared/` sweep: search for components whose filename begins with the slice entity prefix. These are slice-owned components that ended up in the shared bucket. +- For each component file with a sibling `.pw.tsx` or `__screenshots__/` directory, include those siblings in the mapping. +- Classify each component: core slice → `client/slices//components/`; feature/chain-specific → `client/features//components/`. + +**Pages (Area 5)** +- `ui/pages/` sweep: search for page components whose filename begins with the entity prefix. **Also** scan the Next.js `pages/` directory for entries that `dynamic(() => import('ui/pages/'))` a component related to the entity — page filenames may not match the route (e.g. `Accounts.tsx` → `/accounts`). +- For a tabbed detail page, each tab becomes a named sub-folder under `pages/details/` (e.g. `info/`, `txs/`, `token-transfers/`). Feature-owned tab components go to `client/features//pages//` — never as sub-folders inside the slice's `pages/` tree. +- For index/list pages, all related list-item and table components go flat into `pages/index/`. +- Include `.pw.tsx` siblings and `__screenshots__/` directories for every page component. +- For each Next.js entry file, note the import path update required (the entry file itself stays in `pages/`). + +**Feature impact (Area 6)** +- Compile all features identified in Areas 1–5. +- For each feature, list: feature path, files to create or merge (`types/api.ts`, `types/client.ts`, `stubs/.ts`, page components), and what each file should contain. +- If no features are affected, state that explicitly. + +### 4. Compile and confirm + +Once all areas are approved, assemble the full issue body: + +```markdown +## Scope + +[All approved source → destination file mappings, organised by slice and feature. One mapping table per section.] + +## Feature impact + +[For each affected feature: + - Feature path + - Files to create or merge, and what they should contain +If none: "No feature-side files required."] + +## Findings + +[All Findings from the individual areas, consolidated. Remove duplicates.] + +If nothing non-obvious was found: "No conflicts or extractions identified — straightforward move." + +## Acceptance criteria + +- [ ] All files moved to target paths per `ARCH_REDESIGN.md §6` +- [ ] All import paths updated repo-wide (no references to old paths remain) +- [ ] Extracted API types live in their new slice/feature `types/api.ts` +- [ ] Extracted client/UI types live in their new slice/feature `types/client.ts` +- [ ] `pnpm lint:tsc` passes +- [ ] `pnpm lint:eslint:fix` clean within `client/` (warnings in legacy paths acceptable) +- [ ] Cross-slice deps left at old paths are explicitly listed in the PR description +``` + +Present the full draft with the issue title (`[Migration] : `) and ask: *"Shall I create this issue? You can request changes before I proceed."* + +Wait for explicit confirmation. Apply any requested edits and re-confirm if the changes are significant. Do not proceed to step 5 until the user approves. + +### 5. Create the GitHub issue + +```bash +gh issue create \ + --repo blockscout/frontend \ + --title "[Migration] : " \ + --body "" \ + --label "Task for agent" +``` + +Capture the issue number from the output URL (e.g. `https://github.com/blockscout/frontend/issues/42` → number is `42`). + +### 6. Link to the parent issue + +Get the internal numeric `id` of the new issue (this is different from the issue number): + +```bash +gh api repos/blockscout/frontend/issues/ --jq '.id' +``` + +Link it as a sub-issue of the parent (parent issue number is in the `client/MIGRATION_TASKS.md` header): + +```bash +gh api \ + --method POST \ + repos/blockscout/frontend/issues//sub_issues \ + --field sub_issue_id= +``` + +### 7. Update client/MIGRATION_TASKS.md + +In the task entry, make two changes: +- Change status from `[ ]` to `[~]` +- Append the issue link to the task heading line + +Example — before: +``` +### 1-1 · [ ] Migrate `client/api/` +``` +After: +``` +### 1-1 · [~] Migrate `client/api/` · [#42](https://github.com/blockscout/frontend/issues/42) +``` + +Commit directly to `main`: + +```bash +git add client/MIGRATION_TASKS.md +git commit -m "track: 1-1 in progress — blockscout/frontend#42" +git push origin main +``` + +Replace `1-1` and `42` with the actual task ID and issue number. diff --git a/.cursor/skills/check-github-cli/SKILL.md b/.agents/skills/check-github-cli/SKILL.md similarity index 100% rename from .cursor/skills/check-github-cli/SKILL.md rename to .agents/skills/check-github-cli/SKILL.md diff --git a/.cursor/skills/create-issue-from-slack-thread/SKILL.md b/.agents/skills/create-issue-from-slack-thread/SKILL.md similarity index 100% rename from .cursor/skills/create-issue-from-slack-thread/SKILL.md rename to .agents/skills/create-issue-from-slack-thread/SKILL.md diff --git a/.cursor/skills/create-pr/SKILL.md b/.agents/skills/create-pr/SKILL.md similarity index 99% rename from .cursor/skills/create-pr/SKILL.md rename to .agents/skills/create-pr/SKILL.md index cd2daca682..86b88310a1 100644 --- a/.cursor/skills/create-pr/SKILL.md +++ b/.agents/skills/create-pr/SKILL.md @@ -1,7 +1,6 @@ --- name: create-pr description: Create a well-structured pull request with proper description, labels, and reviewers -disable-model-invocation: true --- # Create PR diff --git a/.cursor/skills/deploy-demo/SKILL.md b/.agents/skills/deploy-demo/SKILL.md similarity index 100% rename from .cursor/skills/deploy-demo/SKILL.md rename to .agents/skills/deploy-demo/SKILL.md diff --git a/.cursor/skills/get-checks-status/SKILL.md b/.agents/skills/get-checks-status/SKILL.md similarity index 100% rename from .cursor/skills/get-checks-status/SKILL.md rename to .agents/skills/get-checks-status/SKILL.md diff --git a/.cursor/skills/grill-me/SKILL.md b/.agents/skills/grill-me/SKILL.md similarity index 100% rename from .cursor/skills/grill-me/SKILL.md rename to .agents/skills/grill-me/SKILL.md diff --git a/.cursor/skills/prepare-release/SKILL.md b/.agents/skills/prepare-release/SKILL.md similarity index 100% rename from .cursor/skills/prepare-release/SKILL.md rename to .agents/skills/prepare-release/SKILL.md diff --git a/.cursor/skills/prepare-release/fetch-release-prs.js b/.agents/skills/prepare-release/fetch-release-prs.js similarity index 100% rename from .cursor/skills/prepare-release/fetch-release-prs.js rename to .agents/skills/prepare-release/fetch-release-prs.js diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 120000 index 0000000000..b1cdfc35c2 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +../.agents/AGENTS.md \ No newline at end of file diff --git a/.claude/rules b/.claude/rules new file mode 120000 index 0000000000..2d5c9a97a7 --- /dev/null +++ b/.claude/rules @@ -0,0 +1 @@ +../.agents/rules \ No newline at end of file diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000000..2b7a412b8f --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/.codex/environments/environment.toml b/.codex/environments/environment.toml new file mode 100644 index 0000000000..11d8f29137 --- /dev/null +++ b/.codex/environments/environment.toml @@ -0,0 +1,11 @@ +# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY +version = 1 +name = "frontend" + +[setup] +script = "" + +[[actions]] +name = "Eth" +icon = "run" +command = "pnpm run dev:preset eth" diff --git a/.cursor/BUGBOT.md b/.cursor/BUGBOT.md index f8d389874b..b9e7aee4c1 100644 --- a/.cursor/BUGBOT.md +++ b/.cursor/BUGBOT.md @@ -4,50 +4,21 @@ In addition to the regular bug-finding process, check the following aspects in t ## 1. Design System & Theming -- Verify that all color values use **semantic color tokens** from the project's design system (`text.secondary`, `border.divider`, `icon.secondary`, `button.solid.bg`, `global.body.bg`, etc.). Flag any hardcoded color values such as `rgb(...)`, `#hex`. The full list of available tokens can be found in `toolkit/theme/foundations/semanticTokens.ts` and `toolkit/theme/foundations/colors.ts`. In rare cases the author may use hardcoded color values, but they must leave a comment explaining why. -- Flag any custom `box-shadow` values. Advise the author to use the design system's shadow tokens instead. -- Check for custom margins or paddings that override defaults of **internal parts** of compound components (e.g., `DialogHeader` inside a `Dialog`). This rule does not apply to the root component itself. If a part component already has default spacing from the design system, it should not be overridden with custom values. This applies to all components from the toolkit (see `toolkit/chakra`). -- Look for duplicated style props — check whether a property is already set by an inherited style prop and flag any redundant re-declarations. -- Refer to the Chakra documentation at [llms.txt](https://chakra-ui.com/llms.txt) to understand what components Chakra UI provides and how the styling system works. +See `.agents/rules/design-system.mdc`. -## 2. TypeScript & Code Quality +## 2. TypeScript -- Check for usage of the `any` type. If found, advise the author to use `unknown` instead and narrow it properly in the code. -- Look for `eslint-disable` comments. If any exist without an accompanying explanation, ask the author to add a comment describing why the rule was disabled. Example: `// eslint-disable-next-line @typescript-eslint/no-explicit-any -- API response shape is dynamic and validated at runtime`. -- Identify magic numbers in the code. Advise the author to extract them into named constants using `UPPER_UNDERSCORE_CASE` and place the constants above the component definition. For example, `const MAX_VISIBLE_ITEMS = 4;` is preferable to an inline `4`. -- Evaluate naming of hooks, features, components, functions, and variables. Names should be specific and self-documenting. Flag vague names like `useWidgets` and suggest more descriptive alternatives such as `useAddress3rdPartyWidgets`. -- Where an `as MyType[]` assertion is used, suggest using `satisfies Array` instead, if applicable. -- Check for inline empty arrays or objects used as default values on every render (e.g., `?? []`). Advise the author to define a static constant outside the component (e.g., `const EMPTY_ARRAY: Array = [];`) and reference it instead. -- Check whether `.filter()`, `.map()`, or `.reduce()` calls produce new array references that are passed as props or used as hook dependencies. If so, advise the author to wrap them in `useMemo`. -- Flag unnecessary string interpolation in file paths. Explicit file names (e.g., `streak_30.png`) are preferable to template literals (e.g., `` `streak_${days}.png` ``), as they are easier to locate with search tools. +See `.agents/rules/typescript.mdc`. -## 3. Environment Variables +## 3. Code Quality -When a PR adds, changes, renames, or removes an environment variable, verify that **all** of the following steps have been completed: +See `.agents/rules/code-quality.mdc`. -- The variable is documented in `docs/ENVS.md` with its name, expected type, compulsoriness, default value, and an example value. -- The variable is added to the app config in the appropriate section of `configs/app/` (`features/`, `ui.ts`, `api.ts`, etc.). -- A validation schema is added or updated in `deploy/tools/envs-validator/schema.ts`. For complex configs, a **separate sub-schema** should be created (similar to `tacScheme` or `beaconChainSchema`). -- The variable is added to the test presets in `deploy/tools/envs-validator/test/.env.base`. If the variable supports alternative configurations, examples should also be added to `.env.alt`. -- If the variable contains an external URL that is **not** an asset URL (i.e., not an image or JSON file), the appropriate CSP policy in `nextjs/csp/policies/` is updated. Policies should only be added when the relevant config option is actually enabled. -- If the variable links to an external resource (image or JSON config), the `ASSETS_ENVS` array in `deploy/scripts/download_assets.sh` is extended with the variable name. -- If the variable stores a JSON config URL, an example file is added to `deploy/tools/envs-validator/test/assets/configs/` and the `envsWithJsonConfig` array in `deploy/tools/envs-validator/index.ts` is extended. -- The PR description clearly states that a new ENV variable was added and includes example values. +## 4. Environment Variables -## 4. Component Architecture & Reuse +See `.agents/rules/env-vars.mdc`. -- When links to **application pages** are constructed, verify that `nextjs-routes` or `nextjs/routes` utilities are used instead of string concatenation or template literals. The full list of application routes is available in `nextjs/nextjs-routes.d.ts`. +## 5. Testing -## 5. Testing with Playwright - -- Flag any tests that rely on CSS class selectors (`.className`). These will break when the implementation changes. Advise the author to use roles, test IDs, text content, or other semantic selectors instead. -- Flag unnecessary use of the `testFn` pattern (i.e., `const testFn: TestFixture<...> = async (...)`). This pattern makes refactoring harder and can bypass Playwright lint rules. It should only be used when genuinely sharing test logic across multiple test suites. -- Check for unexplained magic values in test data. If a test uses a specific number or string, its meaning should be clear. Advise the author to use named constants (e.g., `const BLOCK_HEIGHT = 3947;` instead of a bare `3947`). -- Check for hardcoded URLs or values that already exist in mock files. Advise the author to import and reference them from the existing mocks to keep tests DRY and consistent. - -## 6. Utilities & Best Practices - -- Check whether any utility logic could be replaced by an `es-toolkit` function (e.g., `clamp`, `get`). Advise the author to prefer existing utilities over manual implementations. -- Flag unsafe type coercions such as `as unknown as MyType`. If coercion is necessary, the author should add runtime validation or a comment explaining why the cast is safe. -- Check that date and time formatting uses the shared project utilities (`Time` or `TimeWithTooltip` components). The project maintains a single consistent date format. -- Flag any commented-out code blocks. Advise the author to either implement the feature or remove the dead code entirely. TODOs are acceptable only when there is a clear follow-up plan. +- Unit tests: See `.agents/rules/tests-unit.mdc`. +- Visual component tests: See `.agents/rules/tests-visual.mdc`. diff --git a/.cursor/rules/frontend.mdc b/.cursor/rules/frontend.mdc deleted file mode 100644 index 39568b9908..0000000000 --- a/.cursor/rules/frontend.mdc +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: -globs: *.tsx,*.ts -alwaysApply: false ---- -You are an expert senior software engineer specializing in modern web development, with deep expertise in TypeScript, React 19, Next.js 15 (Pages Router), Chakra UI, and React Query. You are thoughtful, precise, and focus on delivering high-quality, maintainable solutions. - -## Code Style and Structure - -### General Principles - -- Write concise, readable TypeScript code -- Use functional and declarative programming patterns; avoid classes -- Prefer iteration and modularization over code duplication -- Implement early returns for better readability -- Structure components logically: exports, subcomponents, helpers, types - -### Naming Conventions - -- Use descriptive names with auxiliary verbs (isLoading, hasError) -- Prefix event handlers with "handle" (handleClick, handleSubmit) -- Favor default exports for React components - -### TypeScript Usage - -- Use TypeScript for all code -- Prefer interfaces over types -- Avoid enums; use const maps instead -- Implement proper type safety and inference -- Use `satisfies` operator for type validation - -### UI and Styling -- Utilize Chakra UI v3 components for consistent design -- **Component Import Priority**: Always check if a component exists in `toolkit/chakra/**` before importing from Chakra UI. If a custom version exists in `toolkit/chakra/**`, import from there instead of the native Chakra component. Only use default Chakra components when no custom version is available in `toolkit/chakra/**`. - - - \ No newline at end of file diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml index 68101efcc6..36ad2385ee 100644 --- a/.github/workflows/cleanup.yml +++ b/.github/workflows/cleanup.yml @@ -13,19 +13,21 @@ permissions: id-token: write jobs: - cleanup_release: + cleanup_deployment: uses: blockscout/actions/.github/workflows/cleanup_helmfile.yaml@main with: - appName: review-l2-$GITHUB_REF_NAME_SLUG + appName: review-$GITHUB_REF_NAME_SLUG + namespace: review-$GITHUB_REF_NAME_SLUG globalEnv: review helmfileDir: deploy kubeConfigSecret: ci/data/dev/kubeconfig/k8s-dev vaultRole: ci-dev secrets: inherit - cleanup_l2_release: + cleanup_deployment_2: uses: blockscout/actions/.github/workflows/cleanup_helmfile.yaml@main with: - appName: review-$GITHUB_REF_NAME_SLUG + appName: review-2-$GITHUB_REF_NAME_SLUG + namespace: review-2-$GITHUB_REF_NAME_SLUG globalEnv: review helmfileDir: deploy kubeConfigSecret: ci/data/dev/kubeconfig/k8s-dev @@ -35,4 +37,11 @@ jobs: uses: blockscout/actions/.github/workflows/cleanup_docker.yaml@main with: dockerImage: review-$GITHUB_REF_NAME_SLUG + imageName: frontend-private + secrets: inherit + cleanup_docker_image_2: + uses: blockscout/actions/.github/workflows/cleanup_docker.yaml@main + with: + dockerImage: review-2-$GITHUB_REF_NAME_SLUG + imageName: frontend-private secrets: inherit diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000000..5813d3bb5d --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,35 @@ +name: "Copilot Setup Steps" + +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.32.1 + + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 22.14.0 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile diff --git a/.github/workflows/deploy-review-l2.yml b/.github/workflows/deploy-review-2.yml similarity index 88% rename from .github/workflows/deploy-review-l2.yml rename to .github/workflows/deploy-review-2.yml index eb9f0c751f..cd8999668c 100644 --- a/.github/workflows/deploy-review-l2.yml +++ b/.github/workflows/deploy-review-2.yml @@ -1,4 +1,4 @@ -name: Deploy review environment (L2) +name: Deploy review environment (2) on: workflow_dispatch: @@ -37,7 +37,6 @@ on: - stability - zetachain - zetachain_testnet - - zkevm - zilliqa - zksync - zora @@ -67,17 +66,17 @@ jobs: uses: './.github/workflows/publish-image.yml' with: tags: | - type=raw,value=review-${{ needs.make_slug.outputs.REF_SLUG }} + type=raw,value=review-2-${{ needs.make_slug.outputs.REF_SLUG }} build_args: ENVS_PRESET=${{ inputs.envs_preset }} platforms: linux/amd64 secrets: inherit - deploy_review_l2: - name: Deploy frontend (L2) + deploy_review: + name: Deploy frontend needs: [ make_slug, publish_image ] uses: blockscout/actions/.github/workflows/deploy_helmfile.yaml@main with: - appName: review-l2-${{ needs.make_slug.outputs.REF_SLUG }} + appName: review-2-${{ needs.make_slug.outputs.REF_SLUG }} globalEnv: review helmfileDir: deploy kubeConfigSecret: ci/data/dev/kubeconfig/k8s-dev diff --git a/.github/workflows/deploy-review.yml b/.github/workflows/deploy-review.yml index a32cdf9759..24eb62d266 100644 --- a/.github/workflows/deploy-review.yml +++ b/.github/workflows/deploy-review.yml @@ -42,7 +42,6 @@ on: - tac_spb - zetachain - zetachain_testnet - - zkevm - zilliqa - zksync - zora diff --git a/.gitignore b/.gitignore index 94a37f09f8..eb3c12bdf7 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,5 @@ npm-debug.log* /toolkit/package/dist license.json release-prs-data.json + +.ai diff --git a/.husky/post-checkout b/.husky/post-checkout index c37815e2b5..ca7fcb4008 100755 --- a/.husky/post-checkout +++ b/.husky/post-checkout @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-checkout'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-checkout' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-checkout "$@" diff --git a/.husky/post-commit b/.husky/post-commit index e5230c305f..52b339cb3f 100755 --- a/.husky/post-commit +++ b/.husky/post-commit @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-commit'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-commit' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-commit "$@" diff --git a/.husky/post-merge b/.husky/post-merge index c99b752a52..a912e667aa 100755 --- a/.husky/post-merge +++ b/.husky/post-merge @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-merge'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-merge' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs post-merge "$@" diff --git a/.husky/pre-push b/.husky/pre-push index 216e91527e..0f0089bc25 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,3 +1,3 @@ #!/bin/sh -command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/pre-push'.\n"; exit 2; } +command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks').\n"; exit 2; } git lfs pre-push "$@" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5972798847..fbf61187f4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -371,7 +371,6 @@ "tac_spb", "zetachain", "zetachain_testnet", - "zkevm", "zilliqa", "zksync", "zora", diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index d46ec568e6..0000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,102 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Commands - -```bash -# Development -pnpm dev # Start dev server -pnpm build # Production build (Next.js only) -pnpm build:next # Full build with asset downloading and sprite building - -# Linting -pnpm lint:eslint # ESLint check -pnpm lint:eslint:fix # ESLint auto-fix -pnpm lint:tsc # TypeScript type check -pnpm lint:cspell # Spell check -pnpm lint:envs-validator:test # Validate environment variable schemas - -# Testing -pnpm test:vitest # Unit tests (Vitest, matches **/*.spec.ts(x)) -pnpm test:pw # Playwright component/E2E tests (matches *.pw.tsx) -pnpm test:pw:docker # Playwright tests in Docker - -# Assets -pnpm svg:build-sprite # Rebuild SVG sprite -pnpm chakra:typegen # Regenerate Chakra UI types -``` - -**Requirements:** Node >=22.14.0, pnpm (see `package.json` `engines` / `packageManager`) - -## Architecture - -**Stack:** Next.js 15 (Pages Router, not App Router), React 19, Chakra UI v3, React Query 5, Wagmi 2 / Viem 2, Valibot for schema validation, Vitest + Playwright for testing. - -**Key directories:** -- `pages/` — Next.js page components (require default exports) -- `ui/` — React UI components organized by feature (~65 subdirectories) -- `lib/` — Business logic, API utilities, custom hooks, context providers -- `toolkit/` — Design system layer: `toolkit/chakra/` (custom Chakra components), `toolkit/theme/` (semantic color tokens), `toolkit/hooks/` -- `configs/app/` — Runtime app configuration (features, API endpoints, UI settings) -- `nextjs/` — Next.js config utilities: headers, rewrites, redirects, type-safe routes via `nextjs-routes` -- `mocks/` — Mock data for tests -- `deploy/tools/envs-validator/` — Environment variable validation schema and tests - -**Data flow:** Pages use React Query for server state. Global UI state lives in React Context providers (`AppContextProvider`, `SettingsContextProvider`, etc.) initialized in `pages/_app.tsx`. WebSocket real-time data flows through `SocketProvider`. - -**Routing:** Use `nextjs-routes` / `nextjs/routes` utilities for constructing links to application pages — never string concatenation. The full route list is in `nextjs/nextjs-routes.d.ts`. - -## Design System Rules - -These are enforced by ESLint and must be followed: - -- **Always import from `toolkit/chakra/**`** before falling back to native Chakra UI. If a custom version exists in `toolkit/chakra/`, use it. -- **Never use hardcoded colors** (RGB, hex). Use semantic color tokens from `toolkit/theme/foundations/semanticTokens.ts` and `toolkit/theme/foundations/colors.ts` (e.g., `text.secondary`, `border.divider`, `icon.secondary`). -- **No custom `box-shadow`** — use design system shadow tokens. -- Don't override spacing on internal parts of compound components (e.g., don't add custom padding to `DialogHeader` inside a `Dialog`). -- Use `toolkit/chakra/link` instead of `next/link`. -- Use `lib/date/dayjs.ts` instead of importing `dayjs` directly. -- Date/time rendering must use the shared `Time` or `TimeWithTooltip` components. - -## Global Type Declarations - -- **Never use `(window as any)`** to access third-party globals. Instead, declare the property in `global.d.ts` inside the existing `declare global { interface Window { ... } }` block. -- Use `decs.d.ts` only for untyped third-party module declarations (`declare module 'foo'`). - -## TypeScript Conventions - -- Prefer `interface` over `type`. Use `interface extends` over `&` intersection (performance). -- No `enum` — use `as const` objects instead. -- Use top-level `import type { Foo }` not inline `import { type Foo }`. -- Default exports only when required by the framework (Next.js pages). All other exports are named. -- Declare return types on top-level module functions. -- `readonly` properties by default; omit only when genuinely mutable. -- Use `satisfies` for type validation instead of `as MyType[]` assertions. -- Outside generic functions, use `any` extremely sparingly; prefer `unknown` with proper narrowing. -- Extract magic numbers as `UPPER_SNAKE_CASE` constants above the component definition. -- Define empty array/object defaults as static constants outside components (not inline `?? []`). -- Wrap `.filter()`, `.map()`, `.reduce()` results in `useMemo` when passed as props or used as hook deps. -- Type parameters in generics are prefixed with `T` (e.g., `TKey`, `TValue`). - -## Adding Environment Variables - -When adding, renaming, or removing an environment variable, all of the following must be updated: - -1. `docs/ENVS.md` — document name, type, whether required, default, and example -2. `configs/app/` — add to the appropriate section (`features/`, `ui.ts`, `api.ts`, etc.) -3. `deploy/tools/envs-validator/schema.ts` — add/update validation schema -4. `deploy/tools/envs-validator/test/.env.base` — add to test presets -5. `nextjs/csp/policies/` — update CSP if the variable references an external (non-asset) URL -6. `deploy/scripts/download_assets.sh` — add to `ASSETS_ENVS` if it's an asset URL -7. If it's a JSON config URL: add example to `deploy/tools/envs-validator/test/assets/configs/` and extend `envsWithJsonConfig` in `deploy/tools/envs-validator/index.ts` - -## Testing - -**Vitest** (unit): Files named `*.spec.ts` / `*.spec.tsx`. Run a single file with `pnpm test:vitest path/to/file.spec.ts`. - -**Playwright** (component/E2E): Files named `*.pw.tsx`. Three test projects run against each test: `default` (desktop Chrome 1200×750), `mobile` (iPhone 13 Pro), `dark-color-mode`. Tag tests with `@mobile` or `@dark-mode` to target specific projects; use `-@default` to exclude desktop. - -- Use roles, test IDs, and text content as selectors — never CSS class selectors. -- Import mock values from existing files in `mocks/` rather than hardcoding them. -- Avoid the `testFn: TestFixture<...>` pattern unless sharing logic across multiple suites. diff --git a/Dockerfile b/Dockerfile index f430a606ed..c5ab384436 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,6 +49,7 @@ RUN set -a && \ # Build app for production ENV NODE_OPTIONS="--max-old-space-size=8192" +RUN pnpm routes:generate RUN pnpm run build diff --git a/architecture_design.md b/client/ARCH_REDESIGN.md similarity index 71% rename from architecture_design.md rename to client/ARCH_REDESIGN.md index c0e617821e..b711e957eb 100644 --- a/architecture_design.md +++ b/client/ARCH_REDESIGN.md @@ -1,6 +1,4 @@ -# Client architecture blueprint (draft — round 2) - -Living document: extend after further design rounds. Intended for **human engineers and agents** implementing the migration. +# Client architecture blueprint --- @@ -24,9 +22,9 @@ Aligned with **Next.js-style** paths: **kebab-case for directories** and for **n | **Helper / util modules** (non-hook) | `kebab-case.ts` | `format-tx-hash.ts`, `get-tab-list.ts` | | **Role files** | lowercase | `types.ts`, `mocks.ts`, `stubs.ts` | -Same folder may contain `TxDetails.tsx`, `use-tx-query.ts` — hooks stay **`useCamelCase.ts`**; everything else non-component prefers **kebab-case**. +Same folder may contain `TxDetails.tsx`, `useTxQuery.ts` — hooks stay **`useCamelCase.ts`**; everything else non-component prefers **kebab-case**. -**No `index.ts` barrel files inside `/client`.** Deep imports are preferred; barrels slow TypeScript server performance and break tree-shaking at scale. +**No re-export-only barrel files inside `/client`.** Deep imports are preferred; dumb barrels (files that only `export * from ...`) slow TypeScript server performance and break tree-shaking at scale. Aggregation files that define or curate their own public surface (e.g. `client/api/services/general/index.ts` acting as a facade for the General API) are acceptable where they provide genuine value. --- @@ -86,13 +84,18 @@ Application **chrome**: layout, error UI, header, footer, page title, top-bar, a Keep the current organization largely intact. Do not flatten everything into a 1:1 mapping with slices — feature services often have only one or two resources and do not warrant separate files. +`general/` maps to the **Blockscout General API namespace** — it is not restricted to vanilla-EVM resources. Files inside it follow the API grouping, not the slice/feature taxonomy. + ```text client/api/ + hooks/ ← React hooks for data fetching (useApiFetch, useApiQuery, useFetch, …) services/ - general/ ← core API resources; only split files inside where genuinely needed + general/ ← Blockscout General API namespace; only split files inside where genuinely needed tx.ts block.ts misc.ts ← resources with no clear single-domain home stay here + rollup.ts ← rollup resources are part of the General API + v1.ts ← previous API version; one active resource ... rewards.ts ← feature-specific APIs stay flat alongside general/ stats.ts @@ -103,13 +106,15 @@ client/api/ socket/ ← migrated from lib/socket/ ``` +> **Decision note — `hooks/` subfolder:** React hooks that wire transport/query-client logic (`useApiFetch`, `useApiQuery`, `useApiInfiniteQuery`, `useApiQueries`, `useQueryClientConfig`, `useFetch`) live under `client/api/hooks/` rather than at the `client/api/` root. This keeps the root flat (utilities + config) and the hooks discoverable as a group, without introducing an extra barrel file. + ### 4.3 `client/slices` **Core explorer entities** — always part of the product surface (not optional "features" in the `configs/app/features` sense). **Classification criterion:** *"Can this area exist on a vanilla EVM chain with no feature flag?"* Yes → slice. No (requires a `configs/app/features` flag) → feature. -**Confirmed slices (non-exhaustive):** `tx`, `block`, `search`, `token`, `address`, `contract`, `internal-tx`, `home`, `tokens`, `accounts`, `token-instance`, … +**Confirmed slices (non-exhaustive):** `tx`, `block`, `search`, `token`, `address`, `contract`, `internal-tx`, `home`, `token`, `token-instance` (rename to `nft`), `log` … - **`contract` (slice):** **Contract verification** and **SolidityScan** (`lib/solidityScan/`) are **slice** concerns (available on any chain). - **`internal-tx` (slice):** Own slice; **`tx`** composes **internal-tx** components (e.g. tab content). Prefer **no imports** from `internal-tx` back into `tx` to avoid cycles; share via **`client/shared`** or a **thin types-only surface** if needed. @@ -121,11 +126,11 @@ client/api/ ```text client/slices/tx/ pages/ - tx-index/ # kebab-case folder = one "screen" / route target + index/ # kebab-case folder = one "screen" / route target TxIndex.tsx TxIndexTable.tsx ... - tx-details/ + details/ TxDetails.tsx ... components/ @@ -133,6 +138,7 @@ client/slices/tx/ utils/ # kebab-case .ts files types/ api.ts # response / DTO types owned by this slice + client.ts # frontend-only types (client types or types derived from API responses) mocks.ts stubs.ts ``` @@ -145,15 +151,26 @@ client/slices/tx/ - Folders may exist for **readability and maintenance** (e.g. per rollup type) even when not everything is enabled for a given chain; **runtime behavior** still comes from **config/env**, not "folder exists = on." -**Confirmed features (non-exhaustive):** `user-ops`, `blobs`, `multichain`, `name-domains`, `account`, `stats`, `gas-tracker`, `epochs`, `validators`, `marketplace`, `rewards`, `rollup/*`, … +**Confirmed features (non-exhaustive):** `user-ops`, `data-availability`, `multichain`, `name-domains`, `account`, `stats`, `gas-tracker`, `validators`, `marketplace`, `rewards`, `rollup/*`, `chain-variants/*`, `advanced-filter`, `ad-banner`, `safe-address-tags`, `metasuites`, `csv-export`, … - **`stats` and `gas-tracker`** are **features** (not slices) — they are config-gated per chain. -- **`epochs` and `validators`** are **features** — chain-specific, not present on a vanilla EVM chain. +- **`validators`** is a **feature** — chain-specific, not present on a vanilla EVM chain. +- **`epochs`** is a sub-concern of the Celo chain variant (`features/chain-variants/celo/`), not a standalone feature. +- **`data-availability`** was previously referred to as `blobs` — use `data-availability` throughout. + +**Classification rules:** +1. *"Can this area exist on a vanilla EVM chain with no feature flag?"* Yes → slice. No → feature. +2. **Config-gated infrastructure with no user-facing UI** (analytics providers, error monitoring, A/B flag providers) lives in **`client/shared/`**, not `features/` — the presence of a config flag alone is not sufficient to qualify for a feature folder. +3. Every config-gated feature with user-facing UI gets its own folder under `features/`, regardless of size — consistency and discoverability outweigh folder count. + +**Chain-specific non-rollup features** — chain variants that are not rollups (ZetaChain, TAC, SUAVE, Celo, MegaETH, Beacon chain, etc.) are grouped under **`features/chain-variants//`**, parallel to `features/rollup//`. Example: `features/chain-variants/celo/`, `features/chain-variants/tac/`, `features/chain-variants/zeta-chain/`. **Variant / chain-specific transaction lists** (e.g. kettle, zeta-specific list UIs) live under **`features//pages/...`**, **not** as variants inside `slices/tx`. **Rollups:** `features/rollup//` (e.g. `optimism`, `arbitrum`, …) — **organized by rollup type**, because **views and helpers differ by rollup**. Sub-areas (deposits, batches, withdrawals) are **subfolders** under that rollup, not separate top-level rollup taxonomies. +**Chain variants (non-rollup):** `features/chain-variants//` (e.g. `celo`, `tac`, `zeta-chain`, `mega-eth`, `suave`, `beacon-chain`) — same sub-folder structure as rollups. + **Typical feature shape:** same idea as slices — `pages/`, `components/`, `hooks/`, `utils/`, `types/`, `mocks.ts`, `stubs.ts`. ### 4.5 `client/shared` @@ -176,7 +193,7 @@ client/slices/tx/ | `hooks/` | Generic hooks from `lib/hooks/` with no single domain owner | | `i18n/` | `lib/setLocale.ts` | | `links/utils/` | `lib/utils/stripUtmParams.ts` and URL-related helpers | -| `lists/` | `lib/hooks/useLazyRenderedList`, `lib/getItemIndex.ts` | +| `lists/` | `lib/hooks/useLazyRenderedList`, `lib/hooks/useInitialList`, `lib/getItemIndex.ts` | | `metadata/` | `lib/metadata/` (centralized — see note below) | | `monitoring/rollbar/` | `lib/rollbar/` (client-side observability infrastructure) | | `router/` | `lib/router/` + query-param filter helpers (`getFilterValueFromQuery`, etc.) | @@ -225,24 +242,34 @@ client/slices/tx/ | `lib/router/` | `client/shared/router/` | | `lib/web3/` | `client/shared/web3/` | | `lib/errors/` | `client/shared/errors/` | -| `lib/mixpanel/` | `client/shared/analytics/` | +| `lib/mixpanel/` | `client/shared/analytics/mixpanel` | | `lib/growthbook/` | `client/shared/feature-flags/` | | `lib/networks/` + `lib/units.ts` | `client/shared/chain/` (rename `network` → `chain` throughout) | | `lib/hooks/useNavItems` | `client/shell/` | | `lib/hooks/useTimeAgoIncrement` | `client/shared/date-and-time/` | | `lib/hooks/useLazyRenderedList` | `client/shared/lists/` | +| `lib/hooks/useInitialList` | `client/shared/lists/` | | `lib/hooks/useRewardsActivity` | `client/features/rewards/` | -| `lib/hooks/useAddressProfileApiQuery` | `client/slices/address/` | +| `lib/hooks/useAddressProfileApiQuery` | `client/features/address-profile-api/hooks/useAddressProfileApiQuery.tsx` | +| `lib/hooks/useFetch` | `client/api/hooks/` | +| `lib/hooks/useGetCsrfToken` | `client/features/account/` | +| `lib/hooks/useGraphLinks` | `client/features/marketplace/` | +| `lib/hooks/useAdblockDetect` | `client/features/ad-banner/` | +| `lib/hooks/useIsSafeAddress` | `client/features/safe-address-tags/` | +| `lib/hooks/useNotifyOnNavigation` | `client/features/metasuites/` | | `lib/hooks/` (remaining) | `client/shared/hooks/` | | `lib/contexts/app.tsx`, `fallback.tsx` | `client/shell/` | | `lib/contexts/settings.tsx` | `client/shell/top-bar/` | | `lib/contexts/multichain.tsx` | `client/features/multichain/` | | `lib/contexts/rewards.tsx` | `client/features/rewards/` | | `lib/contexts/marketplace.tsx` | `client/features/marketplace/` | -| `lib/contexts/addressHighlight.tsx` | `client/slices/address/` | +| `lib/contexts/addressHighlight.tsx` | `client/slices/address/contexts/address-highlight.tsx` | | `lib/tx/` | `client/slices/tx/utils/` | | `lib/token/` | `client/slices/token/` | -| `lib/address/` | `client/slices/address/` | +| `lib/address/parseMetaPayload.ts` | `client/features/address-metadata/utils/parseMetaPayload.ts` | +| `lib/address/useAddressMetadataInfoQuery.ts` | `client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts` | +| `lib/address/useAddressMetadataInitUpdate.ts` | `client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts` | +| `lib/address/` (remaining) | `client/slices/address/` | | `lib/rollups/` | `client/features/rollup/` | | `lib/multichain/` | `client/features/multichain/` | | `lib/search/` | `client/slices/search/` | @@ -281,6 +308,7 @@ client/slices/tx/ ## 8. Dependency rules (high level) - **ESLint rules (`import/no-cycle` + explicit `boundaries` rules for `client/api`, `configs`, `toolkit`) must be enabled _before_ the migration starts.** Running them against the existing code surfaces real problem areas cheaply, before they become migration blockers. +- **Enforcement strategy: warn on legacy, error on new.** Configure boundary rules to emit **errors** only within `client/`; legacy `lib/` and `ui/` paths receive **warnings** only. This avoids a costly upfront clean-up of the entire codebase while ensuring every migrated file is immediately held to the new standard. - **Guidelines:** - **`client/api`** allows **`import type`** from slices/features for response-shape typing; **no runtime imports** (no logic, hooks, or components — types only). - **Slice → feature type imports** are intentional: the backend includes feature-specific fields (e.g. `arbitrum?`, `scroll?`) in API responses unconditionally when the rollup is enabled — the frontend type must mirror the full API contract. UI component imports follow the same direction (e.g. `TxDetails` imports `TxDetailsArbitrum`). @@ -312,19 +340,23 @@ client/slices/tx/ ### Rules during migration -- Update **all imports in the same PR** as the file move — no shims. -- Each PR leaves ESLint green; do not merge with new cycle violations. -- `pages/` thin wrappers: **dynamic import only** — `getServerSideProps` helpers stay in `nextjs/`; SEO metadata lives in the slice/feature page folder. +- Update **all imports in the same PR** as the file move — no shims. For high-fanout files use an automated codemod as a dedicated commit within the branch. +- **Rename to kebab-case at move time** — file moves and naming convention changes happen in one pass, never two separate PRs. +- Each PR leaves ESLint green within `client/`; do not merge with new cycle/boundary violations inside `client/`. +- **`types/api.ts` is the stable external type surface** for each slice/feature. External consumers (other slices, features, `client/api/services/`) must import types only from `/types/api.ts`, never from deeper internal paths. Enforced via ESLint `boundaries`. +- `pages/` thin wrappers: **no UI component or business logic** — a dynamic import and, if needed, a `getServerSideProps` inline (per-page logic may stay inline; only shared/reusable SSR helpers move to `nextjs/`). SEO metadata lives in the slice/feature page folder. +- **Tx pilot PR strategy:** migrate the tx slice end-to-end (UI, hooks, utils, types, mocks) while leaving cross-slice dependencies (address, rollup types, etc.) at their old `lib/` paths. Every remaining old-path import in `client/slices/tx/` after the pilot becomes an explicit migration task for the relevant slice/feature PR. --- -## 11. Open topics (round 3) +## 11. Open topics (round 4) -- [ ] **Path aliases** — `client/*` imports; document in `tsconfig.json` / CONTRIBUTING. -- [ ] **Public type surfaces** — document **thin** export files (e.g. `slices/tx/types/api.ts`) to avoid deep cross-slice imports. -- [ ] **Storybook / toolkit** — if any stories live under `client`, how they resolve paths to `toolkit`. -- [ ] **`lib/hooks/` full audit** — confirm every remaining hook in `lib/hooks/` that is not explicitly listed above goes to `client/shared/hooks/`. -- [ ] **`lib/api/services/general/` audit** — identify any files inside `general/` that should be split out (beyond `tx.ts`, `block.ts`, etc.) now that slices own their types. +- [x] ~~**Path aliases**~~ — `baseUrl: "."` in `tsconfig.json` makes `client/**` absolute imports work without explicit `paths` entries. No action needed. +- [x] ~~**Public type surfaces**~~ — `types/api.ts` is the stable external surface per slice/feature; documented in §10. Cross-slice `types/client.ts` imports left as an open question to resolve with real examples during implementation. +- [x] ~~**Storybook / toolkit**~~ — project does not use Storybook. Topic closed. +- [x] ~~**`lib/hooks/` full audit**~~ — all hooks explicitly placed; see §6 migration map. +- [x] ~~**`lib/api/services/general/` audit**~~ — all files stay under `general/` (General API namespace). No splits needed beyond existing file boundaries. +- [x] ~~**Project glossary**~~ — a `GLOSSARY.md` (or section in CONTRIBUTING) mapping internal codenames to their product/feature meaning (e.g. `tac` → Ton Application Chain, `blobs`/`data-availability`, `cctx` → cross-chain transactions, `epochs` → Celo epoch pages). Aids both engineers and agents navigating the codebase. --- @@ -370,5 +402,3 @@ client/ ``` --- - -*End of round 2 blueprint.* diff --git a/client/MIGRATION_TASKS.md b/client/MIGRATION_TASKS.md new file mode 100644 index 0000000000..a2d2edb33c --- /dev/null +++ b/client/MIGRATION_TASKS.md @@ -0,0 +1,274 @@ +# Architecture Migration — Task Backlog + +Progress tracker for the `client/` architecture redesign. +Blueprint: `client/ARCH_REDESIGN.md`. Terminology: `docs/GLOSSARY.md`. + +**Task ID format:** `-` — stage number + sequential index within that stage. +Adding tasks to a stage: append the next index; no renumbering needed. + +**Status:** `[ ]` to do · `[~]` in progress · `[x]` done +**Base branch:** `main` — each task branches from `main`; PRs target `main`. +**Parent issue:** [`blockscout/frontend#3341`](https://github.com/blockscout/frontend/issues/3341) + +> `/arch-research ` creates the sub-issue and marks the task `[~]`. +> `/arch-migrate ` executes the task and opens a PR. +> Change `[~]` → `[x]` after the PR is merged. + +--- + +## Stage 0 — Pre-migration + +### 0-1 · [x] Rename `configs/app/features/` files to kebab-case · [#3345](https://github.com/blockscout/frontend/issues/3345) + +**Scope:** Rename every `.ts` file under `configs/app/features/` from camelCase to kebab-case +(e.g. `userOps.ts` → `user-ops.ts`, `adsBanner.ts` → `ads-banner.ts`). +Update all import paths repo-wide. Named exports inside files are unchanged — filenames only. + +--- + +### 0-2 · [x] Enable ESLint `import/no-cycle` + `boundaries` rules · [#3348](https://github.com/blockscout/frontend/issues/3348) + +**Scope:** Add `eslint-plugin-boundaries` (if not already installed). Configure rules so that: +- Imports within `client/` emit **errors** on violations. +- Imports in legacy `lib/` and `ui/` emit **warnings** only. +Fix all pre-existing errors (there should be none in `client/` yet). Confirm warnings in legacy code are expected and do not block CI. + +--- + +## Stage 1 — Foundation + +### 1-1 · [x] Migrate `client/api/` · [#3369](https://github.com/blockscout/frontend/issues/3369) + +**Scope:** Move `lib/api/**` → `client/api/**` (preserve `services/` structure). +Additional moves in the same PR: +- `lib/socket/` → `client/api/socket/` +- `lib/hooks/useFetch.ts` → `client/api/` +- `lib/api/services/utils.ts` → `client/api/types.ts` + +Update all repo-wide imports. Verify `client/api` has no runtime imports from `client/slices/*` or +`client/features/*` (`import type` is permitted). + +--- + +### 1-2 · [x] Migrate `client/shared/` · [#3371](https://github.com/blockscout/frontend/issues/3371) + +**Scope:** Move all cross-cutting utilities from `lib/` to `client/shared//`. +Key moves (see `ARCH_REDESIGN.md §6` for full list): + +| Source | Destination | +|--------|-------------| +| `lib/mixpanel/` | `client/shared/analytics/` | +| `lib/rollbar/` | `client/shared/monitoring/rollbar/` | +| `lib/growthbook/` | `client/shared/feature-flags/` | +| `lib/networks/` + `lib/units.ts` | `client/shared/chain/` (rename `network` → `chain` throughout) | +| `lib/router/` + filter-value helpers | `client/shared/router/` | +| `lib/errors/` + `lib/getErrorMessage.ts` | `client/shared/errors/` | +| `lib/web3/` | `client/shared/web3/` | +| `lib/metadata/` | `client/shared/metadata/` | +| `lib/hooks/useTimeAgoIncrement` | `client/shared/date-and-time/` | +| `lib/hooks/useLazyRenderedList`, `useInitialList`, `lib/getItemIndex.ts` | `client/shared/lists/` | +| `lib/cookies.ts` | `client/shared/storage/` | +| `lib/decodeJWT.ts` | `client/shared/auth/` | +| `lib/setLocale.ts` | `client/shared/i18n/` | +| `lib/capitalizeFirstLetter.ts`, `shortenString.ts`, `escapeRegExp.ts`, `highlightText.ts` | `client/shared/text/` | +| `lib/base64ToHex.ts` and other hex/bytes helpers | `client/shared/transformers/` | +| `lib/utils/stripUtmParams.ts` | `client/shared/links/utils/` | +| `lib/delay.ts`, `lib/isMetaKey.tsx` | `client/shared/utils/` | +| Remaining generic hooks from `lib/hooks/` | `client/shared/hooks/` | + +Note: `lib/monitoring/` (server-side) → `nextjs/`, not `client/shared/`. + +--- + +## Stage 2 — Pilot slice + +### 2-1 · [x] Pilot: migrate `slices/tx` end-to-end + +**Scope:** Full migration of the `tx` slice. Canonical template for all subsequent slices. +- `lib/tx/` → `client/slices/tx/utils/` +- `lib/api/services/general/tx.ts` → `client/api/services/general/tx.ts` (if not already done in 1-1) +- `ui/tx/**` → `client/slices/tx/` (restructure into `pages/`, `components/`, `hooks/`, `utils/`, `types/`, `mocks.ts`, `stubs.ts`) +- Move associated `types/api/tx.ts` → `client/slices/tx/types/api.ts` +- Move associated `mocks/` and `stubs/` entries + +Cross-slice dependencies (address types, rollup sub-types, etc.) may remain at their old `lib/` paths +after this PR — note each as an explicit follow-up in the PR description. + +--- + +## Stage 3 — Core slices + +Each slice follows the template established in 2-1. + +### 3-1 · [x] Slice: `block` · [#3378](https://github.com/blockscout/frontend/issues/3378) +**Scope:** `lib/block/` (if any), `ui/block/**`, related `types/api/`, `mocks/`, `stubs/` → `client/slices/block/` + +### 3-2 · [x] Slice: `address` · [#3380](https://github.com/blockscout/frontend/issues/3380) +**Scope:** `lib/address/`, `ui/address/**`, related types/mocks/stubs, `lib/hooks/useAddressProfileApiQuery`, `lib/contexts/addressHighlight.tsx` → `client/slices/address/` + +### 3-3 · [ ] Slice: `search` +**Scope:** `lib/search/`, `ui/snippets/searchBar/`, `lib/recentSearchKeywords.ts`, related types/mocks/stubs → `client/slices/search/` + +### 3-4 · [ ] Slice: `token` +**Scope:** `lib/token/`, `ui/token/**`, related types/mocks/stubs → `client/slices/token/` + +### 3-5 · [ ] Slice: `contract` +**Scope:** `lib/contracts/`, `lib/solidityScan/`, `ui/contract/**`, related types/mocks/stubs → `client/slices/contract/` + +### 3-6 · [ ] Slice: `internal-tx` +**Scope:** `ui/internalTxs/**` (and any `lib/` counterparts), related types/mocks/stubs → `client/slices/internal-tx/` + +### 3-7 · [ ] Slice: `home` +**Scope:** `ui/home/**`, related types/mocks/stubs → `client/slices/home/` + +### 3-8 · [ ] Slice: `tokens` (token list) +**Scope:** `ui/tokens/**`, related types/mocks/stubs → `client/slices/tokens/` + +### 3-9 · [ ] Slice: `token-instance` +**Scope:** `ui/tokenInstance/**`, related types/mocks/stubs → `client/slices/nft/` + +### 3-10 · [ ] Slice: `log` +**Scope:** `ui/shared/log/**`, related types/mocks/stubs → `client/slices/log/` + +--- + +## Stage 4 — Features: rollups + +One PR per rollup type. Each goes under `client/features/rollup//`. + +> Enumerate additional rollup tasks when this stage begins, based on what exists under `lib/rollups/` and `ui/`. + +### 4-1 · [ ] Feature: `rollup/optimism` +**Scope:** All Optimism-specific UI, hooks, utils, types → `client/features/rollup/optimism/`. Includes fault proof system and dispute games. + +### 4-2 · [ ] Feature: `rollup/arbitrum` +**Scope:** All Arbitrum-specific UI, hooks, utils, types → `client/features/rollup/arbitrum/` + +### 4-3 · [ ] Feature: `rollup/zk-sync` +**Scope:** zkSync-specific UI, hooks, utils, types → `client/features/rollup/zk-sync/`. Check for other zk-based rollup types (scroll, etc.) and add tasks if needed. + +--- + +## Stage 5 — Features: chain variants + +One PR per chain variant. Each goes under `client/features/chain-variants//`. + +### 5-1 · [ ] Feature: `chain-variants/celo` +**Scope:** All Celo-specific UI and logic including epochs → `client/features/chain-variants/celo/`. See `configs/app/features/celo.ts`. + +### 5-2 · [ ] Feature: `chain-variants/tac` +**Scope:** TAC operations and bridge UI → `client/features/chain-variants/tac/`. See `configs/app/features/tac.ts`. + +### 5-3 · [ ] Feature: `chain-variants/zeta-chain` +**Scope:** ZetaChain CCTX UI → `client/features/chain-variants/zeta-chain/`. See `configs/app/features/zetachain.ts`. + +### 5-4 · [ ] Feature: `chain-variants/suave` +**Scope:** SUAVE Kettle UI → `client/features/chain-variants/suave/`. See `configs/app/features/suave.ts`. + +### 5-5 · [ ] Feature: `chain-variants/mega-eth` +**Scope:** MegaETH Flashblocks UI → `client/features/chain-variants/mega-eth/`. See `configs/app/features/megaEth.ts` and `flashblocks.ts`. + +### 5-6 · [ ] Feature: `chain-variants/beacon-chain` +**Scope:** Beacon chain deposits/withdrawals UI → `client/features/chain-variants/beacon-chain/`. See `configs/app/features/beaconChain.ts`. + +--- + +## Stage 6 — Features: standalone + +One PR per feature. Features that are pure infrastructure (analytics, monitoring, A/B flags) were +migrated to `client/shared/` in 1-2 and do not appear here. + +### 6-1 · [ ] Feature: `user-ops` + +### 6-2 · [ ] Feature: `data-availability` + +### 6-3 · [ ] Feature: `multichain` +**Scope:** Includes `lib/multichain/`, `lib/contexts/multichain.tsx`, `ui/snippets/networkMenu/` → `client/features/multichain/` + +### 6-4 · [ ] Feature: `name-domains` (BENS) +**Scope:** `configs/app/features/nameServices.ts` — use `name-domains` as folder name. + +### 6-5 · [ ] Feature: `account` +**Scope:** Includes `lib/hooks/useGetCsrfToken`, `ui/snippets/auth/`, `ui/snippets/user/` → `client/features/account/` + +### 6-6 · [ ] Feature: `stats` +**Scope:** `lib/stats/` and stats UI → `client/features/stats/` + +### 6-7 · [ ] Feature: `gas-tracker` + +### 6-8 · [ ] Feature: `validators` + +### 6-9 · [ ] Feature: `marketplace` +**Scope:** Includes `lib/contexts/marketplace.tsx`, `lib/hooks/useGraphLinks` → `client/features/marketplace/` + +### 6-10 · [ ] Feature: `rewards` +**Scope:** Includes `lib/contexts/rewards.tsx`, `lib/hooks/useRewardsActivity` → `client/features/rewards/` + +### 6-11 · [ ] Feature: `advanced-filter` + +### 6-12 · [ ] Feature: `ad-banner` +**Scope:** Includes `lib/hooks/useAdblockDetect` → `client/features/ad-banner/`. Covers both `adsBanner.ts` and `adsText.ts` configs. + +### 6-13 · [ ] Feature: `safe-address-tags` +**Scope:** Includes `lib/hooks/useIsSafeAddress` → `client/features/safe-address-tags/`. See `configs/app/features/safe.ts`. + +### 6-14 · [ ] Feature: `metasuites` +**Scope:** Includes `lib/hooks/useNotifyOnNavigation` → `client/features/metasuites/` + +### 6-15 · [ ] Feature: `csv-export` + +### 6-16 · [ ] Feature: `pools` + +### 6-17 · [ ] Feature: `hot-contracts` + +### 6-18 · [ ] Feature: `interchain-indexer` +**Scope:** Cross-chain message indexer (not ZetaChain). See `configs/app/features/crossChainTxs.ts`. + +### 6-19 · [ ] Feature: `mud-framework` + +### 6-20 · [ ] Feature: `visualize` +**Scope:** Solidity-to-UML diagrams. See `configs/app/features/sol2uml.ts`. + +### 6-21 · [ ] Feature: `tx-interpretation` + +### 6-22 · [ ] Feature: `public-tags` +**Scope:** Community address labels. See `configs/app/features/publicTagsSubmission.ts`. + +### 6-23 · [ ] Feature: `address-widgets` +**Scope:** Third-party widgets on address pages. See `configs/app/features/address3rdPartyWidgets.ts`. + +### 6-24 · [ ] Feature: `address-metadata` + +### 6-25 · [ ] Feature: `address-verification` +**Scope:** Covers `addressProfileAPI.ts` and `addressVerification.ts` configs. + +### 6-26 · [ ] Feature: `bridged-tokens` + +### 6-27 · [ ] Feature: `web3-wallet` +**Scope:** Includes `blockchainInteraction.ts` config. + +### 6-28 · [ ] Feature: `alternative-explorers` +**Scope:** The "Verify with other explorers" menu shown on tx, block, address, and token pages. Move `ui/shared/NetworkExplorers.tsx` (and its `.pw.tsx` test) → `client/features/alternative-explorers/`. The util `client/features/alternative-explorers/utils/explorers.ts` already exists (landed in 1-2). Config-gated via `NEXT_PUBLIC_NETWORK_EXPLORERS`. + +> Other small features (`externalTxs`, `xStarScore`, `deFiDropdown`, `getGasButton`, `easterEgg*`, `apiDocs`, `verifiedTokens`, `multichainButton`) — enumerate as separate tasks or group with related features when this stage begins. + +--- + +## Stage 7 — Shell + +### 7-1 · [ ] Migrate `client/shell` + +**Scope:** Migrate after all slices and features it depends on are in place. +- `ui/snippets/header/`, `footer/`, `navigation/`, `topBar/` → `client/shell/` +- `lib/contexts/app.tsx`, `fallback.tsx` → `client/shell/` +- `lib/contexts/settings.tsx` → `client/shell/top-bar/` +- `lib/hooks/useNavItems` → `client/shell/` + +--- + +## Stage 8 — Cleanup + +### 8-1 · [ ] Remove legacy root directories + +**Scope:** Confirm `lib/`, `ui/`, `mocks/`, `stubs/`, `types/` are empty (no remaining unconverted files). +Delete them. Fix any remaining lint warnings that referenced legacy paths. diff --git a/lib/api/buildUrl.spec.ts b/client/api/build-url.spec.ts similarity index 93% rename from lib/api/buildUrl.spec.ts rename to client/api/build-url.spec.ts index 0030a45ba0..56a1d3ca9e 100644 --- a/lib/api/buildUrl.spec.ts +++ b/client/api/build-url.spec.ts @@ -1,6 +1,6 @@ import { expect, test, describe } from 'vitest'; -import buildUrl from './buildUrl'; +import buildUrl from './build-url'; test('builds URL for resource without path params', () => { const url = buildUrl('general:config_backend_version'); @@ -40,6 +40,6 @@ test('builds URL with array-like query parameters', () => { }); test('builds URL for resource with custom API endpoint', () => { - const url = buildUrl('contractInfo:token_verified_info', { chainId: '42', hash: '0x11' }); + const url = buildUrl('contractInfo:token_verified_info', { instanceId: '42', hash: '0x11' }); expect(url).toBe('https://localhost:3005/api/v1/chains/42/token-infos/0x11'); }); diff --git a/lib/api/buildUrl.ts b/client/api/build-url.ts similarity index 92% rename from lib/api/buildUrl.ts rename to client/api/build-url.ts index 3f0ee2066d..c85b316f91 100644 --- a/lib/api/buildUrl.ts +++ b/client/api/build-url.ts @@ -4,8 +4,8 @@ import type { ExternalChainExtended } from 'types/externalChains'; import config from 'configs/app'; -import getResourceParams from './getResourceParams'; -import isNeedProxy from './isNeedProxy'; +import getResourceParams from './get-resource-params'; +import isNeedProxy from './is-need-proxy'; import type { ResourceName, ResourcePathParams } from './resources'; export default function buildUrl( diff --git a/lib/api/getResourceParams.ts b/client/api/get-resource-params.ts similarity index 100% rename from lib/api/getResourceParams.ts rename to client/api/get-resource-params.ts diff --git a/lib/api/getSocketUrl.ts b/client/api/get-socket-url.ts similarity index 100% rename from lib/api/getSocketUrl.ts rename to client/api/get-socket-url.ts diff --git a/lib/api/useApiFetch.tsx b/client/api/hooks/useApiFetch.ts similarity index 89% rename from lib/api/useApiFetch.tsx rename to client/api/hooks/useApiFetch.ts index ab6ff8a38c..ff3749eb96 100644 --- a/lib/api/useApiFetch.tsx +++ b/client/api/hooks/useApiFetch.ts @@ -5,16 +5,16 @@ import React from 'react'; import type { CsrfData } from 'types/client/account'; import type { ExternalChainExtended } from 'types/externalChains'; -import isBodyAllowed from 'lib/api/isBodyAllowed'; -import isNeedProxy from 'lib/api/isNeedProxy'; -import { getResourceKey } from 'lib/api/useApiQuery'; -import * as cookies from 'lib/cookies'; -import type { Params as FetchParams } from 'lib/hooks/useFetch'; -import useFetch from 'lib/hooks/useFetch'; +import * as cookies from 'client/shared/storage/cookies'; -import buildUrl from './buildUrl'; -import getResourceParams from './getResourceParams'; -import type { ResourceName, ResourcePathParams } from './resources'; +import buildUrl from '../build-url'; +import getResourceParams from '../get-resource-params'; +import isBodyAllowed from '../is-body-allowed'; +import isNeedProxy from '../is-need-proxy'; +import type { ResourceName, ResourcePathParams } from '../resources'; +import { getResourceKey } from './useApiQuery'; +import type { Params as FetchParams } from './useFetch'; +import useFetch from './useFetch'; export interface Params { pathParams?: ResourcePathParams; diff --git a/lib/api/useApiInfiniteQuery.tsx b/client/api/hooks/useApiInfiniteQuery.ts similarity index 98% rename from lib/api/useApiInfiniteQuery.tsx rename to client/api/hooks/useApiInfiniteQuery.ts index e52bd65a78..1a2b331ad3 100644 --- a/lib/api/useApiInfiniteQuery.tsx +++ b/client/api/hooks/useApiInfiniteQuery.ts @@ -1,7 +1,7 @@ import type { InfiniteData, QueryKey, UseInfiniteQueryResult, UseInfiniteQueryOptions } from '@tanstack/react-query'; import { useInfiniteQuery } from '@tanstack/react-query'; -import type { PaginatedResourceName, ResourceError, ResourcePayload } from './resources'; +import type { PaginatedResourceName, ResourceError, ResourcePayload } from '../resources'; import useApiFetch from './useApiFetch'; import type { Params as ApiFetchParams } from './useApiFetch'; import { getResourceKey } from './useApiQuery'; diff --git a/lib/api/useApiQueries.ts b/client/api/hooks/useApiQueries.ts similarity index 99% rename from lib/api/useApiQueries.ts rename to client/api/hooks/useApiQueries.ts index 002fb44ddb..677e0f867a 100644 --- a/lib/api/useApiQueries.ts +++ b/client/api/hooks/useApiQueries.ts @@ -1,7 +1,7 @@ import type { UseQueryOptions } from '@tanstack/react-query'; import { useQueries } from '@tanstack/react-query'; -import type { ResourceError, ResourceName, ResourcePayload } from './resources'; +import type { ResourceError, ResourceName, ResourcePayload } from '../resources'; import useApiFetch from './useApiFetch'; import type { Params as ApiQueryParams } from './useApiQuery'; import { getResourceKey } from './useApiQuery'; diff --git a/lib/api/useApiQuery.tsx b/client/api/hooks/useApiQuery.ts similarity index 94% rename from lib/api/useApiQuery.tsx rename to client/api/hooks/useApiQuery.ts index e3cbba398b..a873ac87dc 100644 --- a/lib/api/useApiQuery.tsx +++ b/client/api/hooks/useApiQuery.ts @@ -4,10 +4,10 @@ import { useQuery } from '@tanstack/react-query'; import type { ExternalChainExtended } from 'types/externalChains'; import { useMultichainContext } from 'lib/contexts/multichain'; -import type { Params as FetchParams } from 'lib/hooks/useFetch'; -import type { ResourceError, ResourceName, ResourcePathParams, ResourcePayload } from './resources'; +import type { ResourceError, ResourceName, ResourcePathParams, ResourcePayload } from '../resources'; import useApiFetch from './useApiFetch'; +import type { Params as FetchParams } from './useFetch'; export interface Params> { pathParams?: ResourcePathParams; @@ -23,6 +23,7 @@ export interface GetResourceKeyParams(resource: R, { pathParams, queryParams, chainId }: GetResourceKeyParams = {}) { if (pathParams || queryParams) { return [ resource, chainId, { ...pathParams, ...queryParams } ].filter(Boolean); diff --git a/lib/hooks/useFetch.tsx b/client/api/hooks/useFetch.ts similarity index 93% rename from lib/hooks/useFetch.tsx rename to client/api/hooks/useFetch.ts index afd6db2d2f..fd11e481be 100644 --- a/lib/hooks/useFetch.tsx +++ b/client/api/hooks/useFetch.ts @@ -1,8 +1,9 @@ import React from 'react'; -import isBodyAllowed from 'lib/api/isBodyAllowed'; -import type { ResourceError, ResourcePath } from 'lib/api/resources'; -import { useRollbar } from 'lib/rollbar'; +import { useRollbar } from 'client/shared/monitoring/rollbar'; + +import isBodyAllowed from '../is-body-allowed'; +import type { ResourceError, ResourcePath } from '../resources'; export interface Params { method?: RequestInit['method']; diff --git a/lib/api/useQueryClientConfig.tsx b/client/api/hooks/useQueryClientConfig.ts similarity index 88% rename from lib/api/useQueryClientConfig.tsx rename to client/api/hooks/useQueryClientConfig.ts index 09b2304285..8f31016af0 100644 --- a/lib/api/useQueryClientConfig.tsx +++ b/client/api/hooks/useQueryClientConfig.ts @@ -1,10 +1,10 @@ import { QueryClient } from '@tanstack/react-query'; import React from 'react'; -import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; -import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode'; +import getErrorObjPayload from 'client/shared/errors/get-error-obj-payload'; +import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; -import type { ResourceName } from './resources'; +import type { ResourceName } from '../resources'; export const retry = (failureCount: number, error: unknown) => { const errorPayload = getErrorObjPayload<{ status: number }>(error); diff --git a/lib/api/isBodyAllowed.ts b/client/api/is-body-allowed.ts similarity index 100% rename from lib/api/isBodyAllowed.ts rename to client/api/is-body-allowed.ts diff --git a/lib/api/isNeedProxy.ts b/client/api/is-need-proxy.ts similarity index 100% rename from lib/api/isNeedProxy.ts rename to client/api/is-need-proxy.ts diff --git a/lib/api/resources.ts b/client/api/resources.ts similarity index 92% rename from lib/api/resources.ts rename to client/api/resources.ts index 9645da1d49..ebda0f70c7 100644 --- a/lib/api/resources.ts +++ b/client/api/resources.ts @@ -1,4 +1,4 @@ -import type { ApiName, ApiResource } from './types'; +import type { ApiName, ApiResource, IsPaginated } from './types'; import type { AdminApiResourceName, AdminApiResourcePayload } from './services/admin'; import { ADMIN_API_RESOURCES } from './services/admin'; @@ -6,26 +6,27 @@ import { BENS_API_RESOURCES } from './services/bens'; import type { BensApiResourceName, BensApiResourcePayload, BensApiPaginationFilters, BensApiPaginationSorting } from './services/bens'; import { CLUSTERS_API_RESOURCES } from './services/clusters'; import type { ClustersApiResourceName, ClustersApiResourcePayload, ClustersApiPaginationFilters, ClustersApiPaginationSorting } from './services/clusters'; -import { CONTRACT_INFO_API_RESOURCES } from './services/contractInfo'; -import type { ContractInfoApiPaginationFilters, ContractInfoApiResourceName, ContractInfoApiResourcePayload } from './services/contractInfo'; +import { CONTRACT_INFO_API_RESOURCES } from './services/contract-info'; +import type { ContractInfoApiPaginationFilters, ContractInfoApiResourceName, ContractInfoApiResourcePayload } from './services/contract-info'; import { GENERAL_API_RESOURCES } from './services/general'; import type { GeneralApiResourceName, GeneralApiResourcePayload, GeneralApiPaginationFilters, GeneralApiPaginationSorting } from './services/general'; import type { InterchainIndexerApiPaginationFilters, InterchainIndexerApiResourceName, InterchainIndexerApiResourcePayload, -} from './services/interchainIndexer'; -import { INTERCHAIN_INDEXER_API_RESOURCES } from './services/interchainIndexer'; + InterchainIndexerApiPaginationSorting, +} from './services/interchain-indexer'; +import { INTERCHAIN_INDEXER_API_RESOURCES } from './services/interchain-indexer'; import type { MetadataApiResourceName, MetadataApiResourcePayload } from './services/metadata'; import { METADATA_API_RESOURCES } from './services/metadata'; import type { MultichainAggregatorApiPaginationFilters, MultichainAggregatorApiResourceName, MultichainAggregatorApiResourcePayload, -} from './services/multichainAggregator'; -import { MULTICHAIN_AGGREGATOR_API_RESOURCES } from './services/multichainAggregator'; -import type { MultichainStatsApiResourcePayload, MultichainStatsApiResourceName } from './services/multichainStats'; -import { MULTICHAIN_STATS_API_RESOURCES } from './services/multichainStats'; +} from './services/multichain-aggregator'; +import { MULTICHAIN_AGGREGATOR_API_RESOURCES } from './services/multichain-aggregator'; +import type { MultichainStatsApiResourcePayload, MultichainStatsApiResourceName } from './services/multichain-stats'; +import { MULTICHAIN_STATS_API_RESOURCES } from './services/multichain-stats'; import type { RewardsApiResourceName, RewardsApiResourcePayload } from './services/rewards'; import { REWARDS_API_RESOURCES } from './services/rewards'; import type { StatsApiResourceName, StatsApiResourcePayload } from './services/stats'; @@ -36,12 +37,11 @@ import type { TacOperationLifecycleApiResourceName, TacOperationLifecycleApiResourcePayload, } from './services/tac-operation-lifecycle'; -import { USER_OPS_API_RESOURCES } from './services/userOps'; -import type { IsPaginated } from './services/utils'; +import { USER_OPS_API_RESOURCES } from './services/user-ops'; import { VISUALIZE_API_RESOURCES } from './services/visualize'; import type { VisualizeApiResourceName, VisualizeApiResourcePayload } from './services/visualize'; -import { ZETA_CHAIN_API_RESOURCES } from './services/zetaChain'; -import type { ZetaChainApiPaginationFilters, ZetaChainApiResourceName, ZetaChainApiResourcePayload } from './services/zetaChain'; +import { ZETA_CHAIN_API_RESOURCES } from './services/zeta-chain'; +import type { ZetaChainApiPaginationFilters, ZetaChainApiResourceName, ZetaChainApiResourcePayload } from './services/zeta-chain'; export const RESOURCES = { admin: ADMIN_API_RESOURCES, @@ -137,6 +137,7 @@ export type PaginationSorting = R extends BensApiResourceName ? BensApiPaginationSorting : R extends ClustersApiResourceName ? ClustersApiPaginationSorting : R extends GeneralApiResourceName ? GeneralApiPaginationSorting : +R extends InterchainIndexerApiResourceName ? InterchainIndexerApiPaginationSorting : never; /* eslint-enable @stylistic/indent */ diff --git a/lib/api/services/admin.ts b/client/api/services/admin.ts similarity index 57% rename from lib/api/services/admin.ts rename to client/api/services/admin.ts index c9a50f4c35..f3b6f4b50e 100644 --- a/lib/api/services/admin.ts +++ b/client/api/services/admin.ts @@ -4,28 +4,28 @@ import type { MarketplaceApp } from 'types/client/marketplace'; export const ADMIN_API_RESOURCES = { public_tag_application: { - path: '/api/v1/chains/:chainId/metadata-submissions/tag', - pathParams: [ 'chainId' as const ], + path: '/api/v1/chains/:instanceId/metadata-submissions/tag', + pathParams: [ 'instanceId' as const ], }, token_info_applications_config: { - path: '/api/v1/chains/:chainId/token-info-submissions/selectors', - pathParams: [ 'chainId' as const ], + path: '/api/v1/chains/:instanceId/token-info-submissions/selectors', + pathParams: [ 'instanceId' as const ], }, token_info_applications: { - path: '/api/v1/chains/:chainId/token-info-submissions{/:id}', - pathParams: [ 'chainId' as const, 'id' as const ], + path: '/api/v1/chains/:instanceId/token-info-submissions{/:id}', + pathParams: [ 'instanceId' as const, 'id' as const ], }, marketplace_dapps: { - path: '/api/v1/chains/:chainId/marketplace/dapps', - pathParams: [ 'chainId' as const ], + path: '/api/v1/chains/:instanceId/marketplace/dapps', + pathParams: [ 'instanceId' as const ], }, marketplace_dapp: { - path: '/api/v1/chains/:chainId/marketplace/dapps/:dappId', - pathParams: [ 'chainId' as const, 'dappId' as const ], + path: '/api/v1/chains/:instanceId/marketplace/dapps/:dappId', + pathParams: [ 'instanceId' as const, 'dappId' as const ], }, marketplace_rate_dapp: { - path: '/api/v1/chains/:chainId/marketplace/dapps/:dappId/ratings', - pathParams: [ 'chainId' as const, 'dappId' as const ], + path: '/api/v1/chains/:instanceId/marketplace/dapps/:dappId/ratings', + pathParams: [ 'instanceId' as const, 'dappId' as const ], }, } satisfies Record; diff --git a/lib/api/services/bens.ts b/client/api/services/bens.ts similarity index 100% rename from lib/api/services/bens.ts rename to client/api/services/bens.ts diff --git a/lib/api/services/clusters.ts b/client/api/services/clusters.ts similarity index 100% rename from lib/api/services/clusters.ts rename to client/api/services/clusters.ts diff --git a/lib/api/services/contractInfo.ts b/client/api/services/contract-info.ts similarity index 69% rename from lib/api/services/contractInfo.ts rename to client/api/services/contract-info.ts index e0d67bd57d..9857fcfa49 100644 --- a/lib/api/services/contractInfo.ts +++ b/client/api/services/contract-info.ts @@ -5,26 +5,26 @@ import type { TokenVerifiedInfo } from 'types/api/token'; export const CONTRACT_INFO_API_RESOURCES = { address_verification: { - path: '/api/v1/chains/:chainId/verified-addresses:type', - pathParams: [ 'chainId' as const, 'type' as const ], + path: '/api/v1/chains/:instanceId/verified-addresses:type', + pathParams: [ 'instanceId' as const, 'type' as const ], }, verified_addresses: { - path: '/api/v1/chains/:chainId/verified-addresses', - pathParams: [ 'chainId' as const ], + path: '/api/v1/chains/:instanceId/verified-addresses', + pathParams: [ 'instanceId' as const ], }, token_verified_info: { - path: '/api/v1/chains/:chainId/token-infos/:hash', - pathParams: [ 'chainId' as const, 'hash' as const ], + path: '/api/v1/chains/:instanceId/token-infos/:hash', + pathParams: [ 'instanceId' as const, 'hash' as const ], }, pools: { - path: '/api/v1/chains/:chainId/pools', - pathParams: [ 'chainId' as const ], + path: '/api/v1/chains/:instanceId/pools', + pathParams: [ 'instanceId' as const ], filterFields: [ 'query' as const ], paginated: true, }, pool: { - path: '/api/v1/chains/:chainId/pools/:hash', - pathParams: [ 'chainId' as const, 'hash' as const ], + path: '/api/v1/chains/:instanceId/pools/:hash', + pathParams: [ 'instanceId' as const, 'hash' as const ], }, } satisfies Record; diff --git a/lib/api/services/general/account.ts b/client/api/services/general/account.ts similarity index 100% rename from lib/api/services/general/account.ts rename to client/api/services/general/account.ts diff --git a/lib/api/services/general/address.ts b/client/api/services/general/address.ts similarity index 95% rename from lib/api/services/general/address.ts rename to client/api/services/general/address.ts index 8c36659b5c..9cdb246074 100644 --- a/lib/api/services/general/address.ts +++ b/client/api/services/general/address.ts @@ -1,10 +1,11 @@ import type { ApiResource } from '../../types'; +import type { AddressesMetadataSearchFilters, AddressesMetadataSearchResult } from 'client/features/address-metadata/types/api'; +import type { AddressEpochRewardsResponse } from 'client/features/chain-variants/celo/types/api'; import type { AddressCounters, AddressBlocksValidatedResponse, AddressTokensResponse, AddressCollectionsResponse, - AddressEpochRewardsResponse, AddressNFTsResponse, AddressWithdrawalsResponse, AddressXStarResponse, @@ -20,11 +21,10 @@ import type { AddressTokensFilter, AddressNFTTokensFilter, AddressTokenBalancesResponse, -} from 'types/api/address'; -import type { AddressesMetadataSearchFilters, AddressesMetadataSearchResult, AddressesResponse } from 'types/api/addresses'; + AddressesResponse } from 'client/slices/address/types/api'; +import type { LogsResponseAddress } from 'client/slices/log/types/api'; +import type { TransactionsSorting } from 'client/slices/tx/types/api'; import type { DepositsResponse } from 'types/api/deposits'; -import type { LogsResponseAddress } from 'types/api/log'; -import type { TransactionsSorting } from 'types/api/transaction'; export const GENERAL_API_ADDRESS_RESOURCES = { // ADDRESSES diff --git a/lib/api/services/general/block.ts b/client/api/services/general/block.ts similarity index 92% rename from lib/api/services/general/block.ts rename to client/api/services/general/block.ts index c8167f9970..f36f843f1f 100644 --- a/lib/api/services/general/block.ts +++ b/client/api/services/general/block.ts @@ -1,4 +1,5 @@ import type { ApiResource } from '../../types'; +import type { TxsWithBlobsFilters } from 'client/features/data-availability/types/api'; import type { BlocksResponse, BlockTransactionsResponse, @@ -7,9 +8,8 @@ import type { BlockWithdrawalsResponse, BlockCountdownResponse, BlockInternalTransactionsResponse, -} from 'types/api/block'; +} from 'client/slices/block/types/api'; import type { DepositsResponse } from 'types/api/deposits'; -import type { TTxsWithBlobsFilters } from 'types/api/txsFilters'; export const GENERAL_API_BLOCK_RESOURCES = { blocks: { @@ -63,6 +63,6 @@ never; /* eslint-disable @stylistic/indent */ export type GeneralApiBlockPaginationFilters = R extends 'general:blocks' ? BlockFilters : -R extends 'general:block_txs' ? TTxsWithBlobsFilters : +R extends 'general:block_txs' ? TxsWithBlobsFilters : never; /* eslint-enable @stylistic/indent */ diff --git a/lib/api/services/general/contract.ts b/client/api/services/general/contract.ts similarity index 100% rename from lib/api/services/general/contract.ts rename to client/api/services/general/contract.ts diff --git a/lib/api/services/general/index.ts b/client/api/services/general/index.ts similarity index 100% rename from lib/api/services/general/index.ts rename to client/api/services/general/index.ts diff --git a/lib/api/services/general/misc.ts b/client/api/services/general/misc.ts similarity index 95% rename from lib/api/services/general/misc.ts rename to client/api/services/general/misc.ts index 3d5e22ab24..1a601acb91 100644 --- a/lib/api/services/general/misc.ts +++ b/client/api/services/general/misc.ts @@ -1,13 +1,17 @@ import type { ApiResource } from '../../types'; +import type { CsvExportItemResponse, CsvExportConfig } from 'client/features/csv-export/types/api'; +import type { Block } from 'client/slices/block/types/api'; +import type { + Transaction, +} from 'client/slices/tx/types/api'; import type { AdvancedFilterParams, AdvancedFilterResponse, AdvancedFilterMethodsResponse } from 'types/api/advancedFilter'; import type { ArbitrumL2TxnBatchesItem, ArbitrumLatestDepositsResponse, } from 'types/api/arbitrumL2'; import type { Blob } from 'types/api/blobs'; -import type { Block } from 'types/api/block'; import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransactionResponse } from 'types/api/charts'; -import type { BackendConfig, BackendVersionConfig, CeloConfig, ContractLanguagesConfig, CsvExportConfig } from 'types/api/configs'; +import type { BackendConfig, BackendVersionConfig, CeloConfig, ContractLanguagesConfig } from 'types/api/configs'; import type { HotContractsFilters, HotContractsResponse, HotContractsSorting } from 'types/api/contracts'; import type { DepositsResponse, DepositsCounters } from 'types/api/deposits'; import type { CeloEpochDetails, CeloEpochElectionRewardDetailsResponse, CeloEpochListResponse } from 'types/api/epochs'; @@ -18,9 +22,6 @@ import type { } from 'types/api/optimisticL2'; import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'types/api/search'; import type { HomeStats } from 'types/api/stats'; -import type { - Transaction, -} from 'types/api/transaction'; import type { TxInterpretationResponse } from 'types/api/txInterpretation'; import type { UserOpsResponse, UserOp, UserOpsFilters, UserOpsAccount } from 'types/api/userOps'; import type { @@ -35,9 +36,6 @@ import type { ValidatorZilliqa, } from 'types/api/validators'; import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals'; -import type { - ZkEvmL2TxnBatchesItem, -} from 'types/api/zkEvmL2'; export const GENERAL_API_MISC_RESOURCES = { // WITHDRAWALS @@ -95,9 +93,6 @@ export const GENERAL_API_MISC_RESOURCES = { homepage_txs: { path: '/api/v2/main-page/transactions', }, - homepage_zkevm_l2_batches: { - path: '/api/v2/main-page/zkevm/batches/confirmed', - }, homepage_arbitrum_l2_batches: { path: '/api/v2/main-page/arbitrum/batches/committed', }, @@ -107,9 +102,6 @@ export const GENERAL_API_MISC_RESOURCES = { homepage_indexing_status: { path: '/api/v2/main-page/indexing-status', }, - homepage_zkevm_latest_batch: { - path: '/api/v2/main-page/zkevm/batches/latest-number', - }, homepage_zksync_latest_batch: { path: '/api/v2/main-page/zksync/batches/latest-number', }, @@ -252,6 +244,12 @@ export const GENERAL_API_MISC_RESOURCES = { path: '/api/v2/advanced-filters/csv', }, + // CSV EXPORT + csv_exports_item: { + path: '/api/v2/csv-exports/:id', + pathParams: [ 'id' as const ], + }, + // CONFIGS config_backend: { path: '/api/v2/config/backend', @@ -289,10 +287,8 @@ R extends 'general:homepage_txs' ? Array : R extends 'general:homepage_txs_watchlist' ? Array : R extends 'general:homepage_optimistic_deposits' ? Array : R extends 'general:homepage_arbitrum_deposits' ? ArbitrumLatestDepositsResponse : -R extends 'general:homepage_zkevm_l2_batches' ? { items: Array } : R extends 'general:homepage_arbitrum_l2_batches' ? { items: Array } : R extends 'general:homepage_indexing_status' ? IndexingStatus : -R extends 'general:homepage_zkevm_latest_batch' ? number : R extends 'general:homepage_zksync_latest_batch' ? number : R extends 'general:homepage_arbitrum_latest_batch' ? number : R extends 'general:quick_search' ? Array : @@ -326,6 +322,7 @@ R extends 'general:deposits' ? DepositsResponse : R extends 'general:deposits_counters' ? DepositsCounters : R extends 'general:advanced_filter' ? AdvancedFilterResponse : R extends 'general:advanced_filter_methods' ? AdvancedFilterMethodsResponse : +R extends 'general:csv_exports_item' ? CsvExportItemResponse : never; /* eslint-enable @stylistic/indent */ diff --git a/lib/api/services/general/rollup.ts b/client/api/services/general/rollup.ts similarity index 88% rename from lib/api/services/general/rollup.ts rename to client/api/services/general/rollup.ts index 480abd15a1..ba36788c91 100644 --- a/lib/api/services/general/rollup.ts +++ b/client/api/services/general/rollup.ts @@ -6,7 +6,7 @@ import type { AddressMudRecordsFilter, AddressMudRecordsSorting, AddressMudRecord, -} from 'types/api/address'; +} from 'client/features/chain-variants/mud/types/api'; import type { ArbitrumL2MessagesResponse, ArbitrumL2TxnBatch, @@ -40,17 +40,9 @@ import type { ScrollL2MessagesResponse, } from 'types/api/scrollL2'; import type { ShibariumWithdrawalsResponse, ShibariumDepositsResponse } from 'types/api/shibarium'; -import type { - ZkEvmL2DepositsResponse, - ZkEvmL2TxnBatch, - ZkEvmL2TxnBatchesResponse, - ZkEvmL2TxnBatchTxs, - ZkEvmL2WithdrawalsResponse, -} from 'types/api/zkEvmL2'; import type { ZkSyncBatch, ZkSyncBatchesResponse, ZkSyncBatchTxs } from 'types/api/zkSyncL2'; export const GENERAL_API_ROLLUP_RESOURCES = { - // OPTIMISTIC optimistic_l2_deposits: { path: '/api/v2/optimism/deposits', filterFields: [], @@ -227,42 +219,6 @@ export const GENERAL_API_ROLLUP_RESOURCES = { paginated: true, }, - // zkEvm - zkevm_l2_deposits: { - path: '/api/v2/zkevm/deposits', - filterFields: [], - paginated: true, - }, - zkevm_l2_deposits_count: { - path: '/api/v2/zkevm/deposits/count', - }, - zkevm_l2_withdrawals: { - path: '/api/v2/zkevm/withdrawals', - filterFields: [], - paginated: true, - }, - zkevm_l2_withdrawals_count: { - path: '/api/v2/zkevm/withdrawals/count', - }, - zkevm_l2_txn_batches: { - path: '/api/v2/zkevm/batches', - filterFields: [], - paginated: true, - }, - zkevm_l2_txn_batches_count: { - path: '/api/v2/zkevm/batches/count', - }, - zkevm_l2_txn_batch: { - path: '/api/v2/zkevm/batches/:number', - pathParams: [ 'number' as const ], - }, - zkevm_l2_txn_batch_txs: { - path: '/api/v2/transactions/zkevm-batch/:number', - pathParams: [ 'number' as const ], - filterFields: [], - paginated: true, - }, - // SHIBARIUM shibarium_deposits: { path: '/api/v2/shibarium/deposits', @@ -358,14 +314,6 @@ R extends 'general:arbitrum_l2_txn_batch_txs' ? ArbitrumL2BatchTxs : R extends 'general:arbitrum_l2_txn_batch_blocks' ? ArbitrumL2BatchBlocks : R extends 'general:arbitrum_l2_txn_withdrawals' ? ArbitrumL2TxnWithdrawalsResponse : R extends 'general:arbitrum_l2_message_claim' ? ArbitrumL2MessageClaimResponse : -R extends 'general:zkevm_l2_deposits' ? ZkEvmL2DepositsResponse : -R extends 'general:zkevm_l2_deposits_count' ? number : -R extends 'general:zkevm_l2_withdrawals' ? ZkEvmL2WithdrawalsResponse : -R extends 'general:zkevm_l2_withdrawals_count' ? number : -R extends 'general:zkevm_l2_txn_batches' ? ZkEvmL2TxnBatchesResponse : -R extends 'general:zkevm_l2_txn_batches_count' ? number : -R extends 'general:zkevm_l2_txn_batch' ? ZkEvmL2TxnBatch : -R extends 'general:zkevm_l2_txn_batch_txs' ? ZkEvmL2TxnBatchTxs : R extends 'general:zksync_l2_txn_batches' ? ZkSyncBatchesResponse : R extends 'general:zksync_l2_txn_batches_count' ? number : R extends 'general:zksync_l2_txn_batch' ? ZkSyncBatch : diff --git a/lib/api/services/general/token.ts b/client/api/services/general/token.ts similarity index 100% rename from lib/api/services/general/token.ts rename to client/api/services/general/token.ts diff --git a/lib/api/services/general/tx.ts b/client/api/services/general/tx.ts similarity index 88% rename from lib/api/services/general/tx.ts rename to client/api/services/general/tx.ts index 82e3fbe27b..aea8e9947e 100644 --- a/lib/api/services/general/tx.ts +++ b/client/api/services/general/tx.ts @@ -1,21 +1,21 @@ import type { ApiResource } from '../../types'; -import type { TxBlobs } from 'types/api/blobs'; -import type { FheOperationsResponse } from 'types/api/fheOperations'; -import type { InternalTransactionFilters, InternalTransactionsResponse } from 'types/api/internalTransaction'; -import type { LogsResponseTx } from 'types/api/log'; -import type { RawTracesResponse } from 'types/api/rawTrace'; -import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer'; +import type { TransactionsResponseWatchlist } from 'client/features/account/types/api'; +import type { TransactionsResponseWithBlobs, TxsWithBlobsFilters } from 'client/features/data-availability/types/api'; +import type { LogsResponseTx } from 'client/slices/log/types/api'; import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction, - TransactionsResponseWatchlist, - TransactionsResponseWithBlobs, TransactionsStats, -} from 'types/api/transaction'; + TxsFilters, + TxStateChanges, + TxRawTracesResponse, +} from 'client/slices/tx/types/api'; +import type { TxBlobs } from 'types/api/blobs'; +import type { FheOperationsResponse } from 'types/api/fheOperations'; +import type { InternalTransactionFilters, InternalTransactionsResponse } from 'types/api/internalTransaction'; +import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer'; import type { TxInterpretationResponse } from 'types/api/txInterpretation'; -import type { TTxsFilters, TTxsWithBlobsFilters } from 'types/api/txsFilters'; -import type { TxStateChanges } from 'types/api/txStateChanges'; export const GENERAL_API_TX_RESOURCES = { txs_stats: { @@ -119,7 +119,7 @@ R extends 'general:tx' ? Transaction : R extends 'general:tx_logs' ? LogsResponseTx : R extends 'general:tx_token_transfers' ? TokenTransferResponse : R extends 'general:tx_fhe_operations' ? FheOperationsResponse : -R extends 'general:tx_raw_trace' ? RawTracesResponse : +R extends 'general:tx_raw_trace' ? TxRawTracesResponse : R extends 'general:tx_state_changes' ? TxStateChanges : R extends 'general:tx_blobs' ? TxBlobs : R extends 'general:tx_interpretation' ? TxInterpretationResponse : @@ -130,8 +130,8 @@ never; /* eslint-disable @stylistic/indent */ export type GeneralApiTxPaginationFilters = -R extends 'general:txs_validated' | 'general:txs_pending' ? TTxsFilters : -R extends 'general:txs_with_blobs' ? TTxsWithBlobsFilters : +R extends 'general:txs_validated' | 'general:txs_pending' ? TxsFilters : +R extends 'general:txs_with_blobs' ? TxsWithBlobsFilters : R extends 'general:tx_token_transfers' ? TokenTransferFilters : R extends 'general:internal_txs' ? InternalTransactionFilters : never; diff --git a/lib/api/services/general/v1.ts b/client/api/services/general/v1.ts similarity index 87% rename from lib/api/services/general/v1.ts rename to client/api/services/general/v1.ts index 740852735c..924a114a95 100644 --- a/lib/api/services/general/v1.ts +++ b/client/api/services/general/v1.ts @@ -1,5 +1,5 @@ import type { ApiResource } from '../../types'; -import type { BlockCountdownResponse } from 'types/api/block'; +import type { BlockCountdownResponse } from 'client/slices/block/types/api'; export const GENERAL_API_V1_RESOURCES = { graphql: { diff --git a/lib/api/services/interchainIndexer.ts b/client/api/services/interchain-indexer.ts similarity index 59% rename from lib/api/services/interchainIndexer.ts rename to client/api/services/interchain-indexer.ts index 5cf0a937a9..ad6752ae03 100644 --- a/lib/api/services/interchainIndexer.ts +++ b/client/api/services/interchain-indexer.ts @@ -1,8 +1,15 @@ import type { ApiResource } from '../types'; import type * as interchainIndexer from '@blockscout/interchain-indexer-types'; -import type { CrossChainMessageFilters, CrossChainTransferFilters } from 'types/api/interchainIndexer'; +import type { + CrossChainFilters, + CrossChainChainsStatsSorting, + CrossChainBridgedTokensSorting, +} from 'client/features/cross-chain-txs/types/api'; export const INTERCHAIN_INDEXER_API_RESOURCES = { + chains: { + path: '/api/v1/interchain/chains', + }, messages: { path: '/api/v1/interchain/messages', filterFields: [ 'q' as const ], @@ -45,12 +52,30 @@ export const INTERCHAIN_INDEXER_API_RESOURCES = { stats_common: { path: '/api/v1/stats/common', }, + stats_chains: { + path: '/api/v1/stats/chains', + paginated: true, + }, + stats_chain_messages_sent: { + path: '/api/v1/stats/chain/:chainId/messages-paths/sent', + pathParams: [ 'chainId' as const ], + }, + stats_chain_messages_received: { + path: '/api/v1/stats/chain/:chainId/messages-paths/received', + pathParams: [ 'chainId' as const ], + }, + bridged_tokens: { + path: '/api/v1/stats/chain/:chainId/bridged-tokens', + pathParams: [ 'chainId' as const ], + paginated: true, + }, } satisfies Record; export type InterchainIndexerApiResourceName = `interchainIndexer:${ keyof typeof INTERCHAIN_INDEXER_API_RESOURCES }`; /* eslint-disable @stylistic/indent */ export type InterchainIndexerApiResourcePayload = +R extends 'interchainIndexer:chains' ? interchainIndexer.GetChainsResponse : R extends 'interchainIndexer:messages' ? interchainIndexer.GetMessagesResponse : R extends 'interchainIndexer:message' ? interchainIndexer.InterchainMessage : R extends 'interchainIndexer:tx_messages' ? interchainIndexer.GetMessagesResponse : @@ -60,12 +85,25 @@ R extends 'interchainIndexer:tx_transfers' ? interchainIndexer.GetTransfersRespo R extends 'interchainIndexer:address_transfers' ? interchainIndexer.GetTransfersResponse : R extends 'interchainIndexer:stats_daily' ? interchainIndexer.GetDailyStatisticsResponse : R extends 'interchainIndexer:stats_common' ? interchainIndexer.GetCommonStatisticsResponse : +R extends 'interchainIndexer:stats_chains' ? interchainIndexer.GetChainsStatsResponse : +R extends 'interchainIndexer:bridged_tokens' ? interchainIndexer.GetBridgedTokensResponse : +R extends 'interchainIndexer:stats_chain_messages_sent' ? interchainIndexer.GetMessagePathsResponse : +R extends 'interchainIndexer:stats_chain_messages_received' ? interchainIndexer.GetMessagePathsResponse : never; /* eslint-enable @stylistic/indent */ /* eslint-disable @stylistic/indent */ export type InterchainIndexerApiPaginationFilters = -R extends 'interchainIndexer:messages' ? CrossChainMessageFilters : -R extends 'interchainIndexer:transfers' ? CrossChainTransferFilters : +R extends 'interchainIndexer:messages' ? CrossChainFilters : +R extends 'interchainIndexer:transfers' ? CrossChainFilters : +R extends 'interchainIndexer:stats_chains' ? CrossChainFilters : +R extends 'interchainIndexer:bridged_tokens' ? CrossChainFilters : +never; +/* eslint-enable @stylistic/indent */ + +/* eslint-disable @stylistic/indent */ +export type InterchainIndexerApiPaginationSorting = +R extends 'interchainIndexer:stats_chains' ? CrossChainChainsStatsSorting : +R extends 'interchainIndexer:bridged_tokens' ? CrossChainBridgedTokensSorting : never; /* eslint-enable @stylistic/indent */ diff --git a/lib/api/services/metadata.ts b/client/api/services/metadata.ts similarity index 94% rename from lib/api/services/metadata.ts rename to client/api/services/metadata.ts index 2f88d09de4..422af3cff2 100644 --- a/lib/api/services/metadata.ts +++ b/client/api/services/metadata.ts @@ -1,5 +1,5 @@ import type { ApiResource } from '../types'; -import type { AddressMetadataInfo, PublicTagTypesResponse } from 'types/api/addressMetadata'; +import type { AddressMetadataInfo, PublicTagTypesResponse } from 'client/features/address-metadata/types/api'; export const METADATA_API_RESOURCES = { info: { diff --git a/lib/api/services/multichainAggregator.ts b/client/api/services/multichain-aggregator.ts similarity index 100% rename from lib/api/services/multichainAggregator.ts rename to client/api/services/multichain-aggregator.ts diff --git a/lib/api/services/multichainStats.ts b/client/api/services/multichain-stats.ts similarity index 100% rename from lib/api/services/multichainStats.ts rename to client/api/services/multichain-stats.ts diff --git a/lib/api/services/rewards.ts b/client/api/services/rewards.ts similarity index 100% rename from lib/api/services/rewards.ts rename to client/api/services/rewards.ts diff --git a/lib/api/services/stats.ts b/client/api/services/stats.ts similarity index 100% rename from lib/api/services/stats.ts rename to client/api/services/stats.ts diff --git a/lib/api/services/tac-operation-lifecycle.ts b/client/api/services/tac-operation-lifecycle.ts similarity index 100% rename from lib/api/services/tac-operation-lifecycle.ts rename to client/api/services/tac-operation-lifecycle.ts diff --git a/lib/api/services/userOps.ts b/client/api/services/user-ops.ts similarity index 100% rename from lib/api/services/userOps.ts rename to client/api/services/user-ops.ts diff --git a/lib/api/services/visualize.ts b/client/api/services/visualize.ts similarity index 100% rename from lib/api/services/visualize.ts rename to client/api/services/visualize.ts diff --git a/lib/api/services/zetaChain.ts b/client/api/services/zeta-chain.ts similarity index 100% rename from lib/api/services/zetaChain.ts rename to client/api/services/zeta-chain.ts diff --git a/lib/socket/context.tsx b/client/api/socket/context.tsx similarity index 100% rename from lib/socket/context.tsx rename to client/api/socket/context.tsx diff --git a/lib/socket/types.ts b/client/api/socket/types.ts similarity index 92% rename from lib/socket/types.ts rename to client/api/socket/types.ts index 8dbf55670f..e41e7c11b3 100644 --- a/lib/socket/types.ts +++ b/client/api/socket/types.ts @@ -2,15 +2,13 @@ import type { Channel } from 'phoenix'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type * as zetaChainCCTXType from '@blockscout/zetachain-cctx-types'; -import type { AddressCoinBalanceHistoryItem, AddressTokensBalancesSocketMessage } from 'types/api/address'; +import type { AddressCoinBalanceHistoryItem, AddressTokensBalancesSocketMessage } from 'client/slices/address/types/api'; +import type { NewBlockSocketResponse } from 'client/slices/block/types/api'; +import type { Transaction, TxRawTracesResponse } from 'client/slices/tx/types/api'; import type { NewArbitrumBatchSocketResponse } from 'types/api/arbitrumL2'; -import type { NewBlockSocketResponse } from 'types/api/block'; import type { SmartContractVerificationResponse } from 'types/api/contract'; -import type { RawTracesResponse } from 'types/api/rawTrace'; import type { TokenInstanceMetadataSocketMessage } from 'types/api/token'; import type { TokenTransfer } from 'types/api/tokenTransfer'; -import type { Transaction } from 'types/api/transaction'; -import type { NewZkEvmBatchSocketResponse } from 'types/api/zkEvmL2'; export type SocketMessageParams = SocketMessage.NewBlock | SocketMessage.NewBlockMultichain | @@ -43,7 +41,6 @@ SocketMessage.TokenTransfers | SocketMessage.TokenTotalSupply | SocketMessage.TokenInstanceMetadataFetched | SocketMessage.ContractVerification | -SocketMessage.NewZkEvmL2Batch | SocketMessage.NewArbitrumL2Batch | SocketMessage.NewZetaChainCCTXs | SocketMessage.Unknown; @@ -60,7 +57,7 @@ export namespace SocketMessage { export type BlocksIndexStatus = SocketMessageParamsGeneric<'index_status', { finished: boolean; ratio: string }>; export type InternalTxsIndexStatus = SocketMessageParamsGeneric<'index_status', { finished: boolean; ratio: string }>; export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>; - export type TxRawTrace = SocketMessageParamsGeneric<'raw_trace', RawTracesResponse>; + export type TxRawTrace = SocketMessageParamsGeneric<'raw_trace', TxRawTracesResponse>; export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>; export type NewInteropMessage = SocketMessageParamsGeneric<'new_messages', Array>; export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>; @@ -87,7 +84,6 @@ export namespace SocketMessage { export type TokenTotalSupply = SocketMessageParamsGeneric<'total_supply', { total_supply: number }>; export type TokenInstanceMetadataFetched = SocketMessageParamsGeneric<'fetched_token_instance_metadata', TokenInstanceMetadataSocketMessage>; export type ContractVerification = SocketMessageParamsGeneric<'verification_result', SmartContractVerificationResponse>; - export type NewZkEvmL2Batch = SocketMessageParamsGeneric<'new_zkevm_confirmed_batch', NewZkEvmBatchSocketResponse>; export type NewArbitrumL2Batch = SocketMessageParamsGeneric<'new_arbitrum_batch', NewArbitrumBatchSocketResponse>; export type NewZetaChainCCTXs = SocketMessageParamsGeneric<'new_cctxs', Array>; export type Unknown = SocketMessageParamsGeneric; diff --git a/lib/socket/useSocketChannel.tsx b/client/api/socket/useSocketChannel.ts similarity index 100% rename from lib/socket/useSocketChannel.tsx rename to client/api/socket/useSocketChannel.ts diff --git a/lib/socket/useSocketMessage.tsx b/client/api/socket/useSocketMessage.ts similarity index 88% rename from lib/socket/useSocketMessage.tsx rename to client/api/socket/useSocketMessage.ts index fc3e2a96ba..8933034b20 100644 --- a/lib/socket/useSocketMessage.tsx +++ b/client/api/socket/useSocketMessage.ts @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react'; -import type { SocketMessageParams } from 'lib/socket/types'; +import type { SocketMessageParams } from './types'; export default function useSocketMessage({ channel, event, handler }: SocketMessageParams) { const handlerRef = useRef(handler); diff --git a/lib/api/types.ts b/client/api/types.ts similarity index 81% rename from lib/api/types.ts rename to client/api/types.ts index 6aa8fb537d..290d972ca3 100644 --- a/lib/api/types.ts +++ b/client/api/types.ts @@ -10,3 +10,5 @@ export interface ApiResource { paginated?: boolean; headers?: RequestInit['headers']; } + +export type IsPaginated = R extends { paginated: true } ? true : false; diff --git a/ui/shared/tx/TxWatchListTags.tsx b/client/features/account/components/TxWatchListTags.tsx similarity index 92% rename from ui/shared/tx/TxWatchListTags.tsx rename to client/features/account/components/TxWatchListTags.tsx index 891cadb240..e7c7f33b54 100644 --- a/ui/shared/tx/TxWatchListTags.tsx +++ b/client/features/account/components/TxWatchListTags.tsx @@ -1,7 +1,7 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import { Badge } from 'toolkit/chakra/badge'; diff --git a/ui/address/details/AddressFavoriteButton.tsx b/client/features/account/pages/address/AddressFavoriteButton.tsx similarity index 93% rename from ui/address/details/AddressFavoriteButton.tsx rename to client/features/account/pages/address/AddressFavoriteButton.tsx index 48f4e7f6c5..1f0c351272 100644 --- a/ui/address/details/AddressFavoriteButton.tsx +++ b/client/features/account/pages/address/AddressFavoriteButton.tsx @@ -3,10 +3,12 @@ import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React from 'react'; +import { getResourceKey } from 'client/api/hooks/useApiQuery'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import usePreventFocusAfterModalClosing from 'client/shared/hooks/usePreventFocusAfterModalClosing'; + import config from 'configs/app'; -import { getResourceKey } from 'lib/api/useApiQuery'; -import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing'; -import * as mixpanel from 'lib/mixpanel/index'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/ui/txs/TxsWatchlist.tsx b/client/features/account/pages/tx-index-watchlist/TxsWatchlist.tsx similarity index 83% rename from ui/txs/TxsWatchlist.tsx rename to client/features/account/pages/tx-index-watchlist/TxsWatchlist.tsx index 6dedd5d267..dd0edc4af0 100644 --- a/ui/txs/TxsWatchlist.tsx +++ b/client/features/account/pages/tx-index-watchlist/TxsWatchlist.tsx @@ -1,8 +1,9 @@ import React from 'react'; +import TxsWithFrontendSorting from 'client/slices/tx/pages/index/list/TxsWithFrontendSorting'; + import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import useRedirectForInvalidAuthToken from 'ui/snippets/auth/useRedirectForInvalidAuthToken'; -import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting'; type Props = { query: QueryWithPagesResult<'general:txs_watchlist'>; diff --git a/client/features/account/types/api.ts b/client/features/account/types/api.ts new file mode 100644 index 0000000000..d1ceb78116 --- /dev/null +++ b/client/features/account/types/api.ts @@ -0,0 +1,10 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +export interface TransactionsResponseWatchlist { + items: Array; + next_page_params: { + block_number: number; + index: number; + items_count: 50; + } | null; +} diff --git a/lib/getErrorMessage.ts b/client/features/account/utils/get-api-error-text.ts similarity index 100% rename from lib/getErrorMessage.ts rename to client/features/account/utils/get-api-error-text.ts diff --git a/ui/address/address3rdPartyWidgets/Address3rdPartyWidgetCard.tsx b/client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgetCard.tsx similarity index 86% rename from ui/address/address3rdPartyWidgets/Address3rdPartyWidgetCard.tsx rename to client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgetCard.tsx index 34cc12bb21..9af2e0dea2 100644 --- a/ui/address/address3rdPartyWidgets/Address3rdPartyWidgetCard.tsx +++ b/client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgetCard.tsx @@ -1,10 +1,11 @@ import { Flex, Text, chakra, Separator } from '@chakra-ui/react'; import { useCallback } from 'react'; -import type { Address3rdPartyWidget } from 'types/views/address'; +import type { Address3rdPartyWidget } from 'client/features/address-3rd-party-widgets/types/view'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; import config from 'configs/app'; -import * as mixpanel from 'lib/mixpanel/index'; import { Image } from 'toolkit/chakra/image'; import { LinkBox, LinkOverlay } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; @@ -28,7 +29,13 @@ function formatUrl(tpl: string, ctx: Record) { } const Address3rdPartyWidgetCard = ({ name, config, address, isLoading }: Props) => { - const { data, isLoading: isDataLoading } = useWidgetData(name, config?.valuePath, address, isLoading); + const { data, isLoading: isDataLoading } = useWidgetData({ + name, + valuePath: config?.valuePath, + valueTitlePath: config?.valueTitlePath, + address, + isLoading, + }); const handleClick = useCallback(() => { mixpanel.logEvent(mixpanel.EventTypes.ADDRESS_WIDGET, { Name: name }); @@ -44,7 +51,7 @@ const Address3rdPartyWidgetCard = ({ name, config, address, isLoading }: Props) chainId: config.chainIds?.[chainId] ?? chainId, }); - const [ integer, decimal ] = data?.split('.') || []; + const [ integer, decimal ] = data?.value?.split('.') || []; const content = isLoading ? ( <> @@ -60,7 +67,7 @@ const Address3rdPartyWidgetCard = ({ name, config, address, isLoading }: Props) <> - { data ? ( + { data?.value ? ( - { config.title } + + { config.title }{ data?.valueTitle !== undefined ? `: ${ data.valueTitle }` : '' } + { config.hint && ( async({ render, mockConfigResponse, mockAs await Promise.all(widgetsMock.widgets.map((widget, i) => mockApiResponse( 'general:address_3rd_party_info', - { value: widgetsMock.values[i] }, + { value: widgetsMock.values[i], valueTitle: 'Duck' }, { pathParams: { name: widget }, queryParams: { address: ADDRESS_HASH, chain_id: '1' } }, ), )); diff --git a/ui/address/Address3rdPartyWidgets.tsx b/client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgets.tsx similarity index 85% rename from ui/address/Address3rdPartyWidgets.tsx rename to client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgets.tsx index 7f867aeb13..90a0418dfa 100644 --- a/ui/address/Address3rdPartyWidgets.tsx +++ b/client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgets.tsx @@ -2,16 +2,17 @@ import { Grid, Flex } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import { useMemo } from 'react'; -import type { Address3rdPartyWidget } from 'types/views/address'; +import type { Address3rdPartyWidget } from 'client/features/address-3rd-party-widgets/types/view'; import { route } from 'nextjs-routes'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { Link } from 'toolkit/chakra/link'; -import Address3rdPartyWidgetCard from './address3rdPartyWidgets/Address3rdPartyWidgetCard'; -import useAddress3rdPartyWidgets from './address3rdPartyWidgets/useAddress3rdPartyWidgets'; +import Address3rdPartyWidgetCard from './Address3rdPartyWidgetCard'; +import useAddress3rdPartyWidgets from './useAddress3rdPartyWidgets'; type Props = { shouldRender?: boolean; diff --git a/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_dark-color-mode_base-view-dark-mode-1.png new file mode 100644 index 0000000000..07cbaaa19a Binary files /dev/null and b/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_base-view-dark-mode-1.png b/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_base-view-dark-mode-1.png new file mode 100644 index 0000000000..af61a42005 Binary files /dev/null and b/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_mobile-base-view-1.png b/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_mobile-base-view-1.png new file mode 100644 index 0000000000..86f9ac22ef Binary files /dev/null and b/client/features/address-3rd-party-widgets/pages/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/address/address3rdPartyWidgets/useAddress3rdPartyWidgets.tsx b/client/features/address-3rd-party-widgets/pages/address/useAddress3rdPartyWidgets.tsx similarity index 89% rename from ui/address/address3rdPartyWidgets/useAddress3rdPartyWidgets.tsx rename to client/features/address-3rd-party-widgets/pages/address/useAddress3rdPartyWidgets.tsx index 16802573b8..db58dbbe3a 100644 --- a/ui/address/address3rdPartyWidgets/useAddress3rdPartyWidgets.tsx +++ b/client/features/address-3rd-party-widgets/pages/address/useAddress3rdPartyWidgets.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; -import type { Address3rdPartyWidget } from 'types/views/address'; +import type { Address3rdPartyWidget } from 'client/features/address-3rd-party-widgets/types/view'; import config from 'configs/app'; diff --git a/client/features/address-3rd-party-widgets/pages/address/useWidgetData.ts b/client/features/address-3rd-party-widgets/pages/address/useWidgetData.ts new file mode 100644 index 0000000000..d2cea4c564 --- /dev/null +++ b/client/features/address-3rd-party-widgets/pages/address/useWidgetData.ts @@ -0,0 +1,69 @@ +import { get } from 'es-toolkit/compat'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import config from 'configs/app'; + +const RESOURCE_NAME = 'general:address_3rd_party_info'; + +const formatValue = (value: unknown): string | undefined => { + if (typeof value !== 'number' && typeof value !== 'string') { + return undefined; + } + + const num = Number(value); + if (!isNaN(num)) { + if (num === -1) { + return '0'; + } + return num.toLocaleString(); + } + + return String(value); +}; + +const formatValueTitle = (valueTitle: unknown): string | undefined => { + if (typeof valueTitle !== 'string' && typeof valueTitle !== 'number' && typeof valueTitle !== 'boolean') { + return; + } + + return String(valueTitle); +}; + +interface Props { + name: string; + valuePath?: string; + valueTitlePath?: string; + address: string; + isLoading: boolean; +} + +interface Response { + value: string | undefined; + valueTitle: string | undefined; +} + +export default function useWidgetData({ name, valuePath, valueTitlePath, address, isLoading }: Props) { + const query = useApiQuery(RESOURCE_NAME, { + pathParams: { name }, + queryParams: { address, chain_id: config.chain.id }, + queryOptions: { + select: (response) => { + try { + const value = valuePath ? get(response, valuePath) : undefined; + const valueTitle = valueTitlePath ? get(response, valueTitlePath) : undefined; + return { + value: formatValue(value), + valueTitle: formatValueTitle(valueTitle), + }; + } catch { + return undefined; + } + }, + enabled: !isLoading && Boolean(valuePath), + refetchOnMount: false, + }, + }); + + return query; +} diff --git a/ui/address/address3rdPartyWidgets/useWidgetsConfigQuery.tsx b/client/features/address-3rd-party-widgets/pages/address/useWidgetsConfigQuery.tsx similarity index 80% rename from ui/address/address3rdPartyWidgets/useWidgetsConfigQuery.tsx rename to client/features/address-3rd-party-widgets/pages/address/useWidgetsConfigQuery.tsx index c2d53cea36..684b052745 100644 --- a/ui/address/address3rdPartyWidgets/useWidgetsConfigQuery.tsx +++ b/client/features/address-3rd-party-widgets/pages/address/useWidgetsConfigQuery.tsx @@ -1,10 +1,11 @@ import { useQuery } from '@tanstack/react-query'; -import type { Address3rdPartyWidget } from 'types/views/address'; +import type { Address3rdPartyWidget } from 'client/features/address-3rd-party-widgets/types/view'; + +import useApiFetch from 'client/api/hooks/useFetch'; +import type { ResourceError } from 'client/api/resources'; import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useApiFetch from 'lib/hooks/useFetch'; import { WIDGET_CONFIG } from 'stubs/address3rdPartyWidgets'; const feature = config.features.address3rdPartyWidgets; diff --git a/client/features/address-3rd-party-widgets/types/view.ts b/client/features/address-3rd-party-widgets/types/view.ts new file mode 100644 index 0000000000..4a21e0f38b --- /dev/null +++ b/client/features/address-3rd-party-widgets/types/view.ts @@ -0,0 +1,13 @@ +export const ADDRESS_3RD_PARTY_WIDGET_PAGES = [ 'eoa', 'contract', 'token' ] as const; + +export type Address3rdPartyWidget = { + name: string; + url: string; + icon: string; + title: string; + hint?: string; + valuePath: string; + valueTitlePath?: string; + pages: Array; + chainIds?: Record; +}; diff --git a/lib/address/useAddressMetadataInfoQuery.ts b/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts similarity index 89% rename from lib/address/useAddressMetadataInfoQuery.ts rename to client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts index 8e77b82f7a..6c6c7ee7d7 100644 --- a/lib/address/useAddressMetadataInfoQuery.ts +++ b/client/features/address-metadata/hooks/useAddressMetadataInfoQuery.ts @@ -1,10 +1,11 @@ -import type { AddressMetadataInfoFormatted, AddressMetadataTagFormatted } from 'types/client/addressMetadata'; +import type { AddressMetadataInfoFormatted, AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { useMultichainContext } from 'lib/contexts/multichain'; -import parseMetaPayload from './parseMetaPayload'; +import parseMetaPayload from '../utils/parseMetaPayload'; export default function useAddressMetadataInfoQuery(addresses: Array, isEnabled = true) { diff --git a/lib/address/useAddressMetadataInitUpdate.ts b/client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts similarity index 87% rename from lib/address/useAddressMetadataInitUpdate.ts rename to client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts index d39cfdd377..79354fc260 100644 --- a/lib/address/useAddressMetadataInitUpdate.ts +++ b/client/features/address-metadata/hooks/useAddressMetadataInitUpdate.ts @@ -1,9 +1,10 @@ import React from 'react'; -import type { AddressCounters } from 'types/api/address'; +import type { AddressCounters } from 'client/slices/address/types/api'; + +import useApiFetch from 'client/api/hooks/useApiFetch'; import config from 'configs/app'; -import useApiFetch from 'lib/api/useApiFetch'; const feature = config.features.addressMetadata; diff --git a/types/api/addressMetadata.ts b/client/features/address-metadata/types/api.ts similarity index 83% rename from types/api/addressMetadata.ts rename to client/features/address-metadata/types/api.ts index f4a1074420..7d18bde4ed 100644 --- a/types/api/addressMetadata.ts +++ b/client/features/address-metadata/types/api.ts @@ -1,3 +1,5 @@ +import type { AddressesItem } from 'client/slices/address/types/api'; + export interface AddressMetadataInfo { addresses: Record; @@ -53,3 +55,13 @@ export interface PublicTagType { export interface PublicTagTypesResponse { tagTypes: Array; } + +export interface AddressesMetadataSearchResult { + items: Array; + next_page_params: null; +} + +export interface AddressesMetadataSearchFilters { + slug: string; + tag_type: string; +} diff --git a/types/client/addressMetadata.ts b/client/features/address-metadata/types/view.ts similarity index 76% rename from types/client/addressMetadata.ts rename to client/features/address-metadata/types/view.ts index 8281e1cf1b..6e4f56c432 100644 --- a/types/client/addressMetadata.ts +++ b/client/features/address-metadata/types/view.ts @@ -1,4 +1,4 @@ -import type { AddressMetadataTagApi } from 'types/api/addressMetadata'; +import type { AddressMetadataTagApi } from './api'; export interface AddressMetadataInfoFormatted { addresses: Record; diff --git a/lib/hooks/useAddressProfileApiQuery.tsx b/client/features/address-profile-api/hooks/useAddressProfileApiQuery.tsx similarity index 91% rename from lib/hooks/useAddressProfileApiQuery.tsx rename to client/features/address-profile-api/hooks/useAddressProfileApiQuery.tsx index ef39555d43..e673c55ef4 100644 --- a/lib/hooks/useAddressProfileApiQuery.tsx +++ b/client/features/address-profile-api/hooks/useAddressProfileApiQuery.tsx @@ -1,9 +1,10 @@ import { useQuery } from '@tanstack/react-query'; import * as v from 'valibot'; +import useFetch from 'client/api/hooks/useFetch'; +import type { ResourceError } from 'client/api/resources'; + import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useFetch from 'lib/hooks/useFetch'; const feature = config.features.addressProfileAPI; diff --git a/lib/networks/networkExplorers.ts b/client/features/alternative-explorers/utils/explorers.ts similarity index 91% rename from lib/networks/networkExplorers.ts rename to client/features/alternative-explorers/utils/explorers.ts index b5a4648d41..3b7f3e5472 100644 --- a/lib/networks/networkExplorers.ts +++ b/client/features/alternative-explorers/utils/explorers.ts @@ -27,7 +27,7 @@ import config from 'configs/app'; const stripTrailingSlash = (str: string) => str[str.length - 1] === '/' ? str.slice(0, -1) : str; const addLeadingSlash = (str: string) => str[0] === '/' ? str : '/' + str; -const networkExplorers: Array = (() => { +const chainExplorers: Array = (() => { return config.UI.explorers.items.map((explorer) => ({ ...explorer, baseUrl: stripTrailingSlash(explorer.baseUrl), @@ -35,4 +35,4 @@ const networkExplorers: Array = (() => { })); })(); -export default networkExplorers; +export default chainExplorers; diff --git a/ui/shared/chart/ChartIntervalSelect.tsx b/client/features/chain-stats/components/ChartIntervalSelect.tsx similarity index 90% rename from ui/shared/chart/ChartIntervalSelect.tsx rename to client/features/chain-stats/components/ChartIntervalSelect.tsx index 87d709a72a..2c3de8cae4 100644 --- a/ui/shared/chart/ChartIntervalSelect.tsx +++ b/client/features/chain-stats/components/ChartIntervalSelect.tsx @@ -1,13 +1,14 @@ import { createListCollection } from '@chakra-ui/react'; import React from 'react'; -import type { StatsInterval, StatsIntervalIds } from 'types/client/stats'; +import type { StatsInterval, StatsIntervalIds } from '../types/client'; import { Select } from 'toolkit/chakra/select'; import { Skeleton } from 'toolkit/chakra/skeleton'; import type { TagProps } from 'toolkit/chakra/tag'; import TagGroupSelect from 'ui/shared/tagGroupSelect/TagGroupSelect'; -import { STATS_INTERVALS } from 'ui/stats/constants'; + +import { STATS_INTERVALS } from '../utils/interval'; const intervalCollection = createListCollection({ items: Object.keys(STATS_INTERVALS).map((id: string) => ({ @@ -28,7 +29,7 @@ type Props = { selectTagSize?: TagProps['size']; }; -const ChartIntervalSelect = ({ interval, onIntervalChange, isLoading, selectTagSize }: Props) => { +const ChartIntervalSelect = ({ interval, onIntervalChange, isLoading, selectTagSize = 'lg' }: Props) => { const handleItemSelect = React.useCallback(({ value }: { value: Array }) => { onIntervalChange(value[0] as StatsIntervalIds); diff --git a/client/features/chain-stats/components/ChartWidgetContainer.tsx b/client/features/chain-stats/components/ChartWidgetContainer.tsx new file mode 100644 index 0000000000..9db84eb242 --- /dev/null +++ b/client/features/chain-stats/components/ChartWidgetContainer.tsx @@ -0,0 +1,82 @@ +import { chakra } from '@chakra-ui/react'; +import React from 'react'; + +import type { StatsIntervalIds } from '../types/client'; + +import { route, type Route } from 'nextjs-routes'; + +import { ChartResolution } from 'toolkit/components/charts'; +import { LineChartWidget } from 'toolkit/components/charts/line/LineChartWidget'; +import { useChartsConfig } from 'ui/shared/chart/config'; + +import useChartQuery from '../hooks/useChartQuery'; +import { getChartUrl } from '../utils/chart'; + +export interface Props { + id: string; + title: string; + description: string; + interval: StatsIntervalIds; + onLoadingError: () => void; + isLoading: boolean; + className?: string; + href?: Route; +}; + +const ChartWidgetContainer = ({ + id, + title, + description, + interval, + onLoadingError, + isLoading, + className, + href, +}: Props) => { + const query = useChartQuery({ id, resolution: ChartResolution.DAY, interval, enabled: !isLoading }); + + React.useEffect(() => { + if (query.isError) { + onLoadingError(); + } + }, [ query.isError, onLoadingError ]); + + const chartsConfig = useChartsConfig(); + + const data = query.data?.data; + const units = query.data?.info?.units; + + const charts = React.useMemo(() => { + if (!data || data.length === 0) { + return []; + } + + return [ + { + id, + name: 'Value', + items: data, + charts: chartsConfig, + units, + }, + ]; + }, [ data, id, chartsConfig, units ]); + + return ( + + ); +}; + +export default chakra(ChartWidgetContainer); diff --git a/client/features/chain-stats/hooks/useChainStats.tsx b/client/features/chain-stats/hooks/useChainStats.tsx new file mode 100644 index 0000000000..17ee16b827 --- /dev/null +++ b/client/features/chain-stats/hooks/useChainStats.tsx @@ -0,0 +1,112 @@ +import { uniqBy } from 'es-toolkit'; +import { useRouter } from 'next/router'; +import React from 'react'; + +import type { ChainStatsChart, ChainStatsPayload, ChainStatsSection, StatsIntervalIds } from '../types/client'; +import type { ClusterChainConfig } from 'types/multichain'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { CROSS_CHAIN_TXS_SECTIONS } from 'client/features/cross-chain-txs/utils/chain-stats'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + +import config from 'configs/app'; + +import { CHAIN_STATS_CHARTS } from '../stubs/charts'; + +function isSectionMatches(section: ChainStatsSection, currentSection: string): boolean { + return currentSection === 'all' || section.id === currentSection; +} + +function isChartNameMatches(q: string, chart: ChainStatsChart) { + return chart.title.toLowerCase().includes(q.toLowerCase()); +} + +interface Props { + chain?: ClusterChainConfig; +} + +export default function useChainStats({ chain }: Props = {}) { + const router = useRouter(); + + const [ sectionId, setSectionId ] = React.useState('all'); + const [ filterQuery, setFilterQuery ] = React.useState(''); + const [ initialFilterQuery, setInitialFilterQuery ] = React.useState(''); + const [ interval, setInterval ] = React.useState('oneMonth'); + + const { data, isPlaceholderData, isError } = useApiQuery<'stats:lines', unknown, ChainStatsPayload>('stats:lines', { + queryOptions: { + placeholderData: CHAIN_STATS_CHARTS, + select: (data) => { + const crossChainTxsFeature = (chain?.app_config || config).features.crossChainTxs; + if (!crossChainTxsFeature.isEnabled) { + return data; + } + + const sections: Array = data.sections.slice(); + + for (const extraSection of CROSS_CHAIN_TXS_SECTIONS) { + const existingSection = sections.find((section) => section.id === extraSection.id); + if (existingSection) { + existingSection.charts = uniqBy([ + ...existingSection.charts, + ...extraSection.charts, + ], (chart) => chart.id); + } else { + sections.push(extraSection); + } + } + + return { + sections, + }; + }, + }, + chain, + }); + + React.useEffect(() => { + if (!isPlaceholderData && !isError) { + const chartId = getQueryParamString(router.query.chartId); + const chartName = data?.sections.map((section) => section.charts.find((chart) => chart.id === chartId)).filter(Boolean)[0]?.title; + if (chartName) { + setInitialFilterQuery(chartName); + setFilterQuery(chartName); + router.replace({ pathname: '/stats' }, undefined, { scroll: false }); + } + } + // run only when data is loaded + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ isPlaceholderData ]); + + const displayedSections = React.useMemo(() => { + return data?.sections + ?.map((section) => { + const charts = section.charts.filter((chart) => isSectionMatches(section, sectionId) && isChartNameMatches(filterQuery, chart)); + + return { + ...section, + charts, + }; + }).filter((section) => section.charts.length > 0); + }, [ sectionId, data?.sections, filterQuery ]); + + return React.useMemo(() => ({ + sections: data?.sections, + displayedSections, + sectionId, + + isLoading: isPlaceholderData, + isError, + + initialFilterQuery, + filterQuery, + + interval, + + onFilterChange: setFilterQuery, + onSectionChange: setSectionId, + onIntervalChange: setInterval, + }), [ data?.sections, displayedSections, sectionId, isPlaceholderData, isError, initialFilterQuery, filterQuery, interval ]); +} diff --git a/client/features/chain-stats/hooks/useChartQuery.tsx b/client/features/chain-stats/hooks/useChartQuery.tsx new file mode 100644 index 0000000000..ca9a6c1141 --- /dev/null +++ b/client/features/chain-stats/hooks/useChartQuery.tsx @@ -0,0 +1,63 @@ +import type { ChartDataPayloadLine, StatsIntervalIds } from '../types/client'; +import { ChartResolution } from 'toolkit/components/charts/types'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { getDateLabel } from 'toolkit/components/charts/line/utils/getDateLabel'; + +import { CHAIN_STATS_LINE_CHART } from '../stubs/charts'; +import { getDatesFromInterval } from '../utils/interval'; + +interface Props { + id: string; + resolution: ChartResolution; + interval: StatsIntervalIds; + enabled?: boolean; +} + +export default function useChartQuery({ id, resolution, interval, enabled = true }: Props) { + + const { start: startDate, end: endDate } = getDatesFromInterval(interval); + const resourceName = 'stats:line'; + + return useApiQuery(resourceName, { + pathParams: { id }, + queryParams: { + from: startDate, + to: endDate, + resolution, + }, + queryOptions: { + enabled: enabled, + refetchOnMount: false, + placeholderData: (prevData) => { + return prevData ?? CHAIN_STATS_LINE_CHART; + }, + select: (data) => { + return { + type: 'line', + info: data.info ?? { + id: id, + title: 'Chart title', + description: 'Chart description', + resolutions: [ ], + }, + data: data.chart.map((item) => { + const date = new Date(item.date); + const dateTo = item.date_to ? new Date(item.date_to) : undefined; + // For resolution greater than day, we have to use the date_to field to display X labels correctly + const dateFormatted = resolution === ChartResolution.DAY ? date : dateTo ?? date; + + return { + date: dateFormatted, + date_to: dateTo, + value: Number(item.value), + isApproximate: item.is_approximate, + dateLabel: getDateLabel(date, dateTo, resolution), + }; + }), + }; + }, + }, + }); +} diff --git a/client/features/chain-stats/mocks/cross-chain-txs-paths.ts b/client/features/chain-stats/mocks/cross-chain-txs-paths.ts new file mode 100644 index 0000000000..ad446ae490 --- /dev/null +++ b/client/features/chain-stats/mocks/cross-chain-txs-paths.ts @@ -0,0 +1,23 @@ +import type { GetMessagePathsResponse } from '@blockscout/interchain-indexer-types'; + +import { chainA, chainB, chainC, chainD } from 'mocks/multichain/chains'; + +export const incomingMessagesPaths: GetMessagePathsResponse = { + items: [ + { + source_chain: chainB, + destination_chain: chainA, + messages_count: 7282, + }, + { + source_chain: chainC, + destination_chain: chainA, + messages_count: 0, + }, + { + source_chain: chainD, + destination_chain: chainA, + messages_count: 420, + }, + ], +}; diff --git a/mocks/stats/line.ts b/client/features/chain-stats/mocks/line.ts similarity index 100% rename from mocks/stats/line.ts rename to client/features/chain-stats/mocks/line.ts diff --git a/mocks/stats/lines.ts b/client/features/chain-stats/mocks/lines.ts similarity index 100% rename from mocks/stats/lines.ts rename to client/features/chain-stats/mocks/lines.ts diff --git a/client/features/chain-stats/pages/details/ChainStatsDetails.pw.tsx b/client/features/chain-stats/pages/details/ChainStatsDetails.pw.tsx new file mode 100644 index 0000000000..6032dfaa92 --- /dev/null +++ b/client/features/chain-stats/pages/details/ChainStatsDetails.pw.tsx @@ -0,0 +1,71 @@ +import React from 'react'; + +import { ChartResolution } from 'toolkit/components/charts/types'; + +import { CROSS_CHAIN_TXS_CHARTS } from 'client/features/cross-chain-txs/utils/chain-stats'; + +import * as chainsMock from 'mocks/multichain/chains'; +import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; +import { test, expect } from 'playwright/lib'; + +import * as crossChainTxsPathsMock from '../../mocks/cross-chain-txs-paths'; +import * as statsLineMock from '../../mocks/line'; +import ChainStatsDetails from './ChainStatsDetails'; + +test.beforeEach(async({ mockTextAd }) => { + await mockTextAd(); +}); + +test('base view +@dark-mode +@mobile', async({ render, mockApiResponse, page }) => { + + const CHART_ID = 'averageGasPrice'; + const hooksConfig = { + router: { + query: { id: CHART_ID, interval: 'all' }, + }, + }; + + const chartApiUrl = await mockApiResponse( + 'stats:line', + statsLineMock.averageGasPrice, + { + pathParams: { id: CHART_ID }, + queryParams: { + resolution: ChartResolution.DAY, + }, + }, + ); + + const component = await render(, { hooksConfig }); + await page.waitForResponse(chartApiUrl); + await page.waitForFunction(() => { + return document.querySelector('path[data-name="chart-fullscreen"]')?.getAttribute('opacity') === '1'; + }); + await expect(component).toHaveScreenshot(); +}); + +test('cross-chain txs paths view +@dark-mode +@mobile', async({ render, mockApiResponse, mockEnvs }) => { + const CHART = CROSS_CHAIN_TXS_CHARTS[0]; + const hooksConfig = { + router: { + query: { id: CHART.id, interval: 'all' }, + }, + }; + + await mockEnvs([ + ...ENVS_MAP.crossChainTxs, + [ 'NEXT_PUBLIC_NETWORK_NAME', chainsMock.chainA.name ], + [ 'NEXT_PUBLIC_NETWORK_ID', chainsMock.chainA.id ], + ]); + await mockApiResponse('interchainIndexer:chains', { items: [ chainsMock.chainA, chainsMock.chainB, chainsMock.chainC, chainsMock.chainD ] }); + await mockApiResponse( + CHART.resourceName!, + crossChainTxsPathsMock.incomingMessagesPaths, + { + pathParams: { chainId: chainsMock.chainA.id }, + }, + ); + + const component = await render(, { hooksConfig }); + await expect(component).toHaveScreenshot(); +}); diff --git a/client/features/chain-stats/pages/details/ChainStatsDetails.tsx b/client/features/chain-stats/pages/details/ChainStatsDetails.tsx new file mode 100644 index 0000000000..7a94bb7b84 --- /dev/null +++ b/client/features/chain-stats/pages/details/ChainStatsDetails.tsx @@ -0,0 +1,136 @@ +import { useRouter } from 'next/router'; +import React from 'react'; + +import type { StatsIntervalIds } from '../../types/client'; +import type { ChartResolution } from 'toolkit/components/charts/types'; + +import useChartQuery from 'client/features/chain-stats/hooks/useChartQuery'; +import ChainStatsDetailsCrossChainTxs from 'client/features/cross-chain-txs/components/ChainStatsDetailsCrossChainTxs'; +import useCrossChainChartQuery from 'client/features/cross-chain-txs/hooks/useCrossChainChartQuery'; +import { CROSS_CHAIN_TXS_CHARTS } from 'client/features/cross-chain-txs/utils/chain-stats'; + +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; +import * as metadata from 'client/shared/metadata'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import { useQueryParams } from 'client/shared/router/useQueryParams'; + +import config from 'configs/app'; +import type { OnValueChangeHandler } from 'toolkit/chakra/select'; +import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; +import { ALL_OPTION, isAllOption } from 'ui/shared/externalChains/ChainSelect'; +import PageTitle from 'ui/shared/Page/PageTitle'; + +import { getIntervalByResolution, getIntervalFromQuery } from '../../utils/interval'; +import { DEFAULT_RESOLUTION, getResolutionFromQuery } from '../../utils/resolution'; +import ChainStatsDetailsLineChart from './ChainStatsDetailsLineChart'; + +const ChainStatsDetails = () => { + const router = useRouter(); + const { updateQuery } = useQueryParams(); + + const id = getQueryParamString(router.query.id); + const intervalFromQuery = getIntervalFromQuery(router); + const resolutionFromQuery = getResolutionFromQuery(router); + const counterPartyChainIdsFromQuery = Array.isArray(router.query.counterparty_chain_ids) ? + router.query.counterparty_chain_ids : + [ router.query.counterparty_chain_ids ].filter(Boolean); + + const defaultResolution = resolutionFromQuery || DEFAULT_RESOLUTION; + + const [ resolution, setResolution ] = React.useState(defaultResolution); + const [ interval, setInterval ] = React.useState(intervalFromQuery ?? getIntervalByResolution(resolution)); + const [ counterPartyChainIds, setCounterPartyChainIds ] = React.useState>( + counterPartyChainIdsFromQuery.length > 0 ? counterPartyChainIdsFromQuery : [ ALL_OPTION.value ], + ); + + const crossChainTxsChart = config.features.crossChainTxs.isEnabled ? CROSS_CHAIN_TXS_CHARTS.find((chart) => chart.id === id) : undefined; + + const queryBase = useChartQuery({ id, resolution, interval, enabled: !crossChainTxsChart }); + const queryCrossChain = useCrossChainChartQuery({ id, interval, counterPartyChainIds, enabled: Boolean(crossChainTxsChart) }); + + const query = crossChainTxsChart ? queryCrossChain : queryBase; + const isInitialLoading = useIsInitialLoading(query.isPlaceholderData); + + const handleIntervalChange = React.useCallback((interval: StatsIntervalIds) => { + setInterval(interval); + updateQuery({ interval }); + }, [ updateQuery ]); + + const handleResolutionChange = React.useCallback(({ value }: { value: Array }) => { + setResolution(value[0] as ChartResolution); + updateQuery({ resolution: value[0] }); + }, [ updateQuery ]); + + const handleCounterPartyChainIdsChange: OnValueChangeHandler = React.useCallback(({ value }) => { + setCounterPartyChainIds(value); + updateQuery({ counterparty_chain_ids: isAllOption(value) ? undefined : value }); + }, [ updateQuery ]); + + React.useEffect(() => { + if (!isInitialLoading && query.data?.info && !config.meta.seo.enhancedDataEnabled) { + metadata.update({ pathname: '/stats/[id]', query: { id } }, query.data.info); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ isInitialLoading ]); + + if (query.isError) { + if (isCustomAppError(query.error)) { + throwOnResourceLoadError({ resource: 'stats:line', error: query.error, isError: true }); + } + } + + const chartInfo = query.data?.info; + + const content = (() => { + if (query.data?.type === 'line') { + return ( + + ); + } + + if (crossChainTxsChart) { + return ( + + ); + } + + return null; + })(); + + return ( + <> + + { content } + + ); +}; + +export default ChainStatsDetails; diff --git a/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx b/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx new file mode 100644 index 0000000000..4079fb5cf5 --- /dev/null +++ b/client/features/chain-stats/pages/details/ChainStatsDetailsLineChart.tsx @@ -0,0 +1,170 @@ +import { createListCollection, Flex } from '@chakra-ui/react'; +import React from 'react'; + +import type { StatsIntervalIds } from '../../types/client'; +import type { LineChartInfo } from '@blockscout/stats-types'; +import type { LineChartItem } from 'toolkit/components/charts/line/types'; +import { CHART_RESOLUTION_LABELS, type ChartResolution } from 'toolkit/components/charts/types'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; + +import { useMultichainContext } from 'lib/contexts/multichain'; +import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; +import type { OnValueChangeHandler, SelectOption } from 'toolkit/chakra/select'; +import { Select } from 'toolkit/chakra/select'; +import { Skeleton } from 'toolkit/chakra/skeleton'; +import { ChartResetZoomButton } from 'toolkit/components/charts/components/ChartResetZoomButton'; +import { LineChartContent } from 'toolkit/components/charts/line/LineChartContent'; +import LineChartMenu from 'toolkit/components/charts/line/parts/LineChartMenu'; +import { useLineChartZoom } from 'toolkit/components/charts/line/utils/useLineChartZoom'; +import ChainSelect from 'ui/multichain/components/ChainSelect'; +import { useChartsConfig } from 'ui/shared/chart/config'; + +import ChartIntervalSelect from '../../components/ChartIntervalSelect'; +import { DEFAULT_RESOLUTION } from '../../utils/resolution'; + +interface Props { + id: string; + info?: LineChartInfo; + data?: Array; + isLoading: boolean; + isError: boolean; + isInitialLoading: boolean; + interval: StatsIntervalIds; + resolution: ChartResolution; + onIntervalChange: (interval: StatsIntervalIds) => void; + onResolutionChange: OnValueChangeHandler; +} + +const ChainStatsDetailsLineChart = ({ + id, + info, + data, + isLoading, + isError, + isInitialLoading, + interval, + resolution, + onIntervalChange, + onResolutionChange, +}: Props) => { + + const ref = React.useRef(null); + + const chartsConfig = useChartsConfig(); + const chainSelect = useRoutedChainSelect(); + const multichainContext = useMultichainContext(); + + const { zoomRange, handleZoom, handleZoomReset: onZoomReset } = useLineChartZoom(); + + const handleZoomReset = React.useCallback(() => { + onZoomReset(); + onResolutionChange({ value: [ DEFAULT_RESOLUTION ] }); + }, [ onZoomReset, onResolutionChange ]); + + const handleShare = React.useCallback(async() => { + mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Share chart', Info: id }); + }, [ id ]); + + const resolutionCollection = React.useMemo(() => { + const resolutions = info?.resolutions || []; + const items = CHART_RESOLUTION_LABELS + .filter((resolution) => resolutions.includes(resolution.id)) + .map((resolution) => ({ value: resolution.id, label: resolution.title })); + + return createListCollection({ items }); + }, [ info?.resolutions ]); + + const charts = React.useMemo(() => { + if (!info || !data) { + return []; + } + + return [ + { + id: info.id, + name: 'Value', + items: data, + charts: chartsConfig, + units: info.units, + }, + ]; + }, [ chartsConfig, data, info ]); + + const hasNonEmptyCharts = charts.some((chart) => chart.items.length > 2); + const hasItems = (data && data.length > 2) || isLoading || isInitialLoading; + + return ( + <> + + + { multichainContext?.chain && ( + + ) } + + { (info?.resolutions && info?.resolutions.length > 1) && ( + + + Res + + - + { + useEtherscanRedirects(); + + const { + isLoading, + isError, + sections, + displayedSections, + sectionId, + initialFilterQuery, + interval, + onSectionChange, + onIntervalChange, + onFilterChange, + } = useChainStats(); + + return ( + <> + + + + + + + + + + + + + ); +}; + +export default React.memo(ChainStatsIndex); diff --git a/client/features/chain-stats/pages/index/ChainStatsSections.tsx b/client/features/chain-stats/pages/index/ChainStatsSections.tsx new file mode 100644 index 0000000000..dd75966669 --- /dev/null +++ b/client/features/chain-stats/pages/index/ChainStatsSections.tsx @@ -0,0 +1,146 @@ +import { Box, Grid } from '@chakra-ui/react'; +import React from 'react'; + +import type { ChainStatsSection, StatsIntervalIds } from '../../types/client'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import ChartWidgetContainerCrossChain from 'client/features/cross-chain-txs/components/ChartWidgetContainerCrossChain'; +import { CROSS_CHAIN_TXS_CHARTS } from 'client/features/cross-chain-txs/utils/chain-stats'; + +import config from 'configs/app'; +import { useMultichainContext } from 'lib/contexts/multichain'; +import { EmptyState } from 'toolkit/chakra/empty-state'; +import { Heading } from 'toolkit/chakra/heading'; +import { Skeleton } from 'toolkit/chakra/skeleton'; +import GasInfoTooltip from 'ui/shared/gas/GasInfoTooltip'; +import IconSvg from 'ui/shared/IconSvg'; + +import ChartWidgetContainer from '../../components/ChartWidgetContainer'; +import ChainStatsErrorAlert from './ChainStatsErrorAlert'; + +interface Props { + sections?: Array; + displayedSections?: Array; + sectionId: string; + isError: boolean; + isLoading: boolean; + initialFilterQuery: string; + interval: StatsIntervalIds; +}; + +const ChainStatsSections = ({ isError, isLoading, displayedSections, interval, initialFilterQuery, sections, sectionId }: Props) => { + const [ isSomeChartLoadingError, setIsSomeChartLoadingError ] = React.useState(false); + + const hasCharts = sections?.some((section) => section.charts.length > 0); + const hasDisplayedCharts = displayedSections?.some((section) => section.charts.length > 0); + const sectionRef = React.useRef(null); + + const shouldScrollToSection = Boolean(initialFilterQuery); + + React.useEffect(() => { + if (shouldScrollToSection) { + sectionRef.current?.scrollIntoView({ behavior: 'smooth' }); + } + }, [ shouldScrollToSection ]); + + const { chain } = useMultichainContext() || {}; + const isGasTrackerEnabled = config.features.gasTracker.isEnabled; + + const homeStatsQuery = useApiQuery('general:stats', { + queryOptions: { + refetchOnMount: false, + enabled: isGasTrackerEnabled, + }, + }); + + const handleChartLoadingError = React.useCallback( + () => setIsSomeChartLoadingError(true), + [ setIsSomeChartLoadingError ]); + + if (isError) { + return ; + } + + if (!hasDisplayedCharts) { + const selectedSection = sections?.find((section) => section.id === sectionId); + return ( + + ); + } + + return ( + + { isSomeChartLoadingError && ( + + ) } + +
+ { + displayedSections?.map((section) => ( + + + + { section.title } + + { isGasTrackerEnabled && section.id === 'gas' && homeStatsQuery.data && homeStatsQuery.data.gas_prices && ( + + + + ) } + + + + { section.charts.map((chart) => { + + const crossChainTxsChart = CROSS_CHAIN_TXS_CHARTS.find(({ id }) => id === chart.id); + if (crossChainTxsChart) { + return ( + + ); + } + + return ( + + ); + }) } + + + )) + } +
+
+ ); +}; + +export default ChainStatsSections; diff --git a/client/features/chain-stats/stubs/charts.ts b/client/features/chain-stats/stubs/charts.ts new file mode 100644 index 0000000000..a2f657409f --- /dev/null +++ b/client/features/chain-stats/stubs/charts.ts @@ -0,0 +1,60 @@ +import type { ChainStatsChart, ChainStatsSection } from '../types/client'; +import type { LineChart } from '@blockscout/stats-types'; + +export const CHAIN_STATS_CHART_INFO: ChainStatsChart = { + id: 'chart_0', + title: 'Average transaction fee', + description: 'The average amount in ETH spent per transaction', + units: 'ETH', + resolutions: [ 'DAY', 'MONTH' ], +}; + +export const CHAIN_STATS_CHARTS_SECTION: ChainStatsSection = { + id: 'placeholder', + title: 'Placeholder', + charts: [ + CHAIN_STATS_CHART_INFO, + { + id: 'chart_1', + title: 'Transactions fees', + description: 'Amount of tokens paid as fees', + units: 'ETH', + resolutions: [ 'DAY', 'MONTH' ], + }, + { + id: 'chart_2', + title: 'New transactions', + description: 'New transactions number', + units: undefined, + resolutions: [ 'DAY', 'MONTH' ], + }, + { + id: 'chart_3', + title: 'Transactions growth', + description: 'Cumulative transactions number', + units: undefined, + resolutions: [ 'DAY', 'MONTH' ], + }, + ], +}; + +export const CHAIN_STATS_CHARTS_SECTION_GAS: ChainStatsSection = { + id: 'gas', + title: 'Gas', + charts: [ { + id: 'averageGasPrice', + title: 'Average gas price', + description: 'Average gas price', + units: 'ETH', + resolutions: [ 'DAY', 'MONTH' ], + } ], +}; + +export const CHAIN_STATS_CHARTS = { + sections: [ CHAIN_STATS_CHARTS_SECTION ], +}; + +export const CHAIN_STATS_LINE_CHART: LineChart = { + info: CHAIN_STATS_CHART_INFO, + chart: [], +}; diff --git a/client/features/chain-stats/stubs/counters.ts b/client/features/chain-stats/stubs/counters.ts new file mode 100644 index 0000000000..a37e30b945 --- /dev/null +++ b/client/features/chain-stats/stubs/counters.ts @@ -0,0 +1,9 @@ +import type * as stats from '@blockscout/stats-types'; + +export const CHAIN_STATS_COUNTER: stats.Counter = { + id: 'stub', + value: '9074405', + title: 'Placeholder Counter', + description: 'Placeholder description', + units: '', +}; diff --git a/client/features/chain-stats/types/client.ts b/client/features/chain-stats/types/client.ts new file mode 100644 index 0000000000..7f60617709 --- /dev/null +++ b/client/features/chain-stats/types/client.ts @@ -0,0 +1,44 @@ +import type { LineChartInfo, LineChartSection } from '@blockscout/stats-types'; +import type { LineChartItem } from 'toolkit/components/charts/line/types'; +import type { SankeyChartData } from 'toolkit/components/charts/sankey/types'; + +export type ChartType = 'line' | 'sankey'; + +export interface ChainStatsChart extends LineChartInfo { + resourceName?: 'interchainIndexer:stats_chain_messages_sent' | 'interchainIndexer:stats_chain_messages_received'; + type?: ChartType; +} + +export interface ChainStatsSection extends Omit { + charts: Array; +} + +export interface ChainStatsPayload { + sections: Array; +} + +export type ChartData = ChartDataPayloadLine | ChartDataPayloadSankey; + +export interface ChartDataPayloadLine { + type: 'line'; + info: LineChartInfo; + data: Array; +} + +export interface ChartDataPayloadSankey { + type: 'sankey'; + info: LineChartInfo; + data: SankeyChartData; +} + +export type StatsInterval = { id: StatsIntervalIds; title: string }; + +export type StatsIntervalIds = keyof typeof StatsIntervalId; + +export enum StatsIntervalId { + all = 'all', + oneMonth = 'oneMonth', + threeMonths = 'threeMonths', + sixMonths = 'sixMonths', + oneYear = 'oneYear', +} diff --git a/client/features/chain-stats/utils/chart.ts b/client/features/chain-stats/utils/chart.ts new file mode 100644 index 0000000000..8a53c8bddd --- /dev/null +++ b/client/features/chain-stats/utils/chart.ts @@ -0,0 +1,8 @@ +import type { Route } from 'nextjs-routes'; +import { route } from 'nextjs-routes'; + +import config from 'configs/app'; + +export function getChartUrl(href?: Route) { + return href ? `${ config.app.baseUrl }${ route(href) }` : undefined; +} diff --git a/client/features/chain-stats/utils/interval.ts b/client/features/chain-stats/utils/interval.ts new file mode 100644 index 0000000000..a76a7a4e9d --- /dev/null +++ b/client/features/chain-stats/utils/interval.ts @@ -0,0 +1,82 @@ +import type { NextRouter } from 'next/router'; + +import { StatsIntervalId, type StatsIntervalIds } from '../types/client'; +import { ChartResolution } from 'toolkit/components/charts/types'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + +import { formatDate } from 'ui/shared/chart/utils'; + +export const STATS_INTERVALS: { [key in StatsIntervalIds]: { title: string; shortTitle: string; start?: Date } } = { + all: { + title: 'All time', + shortTitle: 'All time', + }, + oneMonth: { + title: '1 month', + shortTitle: '1M', + start: getStartDateInPast(1), + }, + threeMonths: { + title: '3 months', + shortTitle: '3M', + start: getStartDateInPast(3), + }, + sixMonths: { + title: '6 months', + shortTitle: '6M', + start: getStartDateInPast(6), + }, + oneYear: { + title: '1 year', + shortTitle: '1Y', + start: getStartDateInPast(12), + }, +}; + +function getStartDateInPast(months: number): Date { + const date = new Date(); + date.setMonth(date.getMonth() - months); + return date; +} + +export function getDatesFromInterval(interval: StatsIntervalIds): { start?: string; end?: string } { + const selectedInterval = STATS_INTERVALS[interval]; + + if (!selectedInterval.start) { + return { + start: undefined, + end: undefined, + }; + } + + return { + start: formatDate(selectedInterval.start), + end: formatDate(new Date()), + }; +} + +export function getIntervalFromQuery(router: NextRouter): StatsIntervalIds | undefined { + const intervalFromQuery = getQueryParamString(router.query.interval); + + if (!intervalFromQuery || !Object.values(StatsIntervalId).includes(intervalFromQuery as StatsIntervalIds)) { + return undefined; + } + + return intervalFromQuery as StatsIntervalIds; +}; + +export function getIntervalByResolution(resolution: ChartResolution): StatsIntervalIds { + switch (resolution) { + case ChartResolution.DAY: + return 'oneMonth'; + case ChartResolution.WEEK: + return 'oneMonth'; + case ChartResolution.MONTH: + return 'oneYear'; + case ChartResolution.YEAR: + return 'all'; + default: + return 'oneMonth'; + } +}; diff --git a/client/features/chain-stats/utils/resolution.ts b/client/features/chain-stats/utils/resolution.ts new file mode 100644 index 0000000000..0419a274e7 --- /dev/null +++ b/client/features/chain-stats/utils/resolution.ts @@ -0,0 +1,17 @@ +import type { NextRouter } from 'next/router'; + +import { ChartResolution } from 'toolkit/components/charts/types'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + +export const DEFAULT_RESOLUTION = ChartResolution.DAY; + +export function getResolutionFromQuery(router: NextRouter): ChartResolution { + const resolutionFromQuery = getQueryParamString(router.query.resolution) as (keyof typeof ChartResolution | undefined); + + if (!resolutionFromQuery || !ChartResolution[resolutionFromQuery]) { + return DEFAULT_RESOLUTION; + } + + return resolutionFromQuery as ChartResolution; +}; diff --git a/client/features/chain-variants/beacon-chain/mocks/block.ts b/client/features/chain-variants/beacon-chain/mocks/block.ts new file mode 100644 index 0000000000..4ef93fb42e --- /dev/null +++ b/client/features/chain-variants/beacon-chain/mocks/block.ts @@ -0,0 +1,8 @@ +import type { Block } from 'client/slices/block/types/api'; + +import { base } from 'client/slices/block/mocks/block'; + +export const withWithdrawals: Block = { + ...base, + withdrawals_count: 2, +}; diff --git a/ui/address/AddressDeposits.tsx b/client/features/chain-variants/beacon-chain/pages/address/AddressDeposits.tsx similarity index 94% rename from ui/address/AddressDeposits.tsx rename to client/features/chain-variants/beacon-chain/pages/address/AddressDeposits.tsx index c2eb88f243..2ce2bb4458 100644 --- a/ui/address/AddressDeposits.tsx +++ b/client/features/chain-variants/beacon-chain/pages/address/AddressDeposits.tsx @@ -2,8 +2,9 @@ import { Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { DEPOSIT } from 'stubs/deposits'; import { generateListStub } from 'stubs/utils'; import BeaconChainDepositsListItem from 'ui/deposits/beaconChain/BeaconChainDepositsListItem'; diff --git a/ui/block/BlockDeposits.tsx b/client/features/chain-variants/beacon-chain/pages/block/BlockDeposits.tsx similarity index 100% rename from ui/block/BlockDeposits.tsx rename to client/features/chain-variants/beacon-chain/pages/block/BlockDeposits.tsx diff --git a/ui/block/BlockWithdrawals.tsx b/client/features/chain-variants/beacon-chain/pages/block/BlockWithdrawals.tsx similarity index 100% rename from ui/block/BlockWithdrawals.tsx rename to client/features/chain-variants/beacon-chain/pages/block/BlockWithdrawals.tsx diff --git a/ui/block/useBlockDepositsQuery.tsx b/client/features/chain-variants/beacon-chain/pages/block/useBlockDepositsQuery.ts similarity index 94% rename from ui/block/useBlockDepositsQuery.tsx rename to client/features/chain-variants/beacon-chain/pages/block/useBlockDepositsQuery.ts index b3e7332ae5..fae3135743 100644 --- a/ui/block/useBlockDepositsQuery.tsx +++ b/client/features/chain-variants/beacon-chain/pages/block/useBlockDepositsQuery.ts @@ -1,11 +1,11 @@ +import type { BlockQuery } from 'client/slices/block/hooks/useBlockQuery'; + import config from 'configs/app'; import { DEPOSIT } from 'stubs/deposits'; import { generateListStub } from 'stubs/utils'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import type { BlockQuery } from './useBlockQuery'; - export type BlockDepositsQuery = QueryWithPagesResult<'general:block_deposits'> & { isDegradedData: boolean; }; diff --git a/ui/block/useBlockWithdrawalsQuery.tsx b/client/features/chain-variants/beacon-chain/pages/block/useBlockWithdrawalsQuery.ts similarity index 89% rename from ui/block/useBlockWithdrawalsQuery.tsx rename to client/features/chain-variants/beacon-chain/pages/block/useBlockWithdrawalsQuery.ts index 89336a96f6..0db423c3ce 100644 --- a/ui/block/useBlockWithdrawalsQuery.tsx +++ b/client/features/chain-variants/beacon-chain/pages/block/useBlockWithdrawalsQuery.ts @@ -3,24 +3,27 @@ import { useQuery } from '@tanstack/react-query'; import React from 'react'; import type { Chain, GetBlockReturnType } from 'viem'; -import type { BlockWithdrawalsResponse } from 'types/api/block'; +import type { BlockWithdrawalsResponse } from 'client/features/chain-variants/beacon-chain/types/api'; + +import { retry } from 'client/api/hooks/useQueryClientConfig'; +import type { ResourceError } from 'client/api/resources'; + +import { unknownAddress } from 'client/slices/address/utils/consts'; +import type { BlockQuery } from 'client/slices/block/hooks/useBlockQuery'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + +import hexToDecimal from 'client/shared/transformers/hex-to-decimal'; import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import { retry } from 'lib/api/useQueryClientConfig'; -import hexToDecimal from 'lib/hexToDecimal'; -import { publicClient } from 'lib/web3/client'; import { GET_BLOCK } from 'stubs/RPC'; import { generateListStub } from 'stubs/utils'; import { WITHDRAWAL } from 'stubs/withdrawals'; import { SECOND } from 'toolkit/utils/consts'; -import { unknownAddress } from 'ui/shared/address/utils'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import { emptyPagination } from 'ui/shared/pagination/utils'; -import type { BlockQuery } from './useBlockQuery'; - type RpcResponseType = GetBlockReturnType | null; export type BlockWithdrawalsQuery = QueryWithPagesResult<'general:block_withdrawals'> & { diff --git a/client/features/chain-variants/beacon-chain/types/api.ts b/client/features/chain-variants/beacon-chain/types/api.ts new file mode 100644 index 0000000000..3537bf7970 --- /dev/null +++ b/client/features/chain-variants/beacon-chain/types/api.ts @@ -0,0 +1,16 @@ +import type { AddressParam } from 'client/slices/address/types/api'; + +export type BlockWithdrawalsResponse = { + items: Array; + next_page_params: { + index: number; + items_count: number; + } | null; +}; + +export type BlockWithdrawalsItem = { + amount: string; + index: number; + receiver: AddressParam; + validator_index: number; +}; diff --git a/client/features/chain-variants/celo/mocks/block.ts b/client/features/chain-variants/celo/mocks/block.ts new file mode 100644 index 0000000000..443e9cb06e --- /dev/null +++ b/client/features/chain-variants/celo/mocks/block.ts @@ -0,0 +1,35 @@ +import type { Block } from 'client/slices/block/types/api'; + +import { base } from 'client/slices/block/mocks/block'; + +import * as addressMock from 'mocks/address/address'; +import * as tokenMock from 'mocks/tokens/tokenInfo'; +import { ZERO_ADDRESS } from 'toolkit/utils/consts'; + +export const celo: Block = { + ...base, + celo: { + base_fee: { + token: tokenMock.tokenInfoERC20a, + amount: '445690000000000', + breakdown: [ + { + address: addressMock.withName, + amount: '356552000000000.0000000000000', + percentage: 80, + }, + { + address: { + ...addressMock.withoutName, + hash: ZERO_ADDRESS, + }, + amount: '89138000000000.0000000000000', + percentage: 20, + }, + ], + recipient: addressMock.contract, + }, + epoch_number: 1486, + l1_era_finalized_epoch_number: 1485, + }, +}; diff --git a/client/features/chain-variants/celo/mocks/tx.ts b/client/features/chain-variants/celo/mocks/tx.ts new file mode 100644 index 0000000000..b5b2818a21 --- /dev/null +++ b/client/features/chain-variants/celo/mocks/tx.ts @@ -0,0 +1,22 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +import { base } from 'client/slices/tx/mocks/tx'; + +export const celoTxn: Transaction = { + ...base, + celo: { + gas_token: { + address_hash: '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1', + circulating_market_cap: null, + decimals: '18', + exchange_rate: '0.42', + holders_count: '205738', + icon_url: 'https://example.com/icon.png', + name: 'Celo Dollar', + symbol: 'cUSD', + total_supply: '7145754483836626799435133', + type: 'ERC-20', + reputation: 'ok', + }, + }, +}; diff --git a/ui/address/details/AddressCeloAccount.pw.tsx b/client/features/chain-variants/celo/pages/address/AddressCeloAccount.pw.tsx similarity index 100% rename from ui/address/details/AddressCeloAccount.pw.tsx rename to client/features/chain-variants/celo/pages/address/AddressCeloAccount.pw.tsx diff --git a/ui/address/details/AddressCeloAccount.tsx b/client/features/chain-variants/celo/pages/address/AddressCeloAccount.tsx similarity index 94% rename from ui/address/details/AddressCeloAccount.tsx rename to client/features/chain-variants/celo/pages/address/AddressCeloAccount.tsx index 90a3ccae43..2260be1b32 100644 --- a/ui/address/details/AddressCeloAccount.tsx +++ b/client/features/chain-variants/celo/pages/address/AddressCeloAccount.tsx @@ -1,15 +1,17 @@ import { upperFirst } from 'es-toolkit'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import type { ExcludeNull, ExcludeUndefined } from 'types/utils'; -import { currencyUnits } from 'lib/units'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { Link } from 'toolkit/chakra/link'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import * as DetailedInfoItemBreakdown from 'ui/shared/DetailedInfo/DetailedInfoItemBreakdown'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; interface Props { diff --git a/ui/address/AddressEpochRewards.pw.tsx b/client/features/chain-variants/celo/pages/address/AddressEpochRewards.pw.tsx similarity index 100% rename from ui/address/AddressEpochRewards.pw.tsx rename to client/features/chain-variants/celo/pages/address/AddressEpochRewards.pw.tsx diff --git a/ui/address/AddressEpochRewards.tsx b/client/features/chain-variants/celo/pages/address/AddressEpochRewards.tsx similarity index 78% rename from ui/address/AddressEpochRewards.tsx rename to client/features/chain-variants/celo/pages/address/AddressEpochRewards.tsx index d970750e76..24e6d502bb 100644 --- a/ui/address/AddressEpochRewards.tsx +++ b/client/features/chain-variants/celo/pages/address/AddressEpochRewards.tsx @@ -2,18 +2,20 @@ import { Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { EPOCH_REWARD_ITEM } from 'stubs/address'; +import { EPOCH_REWARD_ITEM } from 'client/features/chain-variants/celo/stubs/address'; +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { generateListStub } from 'stubs/utils'; -import AddressEpochRewardsTable from 'ui/address/epochRewards/AddressEpochRewardsTable'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import AddressCsvExportLink from './AddressCsvExportLink'; -import AddressEpochRewardsListItem from './epochRewards/AddressEpochRewardsListItem'; +import AddressEpochRewardsListItem from './AddressEpochRewardsListItem'; +import AddressEpochRewardsTable from './AddressEpochRewardsTable'; type Props = { shouldRender?: boolean; @@ -70,15 +72,15 @@ const AddressEpochRewards = ({ shouldRender = true, isQueryEnabled = true }: Pro const actionBar = ( - { rewardsQuery.pagination.isVisible && ( ) } diff --git a/ui/address/epochRewards/AddressEpochRewardsListItem.tsx b/client/features/chain-variants/celo/pages/address/AddressEpochRewardsListItem.tsx similarity index 92% rename from ui/address/epochRewards/AddressEpochRewardsListItem.tsx rename to client/features/chain-variants/celo/pages/address/AddressEpochRewardsListItem.tsx index 5d5c2457c1..a689a4fb6c 100644 --- a/ui/address/epochRewards/AddressEpochRewardsListItem.tsx +++ b/client/features/chain-variants/celo/pages/address/AddressEpochRewardsListItem.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import type { AddressEpochRewardsItem } from 'types/api/address'; +import type { AddressEpochRewardsItem } from 'client/features/chain-variants/celo/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import EpochEntity from 'ui/shared/entities/epoch/EpochEntity'; import EpochRewardTypeTag from 'ui/shared/EpochRewardTypeTag'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; diff --git a/ui/address/epochRewards/AddressEpochRewardsTable.tsx b/client/features/chain-variants/celo/pages/address/AddressEpochRewardsTable.tsx similarity index 88% rename from ui/address/epochRewards/AddressEpochRewardsTable.tsx rename to client/features/chain-variants/celo/pages/address/AddressEpochRewardsTable.tsx index 1b8774a79c..c66459afce 100644 --- a/ui/address/epochRewards/AddressEpochRewardsTable.tsx +++ b/client/features/chain-variants/celo/pages/address/AddressEpochRewardsTable.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import type { AddressEpochRewardsItem } from 'types/api/address'; +import type { AddressEpochRewardsItem } from 'client/features/chain-variants/celo/types/api'; + +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/address/epochRewards/AddressEpochRewardsTableItem.tsx b/client/features/chain-variants/celo/pages/address/AddressEpochRewardsTableItem.tsx similarity index 90% rename from ui/address/epochRewards/AddressEpochRewardsTableItem.tsx rename to client/features/chain-variants/celo/pages/address/AddressEpochRewardsTableItem.tsx index dd688aa738..01941b28f1 100644 --- a/ui/address/epochRewards/AddressEpochRewardsTableItem.tsx +++ b/client/features/chain-variants/celo/pages/address/AddressEpochRewardsTableItem.tsx @@ -1,13 +1,14 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { AddressEpochRewardsItem } from 'types/api/address'; +import type { AddressEpochRewardsItem } from 'client/features/chain-variants/celo/types/api'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Link } from 'toolkit/chakra/link'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import EpochRewardTypeTag from 'ui/shared/EpochRewardTypeTag'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import TokenValue from 'ui/shared/value/TokenValue'; diff --git a/ui/address/details/__screenshots__/AddressCeloAccount.pw.tsx_default_default-view-mobile-1.png b/client/features/chain-variants/celo/pages/address/__screenshots__/AddressCeloAccount.pw.tsx_default_default-view-mobile-1.png similarity index 100% rename from ui/address/details/__screenshots__/AddressCeloAccount.pw.tsx_default_default-view-mobile-1.png rename to client/features/chain-variants/celo/pages/address/__screenshots__/AddressCeloAccount.pw.tsx_default_default-view-mobile-1.png diff --git a/ui/address/details/__screenshots__/AddressCeloAccount.pw.tsx_mobile_default-view-mobile-1.png b/client/features/chain-variants/celo/pages/address/__screenshots__/AddressCeloAccount.pw.tsx_mobile_default-view-mobile-1.png similarity index 100% rename from ui/address/details/__screenshots__/AddressCeloAccount.pw.tsx_mobile_default-view-mobile-1.png rename to client/features/chain-variants/celo/pages/address/__screenshots__/AddressCeloAccount.pw.tsx_mobile_default-view-mobile-1.png diff --git a/client/features/chain-variants/celo/pages/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png b/client/features/chain-variants/celo/pages/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png new file mode 100644 index 0000000000..962ec432ea Binary files /dev/null and b/client/features/chain-variants/celo/pages/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png differ diff --git a/client/features/chain-variants/celo/pages/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/celo/pages/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png new file mode 100644 index 0000000000..5b562da54f Binary files /dev/null and b/client/features/chain-variants/celo/pages/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/block/BlockCeloEpochTag.tsx b/client/features/chain-variants/celo/pages/block/BlockCeloEpochTag.tsx similarity index 95% rename from ui/block/BlockCeloEpochTag.tsx rename to client/features/chain-variants/celo/pages/block/BlockCeloEpochTag.tsx index 16f7573f43..ba58b0b8fe 100644 --- a/ui/block/BlockCeloEpochTag.tsx +++ b/client/features/chain-variants/celo/pages/block/BlockCeloEpochTag.tsx @@ -3,12 +3,12 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import type { BlockQuery } from 'client/slices/block/hooks/useBlockQuery'; + import { Link } from 'toolkit/chakra/link'; import { Tag } from 'toolkit/chakra/tag'; import { Tooltip } from 'toolkit/chakra/tooltip'; -import type { BlockQuery } from './useBlockQuery'; - interface Props { blockQuery: BlockQuery; } diff --git a/ui/block/details/BlockDetailsBaseFeeCelo.pw.tsx b/client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo.pw.tsx similarity index 80% rename from ui/block/details/BlockDetailsBaseFeeCelo.pw.tsx rename to client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo.pw.tsx index 699f88b544..328dc6f321 100644 --- a/ui/block/details/BlockDetailsBaseFeeCelo.pw.tsx +++ b/client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo.pw.tsx @@ -1,9 +1,10 @@ import { Grid } from '@chakra-ui/react'; import React from 'react'; -import type { BlockBaseFeeCelo } from 'types/api/block'; +import type { BlockBaseFeeCelo } from 'client/features/chain-variants/celo/types/api'; + +import * as blockMock from 'client/slices/block/mocks/block'; -import * as blockMock from 'mocks/blocks/block'; import { test, expect } from 'playwright/lib'; import BlockDetailsBaseFeeCelo from './BlockDetailsBaseFeeCelo'; diff --git a/ui/block/details/BlockDetailsBaseFeeCelo.tsx b/client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo.tsx similarity index 89% rename from ui/block/details/BlockDetailsBaseFeeCelo.tsx rename to client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo.tsx index 9876892d51..ce24e09ae9 100644 --- a/ui/block/details/BlockDetailsBaseFeeCelo.tsx +++ b/client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo.tsx @@ -1,15 +1,16 @@ import { Box, Flex } from '@chakra-ui/react'; import React from 'react'; -import type { AddressParam } from 'types/api/addressParams'; -import type { BlockBaseFeeCelo } from 'types/api/block'; +import type { BlockBaseFeeCelo } from 'client/features/chain-variants/celo/types/api'; +import type { AddressParam } from 'client/slices/address/types/api'; import type { TokenInfo } from 'types/api/token'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; + import { Link } from 'toolkit/chakra/link'; import { ZERO_ADDRESS } from 'toolkit/utils/consts'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import IconSvg from 'ui/shared/IconSvg'; import TokenValue from 'ui/shared/value/TokenValue'; diff --git a/ui/block/details/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_default_base-view-mobile-1.png b/client/features/chain-variants/celo/pages/block/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/block/details/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_default_base-view-mobile-1.png rename to client/features/chain-variants/celo/pages/block/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/block/details/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/celo/pages/block/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/block/details/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/chain-variants/celo/pages/block/__screenshots__/BlockDetailsBaseFeeCelo.pw.tsx_mobile_base-view-mobile-1.png diff --git a/client/features/chain-variants/celo/stubs/address.ts b/client/features/chain-variants/celo/stubs/address.ts new file mode 100644 index 0000000000..38a570c7bf --- /dev/null +++ b/client/features/chain-variants/celo/stubs/address.ts @@ -0,0 +1,15 @@ +import type { AddressEpochRewardsItem } from 'client/features/chain-variants/celo/types/api'; + +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; + +import { TOKEN_INFO_ERC_20 } from 'stubs/token'; + +export const EPOCH_REWARD_ITEM: AddressEpochRewardsItem = { + amount: '136609473658452408568', + block_timestamp: '2022-05-15T13:16:24Z', + type: 'voter', + token: TOKEN_INFO_ERC_20, + account: ADDRESS_PARAMS, + epoch_number: 1234, + associated_account: ADDRESS_PARAMS, +}; diff --git a/client/features/chain-variants/celo/types/api.ts b/client/features/chain-variants/celo/types/api.ts new file mode 100644 index 0000000000..7c03ca7361 --- /dev/null +++ b/client/features/chain-variants/celo/types/api.ts @@ -0,0 +1,45 @@ +import type { AddressParam } from 'client/slices/address/types/api'; +import type { CeloEpochRewardsType } from 'types/api/epochs'; +import type { TokenInfo } from 'types/api/token'; + +export interface TransactionCelo { + celo?: { + gas_token: TokenInfo | null; + }; +} + +export interface BlockBaseFeeCelo { + amount: string; + breakdown: Array<{ amount: string; percentage: number; address: AddressParam }>; + recipient: AddressParam; + token: TokenInfo; +} + +export interface BlockCelo { + celo?: { + epoch_number: number; + l1_era_finalized_epoch_number: number | null; + base_fee?: BlockBaseFeeCelo; + }; +} + +export type AddressEpochRewardsResponse = { + items: Array; + next_page_params: { + amount: string; + associated_account_address_hash: string; + epoch_number: number; + items_count: number; + type: CeloEpochRewardsType; + } | null; +}; + +export type AddressEpochRewardsItem = { + type: CeloEpochRewardsType; + token: TokenInfo; + amount: string; + block_timestamp: string; + account: AddressParam; + epoch_number: number; + associated_account: AddressParam; +}; diff --git a/ui/address/filecoin/FilecoinActorTag.tsx b/client/features/chain-variants/filecoin/pages/address/FilecoinActorTag.tsx similarity index 90% rename from ui/address/filecoin/FilecoinActorTag.tsx rename to client/features/chain-variants/filecoin/pages/address/FilecoinActorTag.tsx index 3e94ae07ca..421b0ce704 100644 --- a/ui/address/filecoin/FilecoinActorTag.tsx +++ b/client/features/chain-variants/filecoin/pages/address/FilecoinActorTag.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { FilecoinActorType } from 'types/api/addressParams'; +import type { FilecoinActorType } from 'client/features/chain-variants/filecoin/types/api'; import { Badge } from 'toolkit/chakra/badge'; diff --git a/client/features/chain-variants/filecoin/types/api.ts b/client/features/chain-variants/filecoin/types/api.ts new file mode 100644 index 0000000000..19ca9ab78f --- /dev/null +++ b/client/features/chain-variants/filecoin/types/api.ts @@ -0,0 +1,23 @@ +export type AddressFilecoinParams = { + actor_type?: FilecoinActorType; + id?: string | null; + robust?: string | null; +}; + +export type FilecoinActorType = + 'account' | + 'cron' | + 'datacap' | + 'eam' | + 'ethaccount' | + 'evm' | + 'init' | + 'market' | + 'miner' | + 'multisig' | + 'paych' | + 'placeholder' | + 'power' | + 'reward' | + 'system' | + 'verifreg'; diff --git a/ui/address/AddressMud.tsx b/client/features/chain-variants/mud/pages/address/AddressMud.tsx similarity index 78% rename from ui/address/AddressMud.tsx rename to client/features/chain-variants/mud/pages/address/AddressMud.tsx index 9c4df0ef7f..66910e2dd8 100644 --- a/ui/address/AddressMud.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMud.tsx @@ -1,11 +1,11 @@ import { useRouter } from 'next/router'; import React from 'react'; -import useIsMounted from 'lib/hooks/useIsMounted'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; -import AddressMudRecord from './mud/AddressMudRecord'; -import AddressMudTable from './mud/AddressMudTable'; -import AddressMudTables from './mud/AddressMudTables'; +import AddressMudRecord from './AddressMudRecord'; +import AddressMudTable from './AddressMudTable'; +import AddressMudTables from './AddressMudTables'; type Props = { shouldRender?: boolean; diff --git a/ui/address/mud/AddressMudBreadcrumbs.tsx b/client/features/chain-variants/mud/pages/address/AddressMudBreadcrumbs.tsx similarity index 95% rename from ui/address/mud/AddressMudBreadcrumbs.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudBreadcrumbs.tsx index f39b3ce3ff..90183a695e 100644 --- a/ui/address/mud/AddressMudBreadcrumbs.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudBreadcrumbs.tsx @@ -3,14 +3,15 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useAddressQuery from 'client/slices/address/hooks/useAddressQuery'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { Link } from 'toolkit/chakra/link'; import { isBrowser } from 'toolkit/utils/isBrowser'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import IconSvg from 'ui/shared/IconSvg'; -import useAddressQuery from '../utils/useAddressQuery'; - type TableViewProps = { className?: string; hash: string; diff --git a/ui/address/mud/AddressMudRecord.pw.tsx b/client/features/chain-variants/mud/pages/address/AddressMudRecord.pw.tsx similarity index 100% rename from ui/address/mud/AddressMudRecord.pw.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudRecord.pw.tsx diff --git a/ui/address/mud/AddressMudRecord.tsx b/client/features/chain-variants/mud/pages/address/AddressMudRecord.tsx similarity index 96% rename from ui/address/mud/AddressMudRecord.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudRecord.tsx index 465b2bd1da..a32bbff10b 100644 --- a/ui/address/mud/AddressMudRecord.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudRecord.tsx @@ -2,8 +2,10 @@ import { Box, Flex, Separator, Text, VStack } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { TableRoot, TableRow, TableCell } from 'toolkit/chakra/table'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; diff --git a/ui/address/mud/AddressMudRecordValues.tsx b/client/features/chain-variants/mud/pages/address/AddressMudRecordValues.tsx similarity index 94% rename from ui/address/mud/AddressMudRecordValues.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudRecordValues.tsx index b342358696..85d4cc98df 100644 --- a/ui/address/mud/AddressMudRecordValues.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudRecordValues.tsx @@ -1,7 +1,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { AddressMudRecord } from 'types/api/address'; +import type { AddressMudRecord } from 'client/features/chain-variants/mud/types/api'; import { TableCell, TableRow } from 'toolkit/chakra/table'; diff --git a/ui/address/mud/AddressMudRecordsKeyFilter.tsx b/client/features/chain-variants/mud/pages/address/AddressMudRecordsKeyFilter.tsx similarity index 100% rename from ui/address/mud/AddressMudRecordsKeyFilter.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudRecordsKeyFilter.tsx diff --git a/ui/address/mud/AddressMudRecordsKeyFilterContent.tsx b/client/features/chain-variants/mud/pages/address/AddressMudRecordsKeyFilterContent.tsx similarity index 100% rename from ui/address/mud/AddressMudRecordsKeyFilterContent.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudRecordsKeyFilterContent.tsx diff --git a/ui/address/mud/AddressMudRecordsTable.tsx b/client/features/chain-variants/mud/pages/address/AddressMudRecordsTable.tsx similarity index 97% rename from ui/address/mud/AddressMudRecordsTable.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudRecordsTable.tsx index 3f5ac4c9c0..1cc3b8170f 100644 --- a/ui/address/mud/AddressMudRecordsTable.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudRecordsTable.tsx @@ -2,12 +2,13 @@ import { Box, Flex } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { AddressMudRecords, AddressMudRecordsFilter, AddressMudRecordsSorting } from 'types/api/address'; +import type { AddressMudRecords, AddressMudRecordsFilter, AddressMudRecordsSorting } from 'client/features/chain-variants/mud/types/api'; import { route } from 'nextjs-routes'; -import capitalizeFirstLetter from 'lib/capitalizeFirstLetter'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import capitalizeFirstLetter from 'client/shared/text/capitalize-first-letter'; + import { Link } from 'toolkit/chakra/link'; import { TableBody, TableCell, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import type { TableColumnHeaderProps } from 'toolkit/chakra/table'; diff --git a/ui/address/mud/AddressMudTable.pw.tsx b/client/features/chain-variants/mud/pages/address/AddressMudTable.pw.tsx similarity index 100% rename from ui/address/mud/AddressMudTable.pw.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudTable.pw.tsx diff --git a/ui/address/mud/AddressMudTable.tsx b/client/features/chain-variants/mud/pages/address/AddressMudTable.tsx similarity index 96% rename from ui/address/mud/AddressMudTable.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudTable.tsx index 18dd58a283..363bfaad61 100644 --- a/ui/address/mud/AddressMudTable.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudTable.tsx @@ -2,10 +2,11 @@ import { Box, HStack, chakra } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { AddressMudRecordsFilter, AddressMudRecordsSorting } from 'types/api/address'; +import type { AddressMudRecordsFilter, AddressMudRecordsSorting } from 'client/features/chain-variants/mud/types/api'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { Tag } from 'toolkit/chakra/tag'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; diff --git a/ui/address/mud/AddressMudTables.pw.tsx b/client/features/chain-variants/mud/pages/address/AddressMudTables.pw.tsx similarity index 100% rename from ui/address/mud/AddressMudTables.pw.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudTables.pw.tsx diff --git a/ui/address/mud/AddressMudTables.tsx b/client/features/chain-variants/mud/pages/address/AddressMudTables.tsx similarity index 90% rename from ui/address/mud/AddressMudTables.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudTables.tsx index 7cadd40729..80c53da0e4 100644 --- a/ui/address/mud/AddressMudTables.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudTables.tsx @@ -2,10 +2,12 @@ import { Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import useDebounce from 'lib/hooks/useDebounce'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { ADDRESS_MUD_TABLE_ITEM } from 'stubs/address'; +import { ADDRESS_MUD_TABLE_ITEM } from 'client/features/chain-variants/mud/stubs/address'; + +import useDebounce from 'client/shared/hooks/useDebounce'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { generateListStub } from 'stubs/utils'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; diff --git a/ui/address/mud/AddressMudTablesListItem.tsx b/client/features/chain-variants/mud/pages/address/AddressMudTablesListItem.tsx similarity index 97% rename from ui/address/mud/AddressMudTablesListItem.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudTablesListItem.tsx index 3c09a36a70..18f9359fff 100644 --- a/ui/address/mud/AddressMudTablesListItem.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudTablesListItem.tsx @@ -2,7 +2,7 @@ import { Text, Flex, VStack, chakra, Box, Grid, GridItem, Separator } from '@cha import { useRouter } from 'next/router'; import React from 'react'; -import type { AddressMudTableItem } from 'types/api/address'; +import type { AddressMudTableItem } from 'client/features/chain-variants/mud/types/api'; import { route } from 'nextjs-routes'; diff --git a/ui/address/mud/AddressMudTablesTable.tsx b/client/features/chain-variants/mud/pages/address/AddressMudTablesTable.tsx similarity index 93% rename from ui/address/mud/AddressMudTablesTable.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudTablesTable.tsx index 321e07bc1a..3623db6a48 100644 --- a/ui/address/mud/AddressMudTablesTable.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudTablesTable.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { AddressMudTables } from 'types/api/address'; +import type { AddressMudTables } from 'client/features/chain-variants/mud/types/api'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; diff --git a/ui/address/mud/AddressMudTablesTableItem.tsx b/client/features/chain-variants/mud/pages/address/AddressMudTablesTableItem.tsx similarity index 98% rename from ui/address/mud/AddressMudTablesTableItem.tsx rename to client/features/chain-variants/mud/pages/address/AddressMudTablesTableItem.tsx index 1f0712a6d9..d7557fe012 100644 --- a/ui/address/mud/AddressMudTablesTableItem.tsx +++ b/client/features/chain-variants/mud/pages/address/AddressMudTablesTableItem.tsx @@ -2,7 +2,7 @@ import { Text, VStack, chakra } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { AddressMudTableItem } from 'types/api/address'; +import type { AddressMudTableItem } from 'client/features/chain-variants/mud/types/api'; import { route } from 'nextjs-routes'; diff --git a/ui/address/mud/__screenshots__/AddressMudRecord.pw.tsx_default_base-view-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudRecord.pw.tsx_default_base-view-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudRecord.pw.tsx_default_base-view-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudRecord.pw.tsx_default_base-view-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudRecord.pw.tsx_default_mobile-base-view-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudRecord.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudRecord.pw.tsx_default_mobile-base-view-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudRecord.pw.tsx_default_mobile-base-view-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_default_base-view-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_default_base-view-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_default_empty-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_default_empty-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_default_empty-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_default_empty-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_default_expanded-view-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_default_expanded-view-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_default_expanded-view-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_default_expanded-view-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_mobile_empty-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_mobile_empty-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_mobile_empty-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_mobile_empty-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_mobile_expanded-view-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_mobile_expanded-view-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTable.pw.tsx_mobile_expanded-view-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTable.pw.tsx_mobile_expanded-view-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_default_base-view-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_default_base-view-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_default_with-schema-opened-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_default_with-schema-opened-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_default_with-schema-opened-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_default_with-schema-opened-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_mobile_base-view-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_mobile_with-schema-opened-mobile-1.png b/client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_mobile_with-schema-opened-mobile-1.png similarity index 100% rename from ui/address/mud/__screenshots__/AddressMudTables.pw.tsx_mobile_with-schema-opened-mobile-1.png rename to client/features/chain-variants/mud/pages/address/__screenshots__/AddressMudTables.pw.tsx_mobile_with-schema-opened-mobile-1.png diff --git a/ui/address/mud/utils.ts b/client/features/chain-variants/mud/pages/address/utils.ts similarity index 85% rename from ui/address/mud/utils.ts rename to client/features/chain-variants/mud/pages/address/utils.ts index 259926daac..5f00980c70 100644 --- a/ui/address/mud/utils.ts +++ b/client/features/chain-variants/mud/pages/address/utils.ts @@ -1,4 +1,4 @@ -import capitalizeFirstLetter from 'lib/capitalizeFirstLetter'; +import capitalizeFirstLetter from 'client/shared/text/capitalize-first-letter'; export const SORT_SEQUENCE: Record<'key0' | 'key1', Array<'desc' | 'asc' | undefined>> = { key0: [ 'desc', 'asc', undefined ], diff --git a/client/features/chain-variants/mud/stubs/address.ts b/client/features/chain-variants/mud/stubs/address.ts new file mode 100644 index 0000000000..31228bce89 --- /dev/null +++ b/client/features/chain-variants/mud/stubs/address.ts @@ -0,0 +1,8 @@ +import type { AddressMudTableItem } from 'client/features/chain-variants/mud/types/api'; + +import { MUD_SCHEMA, MUD_TABLE } from 'stubs/mud'; + +export const ADDRESS_MUD_TABLE_ITEM: AddressMudTableItem = { + schema: MUD_SCHEMA, + table: MUD_TABLE, +}; diff --git a/client/features/chain-variants/mud/types/api.ts b/client/features/chain-variants/mud/types/api.ts new file mode 100644 index 0000000000..8a8fe95e7b --- /dev/null +++ b/client/features/chain-variants/mud/types/api.ts @@ -0,0 +1,53 @@ +import type { MudWorldSchema, MudWorldTable } from 'types/api/mudWorlds'; + +export type AddressMudTableItem = { + schema: MudWorldSchema; + table: MudWorldTable; +}; + +export type AddressMudTables = { + items: Array; + next_page_params: { + items_count: number; + table_id: string; + }; +}; + +export type AddressMudTablesFilter = { + q?: string; +}; + +export type AddressMudRecords = { + items: Array; + schema: MudWorldSchema; + table: MudWorldTable; + next_page_params: { + items_count: number; + key0: string; + key1: string; + key_bytes: string; + }; +}; + +export type AddressMudRecordsItem = { + decoded: Record>; + id: string; + is_deleted: boolean; + timestamp: string; +}; + +export type AddressMudRecordsFilter = { + filter_key0?: string; + filter_key1?: string; +}; + +export type AddressMudRecordsSorting = { + sort: 'key0' | 'key1'; + order: 'asc' | 'desc' | undefined; +}; + +export type AddressMudRecord = { + record: AddressMudRecordsItem; + schema: MudWorldSchema; + table: MudWorldTable; +}; diff --git a/client/features/chain-variants/rootstock/mocks/block.ts b/client/features/chain-variants/rootstock/mocks/block.ts new file mode 100644 index 0000000000..68538b68ee --- /dev/null +++ b/client/features/chain-variants/rootstock/mocks/block.ts @@ -0,0 +1,13 @@ +/* eslint-disable max-len */ +import type { Block } from 'client/slices/block/types/api'; + +import { base } from 'client/slices/block/mocks/block'; + +export const rootstock: Block = { + ...base, + bitcoin_merged_mining_coinbase_transaction: '0x0000000000000080a1219cea298d65d545b56abafe7c5421edfaf084cf9e374bb23ea985ebd86b206088ac0000000000000000266a24aa21a9edb2ac3022ad2a5327449f029b6aa3d2e55605061b5d8171b30abf5b330d1959c900000000000000002a6a52534b424c4f434b3a481d071e57c6c47cb8eb716295a7079b15859962abf35e32f107b21f003f0bb900000000', + bitcoin_merged_mining_header: '0x000000204a7e42cadf8b5b0a094755c5a13298e596d61f361c6d31171a00000000000000970e51977cd6f82bab9ed62e678c8d8ca664af9d5c3b5cea39d5d4337c7abedae334c9649fc63e1982a84aaa', + bitcoin_merged_mining_merkle_proof: '0x09f386e5e6feb20706a1b5d0817eae96f0ebb0d713eeefe6d5625afc6fd87fcdfe8cc9118bb49e32db87f8e928dcb13dd327b526ced76fb9de0115a5dca8d2a9657c929360ad07418fc7e1a3120da27e0002470d0c98c9b8b5b2835e64e379421d2469204533307bf0c5a087d93fd1dfb3aaea3ee83099928860f6cca891cf59d73c4e3c6053ea4b385dce39067e87c28805ddd89c4ff10500401bec7c248f749ad6f0933e6ad270e447d01711aca1cc26d7989ee59e1431fd2fd5d058edca6d', + hash_for_merged_mining: '0x481d071e57c6c47cb8eb716295a7079b15859962abf35e32f107b21f003f0bb9', + minimum_gas_price: '59240000', +}; diff --git a/client/features/chain-variants/rootstock/types/api.ts b/client/features/chain-variants/rootstock/types/api.ts new file mode 100644 index 0000000000..800fa927e9 --- /dev/null +++ b/client/features/chain-variants/rootstock/types/api.ts @@ -0,0 +1,7 @@ +export interface BlockRootstock { + bitcoin_merged_mining_coinbase_transaction?: string | null; + bitcoin_merged_mining_header?: string | null; + bitcoin_merged_mining_merkle_proof?: string | null; + hash_for_merged_mining?: string | null; + minimum_gas_price?: string | null; +} diff --git a/client/features/chain-variants/stability/mocks/tx.ts b/client/features/chain-variants/stability/mocks/tx.ts new file mode 100644 index 0000000000..684ff13183 --- /dev/null +++ b/client/features/chain-variants/stability/mocks/tx.ts @@ -0,0 +1,47 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +import { base } from 'client/slices/tx/mocks/tx'; + +export const stabilityTx: Transaction = { + ...base, + stability_fee: { + dapp_address: { + hash: '0xDc2B93f3291030F3F7a6D9363ac37757f7AD5C43', + implementations: null, + is_contract: false, + is_verified: null, + name: null, + private_tags: [], + public_tags: [], + watchlist_names: [], + ens_domain_name: null, + }, + dapp_fee: '34381250000000', + token: { + address_hash: '0xDc2B93f3291030F3F7a6D9363ac37757f7AD5C43', + circulating_market_cap: null, + decimals: '18', + exchange_rate: '123.567', + holders_count: '92', + icon_url: 'https://example.com/icon.png', + name: 'Stability Gas', + symbol: 'GAS', + total_supply: '10000000000000000000000000', + type: 'ERC-20', + reputation: 'ok', + }, + total_fee: '68762500000000', + validator_address: { + hash: '0x1432997a4058acbBe562F3c1E79738c142039044', + implementations: null, + is_contract: false, + is_verified: null, + name: null, + private_tags: [], + public_tags: [], + watchlist_names: [], + ens_domain_name: null, + }, + validator_fee: '34381250000000', + }, +}; diff --git a/client/features/chain-variants/stability/types/api.ts b/client/features/chain-variants/stability/types/api.ts new file mode 100644 index 0000000000..7b48baa81a --- /dev/null +++ b/client/features/chain-variants/stability/types/api.ts @@ -0,0 +1,13 @@ +import type { AddressParam } from 'client/slices/address/types/api'; +import type { TokenInfo } from 'types/api/token'; + +export interface TransactionStability { + stability_fee?: { + dapp_address: AddressParam; + dapp_fee: string; + token: TokenInfo; + total_fee: string; + validator_address: AddressParam; + validator_fee: string; + }; +} diff --git a/ui/tx/TxAllowedPeekers.tsx b/client/features/chain-variants/suave/pages/tx/TxAllowedPeekers.tsx similarity index 91% rename from ui/tx/TxAllowedPeekers.tsx rename to client/features/chain-variants/suave/pages/tx/TxAllowedPeekers.tsx index 6f875ef985..07297702c0 100644 --- a/ui/tx/TxAllowedPeekers.tsx +++ b/client/features/chain-variants/suave/pages/tx/TxAllowedPeekers.tsx @@ -1,8 +1,9 @@ import React from 'react'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { CollapsibleList } from 'toolkit/chakra/collapsible'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; interface Props { items: Array; diff --git a/ui/tx/TxDetailsWrapped.tsx b/client/features/chain-variants/suave/pages/tx/TxDetailsWrapped.tsx similarity index 89% rename from ui/tx/TxDetailsWrapped.tsx rename to client/features/chain-variants/suave/pages/tx/TxDetailsWrapped.tsx index 232d56efa3..58d392eccf 100644 --- a/ui/tx/TxDetailsWrapped.tsx +++ b/client/features/chain-variants/suave/pages/tx/TxDetailsWrapped.tsx @@ -2,19 +2,20 @@ import { Flex, Grid } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import type { ExcludeUndefined } from 'types/utils'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import TxFee from 'client/slices/tx/components/TxFee'; +import TxDetailsGasPrice from 'client/slices/tx/pages/details/info/parts/TxDetailsGasPrice'; +import TxDetailsOther from 'client/slices/tx/pages/details/info/parts/TxDetailsOther'; + import { Badge } from 'toolkit/chakra/badge'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoNativeCoinValue from 'ui/shared/DetailedInfo/DetailedInfoNativeCoinValue'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData'; import RawInputData from 'ui/shared/RawInputData'; -import TxFee from 'ui/shared/tx/TxFee'; -import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice'; -import TxDetailsOther from 'ui/tx/details/TxDetailsOther'; interface Props { data: ExcludeUndefined; diff --git a/client/features/chain-variants/suave/types/api.ts b/client/features/chain-variants/suave/types/api.ts new file mode 100644 index 0000000000..1523685446 --- /dev/null +++ b/client/features/chain-variants/suave/types/api.ts @@ -0,0 +1,11 @@ +import type { AddressParam } from 'client/slices/address/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; + +export type WrappedTransactionFields = 'decoded_input' | 'fee' | 'gas_limit' | 'gas_price' | 'hash' | 'max_fee_per_gas' | +'max_priority_fee_per_gas' | 'method' | 'nonce' | 'raw_input' | 'to' | 'type' | 'value'; + +export interface TransactionSuave { + execution_node?: AddressParam | null; + allowed_peekers?: Array; + wrapped?: Pick; +} diff --git a/ui/shared/entities/address/AddressEntityTacTon.tsx b/client/features/chain-variants/tac/components/AddressEntityTacTon.tsx similarity index 94% rename from ui/shared/entities/address/AddressEntityTacTon.tsx rename to client/features/chain-variants/tac/components/AddressEntityTacTon.tsx index 9b0b369e45..0769694dbe 100644 --- a/ui/shared/entities/address/AddressEntityTacTon.tsx +++ b/client/features/chain-variants/tac/components/AddressEntityTacTon.tsx @@ -5,9 +5,9 @@ import * as tac from '@blockscout/tac-operation-lifecycle-types'; import { route } from 'nextjs-routes'; -import config from 'configs/app'; +import * as AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import * as AddressEntity from './AddressEntity'; +import config from 'configs/app'; const tacFeature = config.features.tac; diff --git a/ui/shared/entities/tx/TxEntityTon.tsx b/client/features/chain-variants/tac/components/TxEntityTon.tsx similarity index 90% rename from ui/shared/entities/tx/TxEntityTon.tsx rename to client/features/chain-variants/tac/components/TxEntityTon.tsx index 1953720a56..b85c610686 100644 --- a/ui/shared/entities/tx/TxEntityTon.tsx +++ b/client/features/chain-variants/tac/components/TxEntityTon.tsx @@ -1,11 +1,11 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; +import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import config from 'configs/app'; import { stripTrailingSlash } from 'toolkit/utils/url'; -import * as TxEntity from './TxEntity'; - const tacFeature = config.features.tac; const TxEntityTon = (props: TxEntity.EntityProps) => { diff --git a/ui/tx/details/TxDetailsTacOperation.tsx b/client/features/chain-variants/tac/pages/tx/TxDetailsTacOperation.tsx similarity index 97% rename from ui/tx/details/TxDetailsTacOperation.tsx rename to client/features/chain-variants/tac/pages/tx/TxDetailsTacOperation.tsx index 8f46c0a573..ea6c79171e 100644 --- a/ui/tx/details/TxDetailsTacOperation.tsx +++ b/client/features/chain-variants/tac/pages/tx/TxDetailsTacOperation.tsx @@ -1,8 +1,9 @@ import { HStack } from '@chakra-ui/react'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { getTacOperationStage } from 'lib/operations/tac'; import { TAC_OPERATION_DETAILS } from 'stubs/operations'; import { Tag } from 'toolkit/chakra/tag'; diff --git a/ui/shared/entities/tx/TxEntityZetaChainCC.tsx b/client/features/chain-variants/zeta-chain/TxEntityZetaChainCC.tsx similarity index 85% rename from ui/shared/entities/tx/TxEntityZetaChainCC.tsx rename to client/features/chain-variants/zeta-chain/TxEntityZetaChainCC.tsx index 4ef683752c..5dae983e1b 100644 --- a/ui/shared/entities/tx/TxEntityZetaChainCC.tsx +++ b/client/features/chain-variants/zeta-chain/TxEntityZetaChainCC.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { route } from 'nextjs/routes'; -import * as TxEntity from './TxEntity'; +import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; const TxEntityZetaChainCC = (props: TxEntity.EntityProps) => { const defaultHref = route({ pathname: '/cc/tx/[hash]', query: { hash: props.hash } }); diff --git a/ui/shared/entities/tx/TxEntityZetaChainExternal.tsx b/client/features/chain-variants/zeta-chain/TxEntityZetaChainExternal.tsx similarity index 93% rename from ui/shared/entities/tx/TxEntityZetaChainExternal.tsx rename to client/features/chain-variants/zeta-chain/TxEntityZetaChainExternal.tsx index c085f069db..06f7a4127b 100644 --- a/ui/shared/entities/tx/TxEntityZetaChainExternal.tsx +++ b/client/features/chain-variants/zeta-chain/TxEntityZetaChainExternal.tsx @@ -5,9 +5,9 @@ import type { ExternalChain } from 'types/externalChains'; import { route } from 'nextjs/routes'; -import useZetaChainConfig from 'ui/zetaChain/useZetaChainConfig'; +import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; -import * as TxEntity from './TxEntity'; +import useZetaChainConfig from 'ui/zetaChain/useZetaChainConfig'; type Props = { chainId: string; diff --git a/ui/shared/entities/address/AddressEntityZetaChain.pw.tsx b/client/features/chain-variants/zeta-chain/components/AddressEntityZetaChain.pw.tsx similarity index 100% rename from ui/shared/entities/address/AddressEntityZetaChain.pw.tsx rename to client/features/chain-variants/zeta-chain/components/AddressEntityZetaChain.pw.tsx diff --git a/ui/shared/entities/address/AddressEntityZetaChain.tsx b/client/features/chain-variants/zeta-chain/components/AddressEntityZetaChain.tsx similarity index 89% rename from ui/shared/entities/address/AddressEntityZetaChain.tsx rename to client/features/chain-variants/zeta-chain/components/AddressEntityZetaChain.tsx index 280a234c41..4e0b9053df 100644 --- a/ui/shared/entities/address/AddressEntityZetaChain.tsx +++ b/client/features/chain-variants/zeta-chain/components/AddressEntityZetaChain.tsx @@ -5,16 +5,16 @@ import type { ExternalChain } from 'types/externalChains'; import { route } from 'nextjs/routes'; +import type * as AddressEntityBase from 'client/slices/address/components/entity/AddressEntity'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import { unknownAddress } from 'client/slices/address/utils/consts'; + import config from 'configs/app'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; -import { unknownAddress } from 'ui/shared/address/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; import IconSvg from 'ui/shared/IconSvg'; import useZetaChainConfig from 'ui/zetaChain/useZetaChainConfig'; -import type * as AddressEntityBase from './AddressEntity'; -import AddressEntity from './AddressEntity'; - interface Props extends Omit { chainId?: string; address: { hash: string }; diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png b/client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png rename to client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-current-chain-icon-dark-mode-1.png b/client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-current-chain-icon-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-current-chain-icon-dark-mode-1.png rename to client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_dark-color-mode_with-current-chain-icon-dark-mode-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-1.png b/client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-1.png rename to client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png b/client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png rename to client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-current-chain-icon-dark-mode-1.png b/client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-current-chain-icon-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-current-chain-icon-dark-mode-1.png rename to client/features/chain-variants/zeta-chain/components/__screenshots__/AddressEntityZetaChain.pw.tsx_default_with-current-chain-icon-dark-mode-1.png diff --git a/client/features/chain-variants/zilliqa/mocks/block.ts b/client/features/chain-variants/zilliqa/mocks/block.ts new file mode 100644 index 0000000000..d84534d249 --- /dev/null +++ b/client/features/chain-variants/zilliqa/mocks/block.ts @@ -0,0 +1,44 @@ +/* eslint-disable max-len */ +import type { ZilliqaBlockData } from 'client/features/chain-variants/zilliqa/types/api'; +import type { Block } from 'client/slices/block/types/api'; + +import { base } from 'client/slices/block/mocks/block'; + +export const zilliqaWithAggregateQuorumCertificate: Block = { + ...base, + zilliqa: { + view: 1137735, + aggregate_quorum_certificate: { + signature: '0x82d29e8f06adc890f6574c3d0ae0c811de1db695b05ed2755ef384fe21bc44f6505b99e201f6000a65f38ff6a13e286306d0e380ef1b43a273eb9947b3f11f852e14b93c258c32b516f89696fcb1190b147364b789572ebdf85d79c4cf3cbbbb', + view: 1137735, + signers: [ 1, 2, 3, 8 ], + nested_quorum_certificates: [ + { + signature: '0xaeb3567577f9db68565c6f97c158b17522620a9684c6f6beaa78920951ad4cae0f287b630bdd034c4a4f89ada42e3dbe012985e976a6f64057d735a4531a26b4e46c182414eabbe625e5b15e6645be5b6522bdec113df408874f6d1e0d894dca', + view: 1137732, + proposed_by_validator_index: 1, + signers: [ 3, 8 ], + }, + { + signature: '0xaeb3567577f9db68565c6f97c158b17522620a9684c6f6beaa78920951ad4cae0f287b630bdd034c4a4f89ada42e3dbe012985e976a6f64057d735a4531a26b4e46c182414eabbe625e5b15e6645be5b6522bdec113df408874f6d1e0d894dca', + view: 1137732, + proposed_by_validator_index: 2, + signers: [ 0, 2 ], + }, + ], + }, + quorum_certificate: { + signature: '0xaeb3567577f9db68565c6f97c158b17522620a9684c6f6beaa78920951ad4cae0f287b630bdd034c4a4f89ada42e3dbe012985e976a6f64057d735a4531a26b4e46c182414eabbe625e5b15e6645be5b6522bdec113df408874f6d1e0d894dca', + view: 1137732, + signers: [ 0, 2, 3, 8 ], + }, + }, +}; + +export const zilliqaWithoutAggregateQuorumCertificate: Block = { + ...base, + zilliqa: { + ...zilliqaWithAggregateQuorumCertificate.zilliqa, + aggregate_quorum_certificate: null, + } as ZilliqaBlockData, +}; diff --git a/ui/block/details/BlockDetailsZilliqaQuorumCertificate.pw.tsx b/client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate.pw.tsx similarity index 89% rename from ui/block/details/BlockDetailsZilliqaQuorumCertificate.pw.tsx rename to client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate.pw.tsx index af93a2cb7c..01101355f2 100644 --- a/ui/block/details/BlockDetailsZilliqaQuorumCertificate.pw.tsx +++ b/client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate.pw.tsx @@ -1,9 +1,10 @@ import { Grid } from '@chakra-ui/react'; import React from 'react'; -import type { ZilliqaQuorumCertificate } from 'types/api/block'; +import type { ZilliqaQuorumCertificate } from 'client/features/chain-variants/zilliqa/types/api'; + +import * as blockMock from 'client/slices/block/mocks/block'; -import * as blockMock from 'mocks/blocks/block'; import { test, expect } from 'playwright/lib'; import BlockDetailsZilliqaQuorumCertificate from './BlockDetailsZilliqaQuorumCertificate'; diff --git a/ui/block/details/BlockDetailsZilliqaQuorumCertificate.tsx b/client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate.tsx similarity index 98% rename from ui/block/details/BlockDetailsZilliqaQuorumCertificate.tsx rename to client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate.tsx index a375986b06..5d89f97ba6 100644 --- a/ui/block/details/BlockDetailsZilliqaQuorumCertificate.tsx +++ b/client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate.tsx @@ -1,7 +1,7 @@ import { Separator, Grid, GridItem } from '@chakra-ui/react'; import React from 'react'; -import type { ZilliqaNestedQuorumCertificate, ZilliqaQuorumCertificate } from 'types/api/block'; +import type { ZilliqaNestedQuorumCertificate, ZilliqaQuorumCertificate } from 'client/features/chain-variants/zilliqa/types/api'; import { AccordionRoot, AccordionItem, AccordionItemTrigger, AccordionItemContent } from 'toolkit/chakra/accordion'; import { Hint } from 'toolkit/components/Hint/Hint'; diff --git a/ui/block/details/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_aggregated-quorum-certificate-mobile-1.png b/client/features/chain-variants/zilliqa/pages/block/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_aggregated-quorum-certificate-mobile-1.png similarity index 100% rename from ui/block/details/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_aggregated-quorum-certificate-mobile-1.png rename to client/features/chain-variants/zilliqa/pages/block/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_aggregated-quorum-certificate-mobile-1.png diff --git a/ui/block/details/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_quorum-certificate-1.png b/client/features/chain-variants/zilliqa/pages/block/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_quorum-certificate-1.png similarity index 100% rename from ui/block/details/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_quorum-certificate-1.png rename to client/features/chain-variants/zilliqa/pages/block/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_default_quorum-certificate-1.png diff --git a/ui/block/details/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_mobile_aggregated-quorum-certificate-mobile-1.png b/client/features/chain-variants/zilliqa/pages/block/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_mobile_aggregated-quorum-certificate-mobile-1.png similarity index 100% rename from ui/block/details/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_mobile_aggregated-quorum-certificate-mobile-1.png rename to client/features/chain-variants/zilliqa/pages/block/__screenshots__/BlockDetailsZilliqaQuorumCertificate.pw.tsx_mobile_aggregated-quorum-certificate-mobile-1.png diff --git a/client/features/chain-variants/zilliqa/types/api.ts b/client/features/chain-variants/zilliqa/types/api.ts new file mode 100644 index 0000000000..1a7f42091c --- /dev/null +++ b/client/features/chain-variants/zilliqa/types/api.ts @@ -0,0 +1,31 @@ +export interface AddressZilliqaParams { + is_scilla_contract: boolean; +} + +export interface TransactionZilliqa { + zilliqa?: { + is_scilla: boolean; + }; +} + +export interface ZilliqaQuorumCertificate { + view: number; + signature: string; + signers: Array; +} + +export interface ZilliqaNestedQuorumCertificate extends ZilliqaQuorumCertificate { + proposed_by_validator_index: number; +} + +export interface ZilliqaBlockData { + view: number; + quorum_certificate: ZilliqaQuorumCertificate; + aggregate_quorum_certificate: (ZilliqaQuorumCertificate & { + nested_quorum_certificates: Array; + }) | null; +} + +export interface BlockZilliqa { + zilliqa?: ZilliqaBlockData; +} diff --git a/lib/web3/account/useAccountDynamic.ts b/client/features/connect-wallet/hooks/account/useAccountDynamic.ts similarity index 100% rename from lib/web3/account/useAccountDynamic.ts rename to client/features/connect-wallet/hooks/account/useAccountDynamic.ts diff --git a/lib/web3/account/useAccountFallback.ts b/client/features/connect-wallet/hooks/account/useAccountFallback.ts similarity index 100% rename from lib/web3/account/useAccountFallback.ts rename to client/features/connect-wallet/hooks/account/useAccountFallback.ts diff --git a/lib/web3/useAccount.ts b/client/features/connect-wallet/hooks/useAccount.ts similarity index 100% rename from lib/web3/useAccount.ts rename to client/features/connect-wallet/hooks/useAccount.ts diff --git a/lib/web3/useAccountWithDomain.ts b/client/features/connect-wallet/hooks/useAccountWithDomain.ts similarity index 94% rename from lib/web3/useAccountWithDomain.ts rename to client/features/connect-wallet/hooks/useAccountWithDomain.ts index 69969125a0..6467e76a41 100644 --- a/lib/web3/useAccountWithDomain.ts +++ b/client/features/connect-wallet/hooks/useAccountWithDomain.ts @@ -1,7 +1,8 @@ import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import useAccount from './useAccount'; diff --git a/lib/web3/useWallet.ts b/client/features/connect-wallet/hooks/useWallet.ts similarity index 100% rename from lib/web3/useWallet.ts rename to client/features/connect-wallet/hooks/useWallet.ts diff --git a/lib/web3/wallet/types.ts b/client/features/connect-wallet/hooks/wallet/types.ts similarity index 84% rename from lib/web3/wallet/types.ts rename to client/features/connect-wallet/hooks/wallet/types.ts index 20cda71054..392041c371 100644 --- a/lib/web3/wallet/types.ts +++ b/client/features/connect-wallet/hooks/wallet/types.ts @@ -1,4 +1,4 @@ -import type * as mixpanel from 'lib/mixpanel/index'; +import type * as mixpanel from 'client/shared/analytics/mixpanel'; export interface Params { source: mixpanel.EventPayload['Source']; diff --git a/lib/web3/wallet/useWalletDynamic.ts b/client/features/connect-wallet/hooks/wallet/useWalletDynamic.ts similarity index 97% rename from lib/web3/wallet/useWalletDynamic.ts rename to client/features/connect-wallet/hooks/wallet/useWalletDynamic.ts index 466ffb4bec..2e505df926 100644 --- a/lib/web3/wallet/useWalletDynamic.ts +++ b/client/features/connect-wallet/hooks/wallet/useWalletDynamic.ts @@ -4,7 +4,7 @@ import { useAccountEffect } from 'wagmi'; import type { Params, Result } from './types'; -import * as mixpanel from 'lib/mixpanel/index'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; import useAccountDynamic from '../account/useAccountDynamic'; diff --git a/lib/web3/wallet/useWalletFallback.ts b/client/features/connect-wallet/hooks/wallet/useWalletFallback.ts similarity index 100% rename from lib/web3/wallet/useWalletFallback.ts rename to client/features/connect-wallet/hooks/wallet/useWalletFallback.ts diff --git a/lib/web3/wallet/useWalletReown.ts b/client/features/connect-wallet/hooks/wallet/useWalletReown.ts similarity index 97% rename from lib/web3/wallet/useWalletReown.ts rename to client/features/connect-wallet/hooks/wallet/useWalletReown.ts index 2cc3b70d0d..8cb8d64b3a 100644 --- a/lib/web3/wallet/useWalletReown.ts +++ b/client/features/connect-wallet/hooks/wallet/useWalletReown.ts @@ -4,7 +4,7 @@ import { useAccountEffect, useAccount, useDisconnect } from 'wagmi'; import type { Params, Result } from './types'; -import * as mixpanel from 'lib/mixpanel/index'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; export default function useWalletReown({ source, onConnect }: Params): Result { const { open: openModal } = useAppKit(); diff --git a/lib/web3/chains.ts b/client/features/connect-wallet/utils/chains.ts similarity index 100% rename from lib/web3/chains.ts rename to client/features/connect-wallet/utils/chains.ts diff --git a/lib/web3/client.ts b/client/features/connect-wallet/utils/public-client.ts similarity index 100% rename from lib/web3/client.ts rename to client/features/connect-wallet/utils/public-client.ts diff --git a/lib/web3/wagmiConfig.ts b/client/features/connect-wallet/utils/wagmi-config.ts similarity index 97% rename from lib/web3/wagmiConfig.ts rename to client/features/connect-wallet/utils/wagmi-config.ts index 82496bc6bc..c40d37af31 100644 --- a/lib/web3/wagmiConfig.ts +++ b/client/features/connect-wallet/utils/wagmi-config.ts @@ -4,10 +4,11 @@ import type { Chain, Transport } from 'viem'; import { fallback, http } from 'viem'; import { createConfig } from 'wagmi'; +import { chains, parentChain } from 'client/features/connect-wallet/utils/chains'; + import appConfig from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; import multichainConfig from 'configs/multichain'; -import { chains, parentChain } from 'lib/web3/chains'; const feature = appConfig.features.blockchainInteraction; diff --git a/client/features/cross-chain-txs/components/ChainStatsDetailsCrossChainTxs.tsx b/client/features/cross-chain-txs/components/ChainStatsDetailsCrossChainTxs.tsx new file mode 100644 index 0000000000..c60cca8ca7 --- /dev/null +++ b/client/features/cross-chain-txs/components/ChainStatsDetailsCrossChainTxs.tsx @@ -0,0 +1,159 @@ +import { Flex, HStack, VStack, chakra } from '@chakra-ui/react'; +import React from 'react'; + +import type { ChainStatsChart, StatsIntervalIds } from 'client/features/chain-stats/types/client'; +import type { SankeyChartData } from 'toolkit/components/charts/sankey/types'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import ChartIntervalSelect from 'client/features/chain-stats/components/ChartIntervalSelect'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; + +import config from 'configs/app'; +import type { OnValueChangeHandler } from 'toolkit/chakra/select'; +import { Skeleton } from 'toolkit/chakra/skeleton'; +import { SankeyChartMenu } from 'toolkit/components/charts/sankey/parts/SankeyChartMenu'; +import { SankeyChartContent } from 'toolkit/components/charts/sankey/SankeyChartContent'; +import ChainSelect, { isAllOption } from 'ui/shared/externalChains/ChainSelect'; + +interface Props { + chart: ChainStatsChart; + data?: SankeyChartData; + isLoading: boolean; + isError: boolean; + isInitialLoading: boolean; + interval: StatsIntervalIds; + onIntervalChange: (interval: StatsIntervalIds) => void; + counterPartyChainIds: Array; + onCounterPartyChainIdsChange: OnValueChangeHandler; +} + +const ChainStatsDetailsCrossChainTxs = ({ + chart, + data, + isLoading, + isError, + isInitialLoading, + interval, + onIntervalChange, + counterPartyChainIds, + onCounterPartyChainIdsChange, +}: Props) => { + const ref = React.useRef(null); + + const chainsQuery = useApiQuery('interchainIndexer:chains'); + + const handleShare = React.useCallback(async() => { + mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Share chart', Info: chart.id }); + }, [ chart.id ]); + + const chainsConfig = React.useMemo(() => { + return chainsQuery.data?.items + .filter((chain) => chain.id !== config.chain.id) + .map((chain) => ({ + id: chain.id, + name: chain.name, + logo: chain.logo, + explorer_url: chain.explorer_url, + })) || []; + }, [ chainsQuery.data ]); + + const chainSelect = ( + + ); + + const chainFilter = (() => { + const chainNameElement = { config.chain.name }; + if (chart.id === 'outgoing-messages-paths') { + return ( + + From { chainNameElement } to + { chainSelect } + + ); + } + + if (chart.id === 'incoming-messages-paths') { + return ( + + { chainSelect } + to { chainNameElement } + + ); + } + + return null; + })(); + + const counterPartyChainNames = (() => { + if (isAllOption(counterPartyChainIds)) { + return 'All chains'; + } + + return counterPartyChainIds.map((chainId) => { + return chainsConfig.find((chain) => chain.id === chainId)?.name || `Chain ${ chainId }`; + }).join(', '); + })(); + + const isOutgoing = chart.id === 'outgoing-messages-paths'; + const txnsCount = data?.links?.reduce((acc, link) => acc + link.value, 0) || 0; + + return ( + <> + + + + { chainFilter } + + { data && ( + + ) } + + + + Source + { isOutgoing ? config.chain.name : counterPartyChainNames } + + + Destination + { isOutgoing ? counterPartyChainNames : config.chain.name } + + + Txns { isOutgoing ? 'sent' : 'received' } + { Number(txnsCount).toLocaleString() } + + + + + + + ); +}; + +export default React.memo(ChainStatsDetailsCrossChainTxs); diff --git a/client/features/cross-chain-txs/components/ChartWidgetContainerCrossChain.tsx b/client/features/cross-chain-txs/components/ChartWidgetContainerCrossChain.tsx new file mode 100644 index 0000000000..3aac64539f --- /dev/null +++ b/client/features/cross-chain-txs/components/ChartWidgetContainerCrossChain.tsx @@ -0,0 +1,43 @@ +import { chakra } from '@chakra-ui/react'; +import React from 'react'; + +import { route } from 'nextjs-routes'; + +import type { Props as ChartWidgetContainerProps } from 'client/features/chain-stats/components/ChartWidgetContainer'; +import { getChartUrl } from 'client/features/chain-stats/utils/chart'; + +import { SankeyChartWidget } from 'toolkit/components/charts/sankey/SankeyChartWidget'; + +import useCrossChainChartQuery from '../hooks/useCrossChainChartQuery'; +import type { CrossChainTxsChartId } from '../utils/chain-stats'; + +interface Props extends Omit { + id: CrossChainTxsChartId; +} + +const ChartWidgetContainerCrossChain = ({ id, interval, isLoading, onLoadingError, className, href, title, description }: Props) => { + + const query = useCrossChainChartQuery({ id, interval, enabled: !isLoading }); + + React.useEffect(() => { + if (query.isError) { + onLoadingError(); + } + }, [ query.isError, onLoadingError ]); + + return ( + + ); +}; + +export default React.memo(chakra(ChartWidgetContainerCrossChain)); diff --git a/client/features/cross-chain-txs/hooks/useBridgedTokensQuery.ts b/client/features/cross-chain-txs/hooks/useBridgedTokensQuery.ts new file mode 100644 index 0000000000..0173b68079 --- /dev/null +++ b/client/features/cross-chain-txs/hooks/useBridgedTokensQuery.ts @@ -0,0 +1,65 @@ +import { useRouter } from 'next/router'; +import React from 'react'; + +import type { CrossChainBridgedTokensSorting, CrossChainBridgedTokensSortingField, CrossChainBridgedTokensSortingValue } from '../types/api'; + +import useDebounce from 'client/shared/hooks/useDebounce'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + +import config from 'configs/app'; +import { INTERCHAIN_BRIDGED_TOKEN_ITEM } from 'stubs/interchainIndexer'; +import { generateListStub } from 'stubs/utils'; +import type { OnValueChangeHandler } from 'toolkit/chakra/select'; +import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; +import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; +import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; + +import { BRIDGED_TOKENS_SORT_OPTIONS } from '../utils/bridged-tokens-sort'; + +interface Props { + enabled?: boolean; +} + +export default function useBridgedTokensQuery({ enabled }: Props) { + const router = useRouter(); + + const q = getQueryParamString(router.query.q); + + const [ searchTerm, setSearchTerm ] = React.useState(q ?? ''); + const [ sort, setSort ] = React.useState( + getSortValueFromQuery(router.query, BRIDGED_TOKENS_SORT_OPTIONS) ?? 'default', + ); + + const debouncedSearchTerm = useDebounce(searchTerm, 300); + + const query = useQueryWithPages({ + resourceName: 'interchainIndexer:bridged_tokens', + pathParams: { + chainId: config.chain.id, + }, + filters: { q: debouncedSearchTerm }, + sorting: getSortParamsFromValue(sort), + options: { + enabled, + placeholderData: generateListStub<'interchainIndexer:bridged_tokens'>(INTERCHAIN_BRIDGED_TOKEN_ITEM, 10, { next_page_params: { page_token: 'token' } }), + }, + }); + + const onSearchTermChange = React.useCallback((value: string) => { + query.onFilterChange({ q: value }); + setSearchTerm(value); + }, [ query ]); + + const onSortChange: OnValueChangeHandler = React.useCallback(({ value }) => { + setSort(value[0] as CrossChainBridgedTokensSortingValue); + query.onSortingChange(value[0] === 'default' ? undefined : getSortParamsFromValue(value[0] as CrossChainBridgedTokensSortingValue)); + }, [ query ]); + + return React.useMemo(() => ({ + query, + sort, + searchTerm, + onSearchTermChange, + onSortChange, + }), [ query, sort, searchTerm, onSearchTermChange, onSortChange ]); +} diff --git a/client/features/cross-chain-txs/hooks/useCrossChainChartQuery.ts b/client/features/cross-chain-txs/hooks/useCrossChainChartQuery.ts new file mode 100644 index 0000000000..8cffb70505 --- /dev/null +++ b/client/features/cross-chain-txs/hooks/useCrossChainChartQuery.ts @@ -0,0 +1,68 @@ +import { pickBy, uniqBy } from 'es-toolkit'; + +import type { ChartDataPayloadSankey, StatsIntervalIds } from 'client/features/chain-stats/types/client'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { getDatesFromInterval } from 'client/features/chain-stats/utils/interval'; + +import config from 'configs/app'; +import { isAllOption } from 'ui/shared/externalChains/ChainSelect'; + +import { CROSS_CHAIN_TXS_CHART } from '../stubs/chain-stats'; +import { CROSS_CHAIN_TXS_CHARTS } from '../utils/chain-stats'; + +interface Props { + id: string; + interval: StatsIntervalIds; + enabled?: boolean; + counterPartyChainIds?: Array; +} + +export default function useCrossChainChartQuery({ id, interval, enabled = true, counterPartyChainIds }: Props) { + + const chart = CROSS_CHAIN_TXS_CHARTS.find((chart) => chart.id === id) ?? CROSS_CHAIN_TXS_CHARTS[0]; + const { start: startDate, end: endDate } = getDatesFromInterval(interval); + + const resourceName = chart.resourceName; + + return useApiQuery(resourceName, { + pathParams: { chainId: config.chain.id }, + queryParams: pickBy({ + from: startDate, + to: endDate, + counterparty_chain_ids: isAllOption(counterPartyChainIds) ? undefined : counterPartyChainIds, + }, (value) => value !== undefined), + queryOptions: { + enabled: enabled && CROSS_CHAIN_TXS_CHARTS.some((chart) => chart.id === id), + refetchOnMount: false, + placeholderData: (prevData) => { + return prevData ?? CROSS_CHAIN_TXS_CHART; + }, + select: (data) => { + const chains = uniqBy(data.items.flatMap((item) => [ item.source_chain, item.destination_chain ]).filter(Boolean), (chain) => chain?.id); + + return { + info: { + title: chart.title, + description: chart.description, + id: id, + resolutions: [], + }, + type: 'sankey', + data: { + nodes: chains.map((chain) => ({ id: chain.id, name: chain.name })), + links: data.items + .map((item) => { + if (!item.source_chain?.id || !item.destination_chain?.id) { + return null; + } + return { source: item.source_chain.id, target: item.destination_chain.id, value: item.messages_count }; + }) + .filter(Boolean), + }, + }; + }, + }, + }); +} diff --git a/ui/tx/useTxCrossChainTransfersQuery.tsx b/client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery.ts similarity index 100% rename from ui/tx/useTxCrossChainTransfersQuery.tsx rename to client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery.ts diff --git a/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensIndex.tsx b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensIndex.tsx new file mode 100644 index 0000000000..4df521b949 --- /dev/null +++ b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensIndex.tsx @@ -0,0 +1,69 @@ +import { Box } from '@chakra-ui/react'; +import React from 'react'; + +import type { CrossChainBridgedTokensSortingValue } from '../../types/api'; + +import config from 'configs/app'; +import type { OnValueChangeHandler } from 'toolkit/chakra/select'; +import DataListDisplay from 'ui/shared/DataListDisplay'; +import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; + +import BridgedTokensListItem from './BridgedTokensListItem'; +import BridgedTokensTable from './BridgedTokensTable'; + +interface Props { + query: QueryWithPagesResult<'interchainIndexer:bridged_tokens'>; + onSortChange: OnValueChangeHandler; + sort: CrossChainBridgedTokensSortingValue; + actionBar?: React.ReactNode; + hasActiveFilters?: boolean; + tableTop?: number; +} + +const BridgedTokensIndex = ({ query, onSortChange, sort, actionBar, hasActiveFilters, tableTop }: Props) => { + return ( + + { query.data?.items ? ( + <> + + { query.data.items.map((item, index) => { + const tokenCurrentChain = item.tokens.find((token) => String(token.chain_id) === config.chain.id); + + return ( + + ); + }) } + + + + + + ) : null } + + ); +}; + +export default React.memo(BridgedTokensIndex); diff --git a/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensListItem.tsx b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensListItem.tsx new file mode 100644 index 0000000000..99d154b4eb --- /dev/null +++ b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensListItem.tsx @@ -0,0 +1,98 @@ +import { Grid, HStack } from '@chakra-ui/react'; +import React from 'react'; + +import type { StatsBridgedTokenItem, StatsBridgedTokenRow } from '@blockscout/interchain-indexer-types'; +import type { TokenInfo } from 'types/api/token'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import getItemIndex from 'client/shared/lists/get-item-index'; + +import { Skeleton } from 'toolkit/chakra/skeleton'; +import TokenEntity from 'ui/shared/entities/token/TokenEntity'; +import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; + +interface Props { + data: StatsBridgedTokenRow; + token: StatsBridgedTokenItem | undefined; + index: number; + page: number; + isLoading?: boolean; +} + +const BridgedTokensListItem = ({ data, token, index, page, isLoading }: Props) => { + const tokenInfo: TokenInfo | undefined = React.useMemo(() => { + if (!token) { + return; + } + + return { + symbol: token.symbol ?? null, + address_hash: token.token_address, + type: 'ERC-20', + name: token.name ?? null, + decimals: String(token.decimals ?? '0'), + holders_count: null, + exchange_rate: null, + total_supply: null, + icon_url: token.icon_url ?? null, + circulating_market_cap: null, + reputation: null, + }; + }, [ token ]); + + return ( + + + + { tokenInfo ? ( + + ) : Unknown token } + + { getItemIndex(index, page) } + + + { tokenInfo && ( + + ) } + + + + Out transfers + + + { Number(data.output_transfers_count).toLocaleString() } + + + In transfers + + + { Number(data.input_transfers_count).toLocaleString() } + + + Total transfers + + + { Number(data.total_transfers_count).toLocaleString() } + + + + ); +}; + +export default React.memo(BridgedTokensListItem); diff --git a/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensTable.tsx b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensTable.tsx new file mode 100644 index 0000000000..5d452d53a7 --- /dev/null +++ b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensTable.tsx @@ -0,0 +1,83 @@ +import React from 'react'; + +import type { CrossChainBridgedTokensSortingValue, CrossChainBridgedTokensSortingField } from '../../types/api'; +import type { StatsBridgedTokenRow } from '@blockscout/interchain-indexer-types'; +import { BridgedTokensSort } from '@blockscout/interchain-indexer-types'; + +import config from 'configs/app'; +import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; +import getNextSortValue from 'ui/shared/sort/getNextSortValue'; + +import { BRIDGED_TOKENS_SORT_SEQUENCE } from '../../utils/bridged-tokens-sort'; +import BridgedTokensTableItem from './BridgedTokensTableItem'; + +interface Props { + data: Array; + isLoading?: boolean; + sort: CrossChainBridgedTokensSortingValue; + setSorting: ({ value }: { value: Array }) => void; + page: number; + top?: number; +} + +const BridgedTokensTable = ({ data, isLoading, sort, setSorting, page, top }: Props) => { + + const onSortToggle = React.useCallback((field: CrossChainBridgedTokensSortingField) => { + const value = getNextSortValue(BRIDGED_TOKENS_SORT_SEQUENCE, field)(sort); + setSorting({ value: [ value ] }); + }, [ sort, setSorting ]); + + return ( + + + + Token + + In transfers + + + Out transfers + + + Total transfers + + + + + { data.map((item, index) => { + const tokenCurrentChain = item.tokens.find((token) => String(token.chain_id) === config.chain.id); + return ( + + ); + }) } + + + ); +}; + +export default React.memo(BridgedTokensTable); diff --git a/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensTableItem.tsx b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensTableItem.tsx new file mode 100644 index 0000000000..25f2561ced --- /dev/null +++ b/client/features/cross-chain-txs/pages/bridged-tokens/BridgedTokensTableItem.tsx @@ -0,0 +1,110 @@ +import { Flex } from '@chakra-ui/react'; +import React from 'react'; + +import type { StatsBridgedTokenItem, StatsBridgedTokenRow } from '@blockscout/interchain-indexer-types'; +import type { TokenInfo } from 'types/api/token'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; + +import getItemIndex from 'client/shared/lists/get-item-index'; + +import { Skeleton } from 'toolkit/chakra/skeleton'; +import { TableCell, TableRow } from 'toolkit/chakra/table'; +import TokenEntity from 'ui/shared/entities/token/TokenEntity'; + +interface Props { + data: StatsBridgedTokenRow; + token: StatsBridgedTokenItem | undefined; + index: number; + page: number; + isLoading?: boolean; +} + +const BridgedTokensTableItem = ({ data, token, index, page, isLoading }: Props) => { + + const tokenInfo: TokenInfo | undefined = React.useMemo(() => { + if (!token) { + return; + } + + return { + symbol: token.symbol ?? null, + address_hash: token.token_address, + type: 'ERC-20', + name: token.name ?? null, + decimals: String(token.decimals ?? '0'), + holders_count: null, + exchange_rate: null, + total_supply: null, + icon_url: token.icon_url ?? null, + circulating_market_cap: null, + reputation: null, + }; + }, [ token ]); + + return ( + + + + + { getItemIndex(index, page) } + + { tokenInfo ? ( + + + + + + + + ) : Unknown token } + + + + + { Number(data.input_transfers_count).toLocaleString() } + + + + + { Number(data.output_transfers_count).toLocaleString() } + + + + + { Number(data.total_transfers_count).toLocaleString() } + + + + ); +}; + +export default React.memo(BridgedTokensTableItem); diff --git a/client/features/cross-chain-txs/pages/ictt-users/IcttUsers.tsx b/client/features/cross-chain-txs/pages/ictt-users/IcttUsers.tsx new file mode 100644 index 0000000000..7a20099c75 --- /dev/null +++ b/client/features/cross-chain-txs/pages/ictt-users/IcttUsers.tsx @@ -0,0 +1,97 @@ +import { Box, createListCollection } from '@chakra-ui/react'; +import { useRouter } from 'next/router'; +import React from 'react'; + +import type { CrossChainChainsStatsSorting, CrossChainChainsStatsSortingField, CrossChainChainsStatsSortingValue } from '../../types/api'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + +import { INTERCHAIN_STATS_CHAINS_ITEM } from 'stubs/interchainIndexer'; +import { generateListStub } from 'stubs/utils'; +import ActionBar from 'ui/shared/ActionBar'; +import DataListDisplay from 'ui/shared/DataListDisplay'; +import PageTitle from 'ui/shared/Page/PageTitle'; +import Pagination from 'ui/shared/pagination/Pagination'; +import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; +import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; +import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; +import Sort from 'ui/shared/sort/Sort'; + +import { ICTT_USERS_SORT_OPTIONS } from '../../utils/ictt-sort'; +import IcttUsersListItem from './IcttUsersListItem'; +import IcttUsersTable from './IcttUsersTable'; + +const sortCollection = createListCollection({ + items: ICTT_USERS_SORT_OPTIONS, +}); + +const IcttUsers = () => { + const router = useRouter(); + const isMobile = useIsMobile(); + + const [ sort, setSort ] = React.useState( + getSortValueFromQuery(router.query, ICTT_USERS_SORT_OPTIONS) ?? 'default', + ); + + const { data, isPlaceholderData, isError, onSortingChange, pagination } = useQueryWithPages({ + resourceName: 'interchainIndexer:stats_chains', + sorting: getSortParamsFromValue(sort), + options: { + placeholderData: generateListStub<'interchainIndexer:stats_chains'>(INTERCHAIN_STATS_CHAINS_ITEM, 50, { next_page_params: { page_token: 'token' } }), + }, + }); + + const handleSortChange = React.useCallback(({ value }: { value: Array }) => { + setSort(value[0] as CrossChainChainsStatsSortingValue); + onSortingChange(value[0] === 'default' ? undefined : getSortParamsFromValue(value[0] as CrossChainChainsStatsSortingValue)); + }, [ onSortingChange ]); + + const actionBar = isMobile || pagination.isVisible ? ( + + + + + ) : null; + + return ( + <> + + + { data?.items ? ( + <> + + { data.items.map((item, index) => ( + + )) } + + + + + + ) : null } + + + ); +}; + +export default React.memo(IcttUsers); diff --git a/client/features/cross-chain-txs/pages/ictt-users/IcttUsersListItem.tsx b/client/features/cross-chain-txs/pages/ictt-users/IcttUsersListItem.tsx new file mode 100644 index 0000000000..506dde7e36 --- /dev/null +++ b/client/features/cross-chain-txs/pages/ictt-users/IcttUsersListItem.tsx @@ -0,0 +1,40 @@ +import { Grid } from '@chakra-ui/react'; +import React from 'react'; + +import type { StatsChainRow } from '@blockscout/interchain-indexer-types'; + +import { Skeleton } from 'toolkit/chakra/skeleton'; +import ChainSnippetList from 'ui/shared/externalChains/ChainSnippetList'; +import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; + +interface Props { + data: StatsChainRow; + isLoading?: boolean; +} + +const IcttUsersListItem = ({ data, isLoading }: Props) => { + const chainInfo = React.useMemo(() => { + return { + name: data.name, + id: String(data.id), + logo: data.logo, + explorer_url: data.explorer_url, + }; + }, [ data.name, data.id, data.logo, data.explorer_url ]); + + return ( + + + + + Number of unique ICTT users + + + { data.unique_transfer_users_count } + + + + ); +}; + +export default React.memo(IcttUsersListItem); diff --git a/client/features/cross-chain-txs/pages/ictt-users/IcttUsersTable.tsx b/client/features/cross-chain-txs/pages/ictt-users/IcttUsersTable.tsx new file mode 100644 index 0000000000..a7bee4d19d --- /dev/null +++ b/client/features/cross-chain-txs/pages/ictt-users/IcttUsersTable.tsx @@ -0,0 +1,58 @@ +import React from 'react'; + +import type { CrossChainChainsStatsSortingField, CrossChainChainsStatsSortingValue } from '../../types/api'; +import { StatsChainsSort } from '@blockscout/interchain-indexer-types'; +import type { StatsChainRow } from '@blockscout/interchain-indexer-types'; + +import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; +import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; +import getNextSortValue from 'ui/shared/sort/getNextSortValue'; + +import { ICTT_USERS_SORT_SEQUENCE } from '../../utils/ictt-sort'; +import IcttUsersTableItem from './IcttUsersTableItem'; + +interface Props { + data: Array; + isLoading?: boolean; + sort: CrossChainChainsStatsSortingValue; + setSorting: ({ value }: { value: Array }) => void; +} + +const IcttUsersTable = ({ data, isLoading, sort, setSorting }: Props) => { + + const onSortToggle = React.useCallback((field: CrossChainChainsStatsSortingField) => { + const value = getNextSortValue(ICTT_USERS_SORT_SEQUENCE, field)(sort); + setSorting({ value: [ value ] }); + }, [ sort, setSorting ]); + + return ( + + + + Chain + + Number of unique ICTT users + + + + + { data.map((item, index) => ( + + )) } + + + ); +}; + +export default React.memo(IcttUsersTable); diff --git a/client/features/cross-chain-txs/pages/ictt-users/IcttUsersTableItem.tsx b/client/features/cross-chain-txs/pages/ictt-users/IcttUsersTableItem.tsx new file mode 100644 index 0000000000..aed20f66c8 --- /dev/null +++ b/client/features/cross-chain-txs/pages/ictt-users/IcttUsersTableItem.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +import type { StatsChainRow } from '@blockscout/interchain-indexer-types'; + +import { Skeleton } from 'toolkit/chakra/skeleton'; +import { TableCell, TableRow } from 'toolkit/chakra/table'; +import ChainSnippetList from 'ui/shared/externalChains/ChainSnippetList'; + +interface Props { + data: StatsChainRow; + isLoading?: boolean; +} + +const IcttUsersTableItem = ({ data, isLoading }: Props) => { + + const chainInfo = React.useMemo(() => { + return { + name: data.name, + id: String(data.id), + logo: data.logo, + explorer_url: data.explorer_url, + }; + }, [ data.name, data.id, data.logo, data.explorer_url ]); + + return ( + + + + + + + + { data.unique_transfer_users_count } + + + + + ); +}; + +export default React.memo(IcttUsersTableItem); diff --git a/ui/tx/details/TxDetailsCrossChainMessage.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessage.tsx similarity index 95% rename from ui/tx/details/TxDetailsCrossChainMessage.tsx rename to client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessage.tsx index ae4568757a..22670aea13 100644 --- a/ui/tx/details/TxDetailsCrossChainMessage.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessage.tsx @@ -3,13 +3,14 @@ import React from 'react'; import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; +import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; +import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; + import config from 'configs/app'; import { mdash } from 'toolkit/utils/htmlEntities'; import * as DetailedInfoItemBreakdown from 'ui/shared/DetailedInfo/DetailedInfoItemBreakdown'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import AddressEntityInterchain from 'ui/shared/entities/address/AddressEntityInterchain'; import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; -import TxEntityInterchain from 'ui/shared/entities/tx/TxEntityInterchain'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; diff --git a/ui/tx/details/TxDetailsCrossChainMessages.pw.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.pw.tsx similarity index 100% rename from ui/tx/details/TxDetailsCrossChainMessages.pw.tsx rename to client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.pw.tsx diff --git a/ui/tx/details/TxDetailsCrossChainMessages.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.tsx similarity index 96% rename from ui/tx/details/TxDetailsCrossChainMessages.tsx rename to client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.tsx index 577f148a05..03b69c3cc9 100644 --- a/ui/tx/details/TxDetailsCrossChainMessages.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { CollapsibleList } from 'toolkit/chakra/collapsible'; diff --git a/ui/tx/details/TxDetailsCrossChainTransfers.tsx b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers.tsx similarity index 91% rename from ui/tx/details/TxDetailsCrossChainTransfers.tsx rename to client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers.tsx index 6d02533e39..c83862708e 100644 --- a/ui/tx/details/TxDetailsCrossChainTransfers.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import useTxCrossChainTransfersQuery from 'client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery'; + import config from 'configs/app'; import { Link } from 'toolkit/chakra/link'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import useTxCrossChainTransfersQuery from '../useTxCrossChainTransfersQuery'; - interface Props { hash: string; isLoading: boolean; diff --git a/ui/tx/tokenTransfers/TxTokenTransferCrossChain.tsx b/client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain.tsx similarity index 96% rename from ui/tx/tokenTransfers/TxTokenTransferCrossChain.tsx rename to client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain.tsx index 5e1bdc75e6..cca8fb6c6a 100644 --- a/ui/tx/tokenTransfers/TxTokenTransferCrossChain.tsx +++ b/client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain.tsx @@ -1,14 +1,14 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import TokenTransfersCrossChainListItem from 'ui/crossChain/transfers/TokenTransfersCrossChainListItem'; import TokenTransfersCrossChainTable from 'ui/crossChain/transfers/TokenTransfersCrossChainTable'; import { getItemKey } from 'ui/crossChain/transfers/utils'; import DataListDisplay from 'ui/shared/DataListDisplay'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; -import type { TxQuery } from '../useTxQuery'; - interface Props { txQuery: TxQuery; crossChainQuery: QueryWithPagesResult<'interchainIndexer:tx_transfers'>; diff --git a/ui/tx/details/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_default_base-view-mobile-dark-mode-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_default_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_default_base-view-mobile-dark-mode-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_default_base-view-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/client/features/cross-chain-txs/pages/tx/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_mobile_base-view-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_mobile_base-view-mobile-dark-mode-1.png rename to client/features/cross-chain-txs/pages/tx/__screenshots__/TxDetailsCrossChainMessages.pw.tsx_mobile_base-view-mobile-dark-mode-1.png diff --git a/client/features/cross-chain-txs/stubs/chain-stats.ts b/client/features/cross-chain-txs/stubs/chain-stats.ts new file mode 100644 index 0000000000..55794c0da2 --- /dev/null +++ b/client/features/cross-chain-txs/stubs/chain-stats.ts @@ -0,0 +1,5 @@ +import type { GetMessagePathsResponse } from '@blockscout/interchain-indexer-types'; + +export const CROSS_CHAIN_TXS_CHART: GetMessagePathsResponse = { + items: [], +}; diff --git a/client/features/cross-chain-txs/types/api.ts b/client/features/cross-chain-txs/types/api.ts new file mode 100644 index 0000000000..3e6559f16c --- /dev/null +++ b/client/features/cross-chain-txs/types/api.ts @@ -0,0 +1,23 @@ +import type * as interchainIndexer from '@blockscout/interchain-indexer-types'; + +export interface CrossChainFilters { + q?: string; +} + +export interface CrossChainChainsStatsSorting { + sort: Exclude; + order: Exclude; +} + +export type CrossChainChainsStatsSortingField = CrossChainChainsStatsSorting['sort']; + +export type CrossChainChainsStatsSortingValue = `${ CrossChainChainsStatsSortingField }-${ CrossChainChainsStatsSorting['order'] }` | 'default'; + +export interface CrossChainBridgedTokensSorting { + sort: Exclude; + order: Exclude; +} + +export type CrossChainBridgedTokensSortingField = CrossChainBridgedTokensSorting['sort']; + +export type CrossChainBridgedTokensSortingValue = `${ CrossChainBridgedTokensSortingField }-${ CrossChainBridgedTokensSorting['order'] }` | 'default'; diff --git a/client/features/cross-chain-txs/utils/bridged-tokens-sort.ts b/client/features/cross-chain-txs/utils/bridged-tokens-sort.ts new file mode 100644 index 0000000000..07d06a72eb --- /dev/null +++ b/client/features/cross-chain-txs/utils/bridged-tokens-sort.ts @@ -0,0 +1,29 @@ +import { createListCollection } from '@chakra-ui/react'; + +import type { CrossChainBridgedTokensSortingField, CrossChainBridgedTokensSortingValue } from '../types/api'; +import * as interchainIndexer from '@blockscout/interchain-indexer-types'; + +import type { SelectOption } from 'toolkit/chakra/select'; + +export const BRIDGED_TOKENS_SORT_SEQUENCE: Record> = { + [interchainIndexer.BridgedTokensSort.TOTAL_TRANSFERS_COUNT]: [ 'TOTAL_TRANSFERS_COUNT-DESC', 'TOTAL_TRANSFERS_COUNT-ASC', 'default' ], + [interchainIndexer.BridgedTokensSort.OUTPUT_TRANSFERS_COUNT]: [ 'OUTPUT_TRANSFERS_COUNT-DESC', 'OUTPUT_TRANSFERS_COUNT-ASC', 'default' ], + [interchainIndexer.BridgedTokensSort.INPUT_TRANSFERS_COUNT]: [ 'INPUT_TRANSFERS_COUNT-DESC', 'INPUT_TRANSFERS_COUNT-ASC', 'default' ], + [interchainIndexer.BridgedTokensSort.NAME]: [ 'NAME-DESC', 'NAME-ASC', 'default' ], +}; + +export const BRIDGED_TOKENS_SORT_OPTIONS: Array> = [ + { label: 'Default', value: 'default' }, + { label: 'Total transfers count descending', value: 'TOTAL_TRANSFERS_COUNT-DESC' }, + { label: 'Total transfers count ascending', value: 'TOTAL_TRANSFERS_COUNT-ASC' }, + { label: 'Output transfers count descending', value: 'OUTPUT_TRANSFERS_COUNT-DESC' }, + { label: 'Output transfers count ascending', value: 'OUTPUT_TRANSFERS_COUNT-ASC' }, + { label: 'Input transfers count descending', value: 'INPUT_TRANSFERS_COUNT-DESC' }, + { label: 'Input transfers count ascending', value: 'INPUT_TRANSFERS_COUNT-ASC' }, + { label: 'Name descending', value: 'NAME-DESC' }, + { label: 'Name ascending', value: 'NAME-ASC' }, +]; + +export const BRIDGED_TOKENS_SORT_COLLECTION = createListCollection({ + items: BRIDGED_TOKENS_SORT_OPTIONS, +}); diff --git a/client/features/cross-chain-txs/utils/chain-stats.ts b/client/features/cross-chain-txs/utils/chain-stats.ts new file mode 100644 index 0000000000..68e6234edb --- /dev/null +++ b/client/features/cross-chain-txs/utils/chain-stats.ts @@ -0,0 +1,30 @@ +import type { ChainStatsChart, ChainStatsSection } from 'client/features/chain-stats/types/client'; + +export const CROSS_CHAIN_TXS_CHARTS = [ + { + id: 'outgoing-messages-paths' as const, + title: 'Cross-chain txns sent paths', + description: 'Bridging volume trends over time', + type: 'sankey', + resourceName: 'interchainIndexer:stats_chain_messages_sent', + resolutions: [], + }, + { + id: 'incoming-messages-paths' as const, + title: 'Cross-chain txns received paths', + description: 'Bridging volume trends over time', + type: 'sankey', + resourceName: 'interchainIndexer:stats_chain_messages_received', + resolutions: [], + }, +] satisfies Array; + +export type CrossChainTxsChartId = (typeof CROSS_CHAIN_TXS_CHARTS)[number]['id']; + +export const CROSS_CHAIN_TXS_SECTIONS: Array = [ + { + id: 'messages', + title: 'Messages', + charts: CROSS_CHAIN_TXS_CHARTS, + }, +]; diff --git a/client/features/cross-chain-txs/utils/ictt-sort.ts b/client/features/cross-chain-txs/utils/ictt-sort.ts new file mode 100644 index 0000000000..6fa139ebd8 --- /dev/null +++ b/client/features/cross-chain-txs/utils/ictt-sort.ts @@ -0,0 +1,14 @@ +import type { CrossChainChainsStatsSortingField, CrossChainChainsStatsSortingValue } from '../types/api'; +import { StatsChainsSort } from '@blockscout/interchain-indexer-types'; + +import type { SelectOption } from 'toolkit/chakra/select'; + +export const ICTT_USERS_SORT_SEQUENCE: Record> = { + [StatsChainsSort.UNIQUE_TRANSFER_USERS_COUNT]: [ 'UNIQUE_TRANSFER_USERS_COUNT-DESC', 'UNIQUE_TRANSFER_USERS_COUNT-ASC', 'default' ], +}; + +export const ICTT_USERS_SORT_OPTIONS: Array> = [ + { label: 'Default', value: 'default' }, + { label: 'Number of unique ICTT users descending', value: 'UNIQUE_TRANSFER_USERS_COUNT-DESC' }, + { label: 'Number of unique ICTT users ascending', value: 'UNIQUE_TRANSFER_USERS_COUNT-ASC' }, +]; diff --git a/client/features/csv-export/components/CsvExport.tsx b/client/features/csv-export/components/CsvExport.tsx new file mode 100644 index 0000000000..d1a9ac81aa --- /dev/null +++ b/client/features/csv-export/components/CsvExport.tsx @@ -0,0 +1,282 @@ +import type { JsxStyleProps } from '@chakra-ui/react'; +import { Box } from '@chakra-ui/react'; +import { delay, mapValues, pickBy } from 'es-toolkit'; +import React from 'react'; + +import type { CsvExportDownloadResponse } from '../types/api'; +import type { CsvExportType } from '../types/client'; +import type { FormFields } from './dialog/types'; +import type { NextJsQueryParam } from 'client/shared/router/types'; +import type { ClusterChainConfig } from 'types/multichain'; + +import buildUrl from 'client/api/build-url'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import useApiQuery from 'client/api/hooks/useApiQuery'; +import isNeedProxy from 'client/api/is-need-proxy'; +import type { ResourceName, ResourcePathParams } from 'client/api/resources'; + +import getErrorMessage from 'client/shared/errors/get-error-message'; +import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + +import config from 'configs/app'; +import { useMultichainContext } from 'lib/contexts/multichain'; +import dayjs from 'lib/date/dayjs'; +import { IconButton } from 'toolkit/chakra/icon-button'; +import { toaster } from 'toolkit/chakra/toaster'; +import { Tooltip } from 'toolkit/chakra/tooltip'; +import { useDisclosure } from 'toolkit/hooks/useDisclosure'; +import { downloadBlob } from 'toolkit/utils/file'; +import IconSvg from 'ui/shared/IconSvg'; +import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; +import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; + +import { useCsvExportContext } from '../utils/context'; +import getFileName from '../utils/get-file-name'; +import type { StorageItem } from '../utils/storage'; +import CsvExportDialog from './dialog/CsvExportDialog'; +import CsvExportDialogDescription from './dialog/CsvExportDialogDescription'; + +interface Props extends JsxStyleProps { + type: CsvExportType; + resourceName: R; + pathParams?: ResourcePathParams; + queryParams?: Record; + extraParams?: Record; + chainData?: ClusterChainConfig; + loadingInitial?: boolean; + loadingSkeleton?: boolean; + periodFilter?: boolean; +} + +const CsvExport = ({ + chainData, + loadingInitial, + loadingSkeleton, + type, + resourceName, + pathParams, + queryParams, + extraParams, + periodFilter = true, + ...rest +}: Props) => { + + const [ isPending, setIsPending ] = React.useState(false); + + const abortControllerRef = React.useRef(null); + + const isInitialLoading = useIsInitialLoading(loadingInitial); + const multichainContext = useMultichainContext(); + const dialog = useDisclosure(); + const recaptcha = useReCaptcha(); + const csvExportContext = useCsvExportContext(); + const apiFetch = useApiFetch(); + + const chain = chainData || multichainContext?.chain; + + const configQuery = useApiQuery('general:config_csv_export', { + queryOptions: { + refetchOnMount: false, + }, + chain, + }); + + const chainConfig = chain?.app_config || config; + const recordsLimit = configQuery.data?.limit || 10_000; + const isAsyncDownload = configQuery.data?.async_enabled; + + const mergedParams: Record = React.useMemo(() => { + return { + ...pathParams, + ...mapValues(queryParams || {}, (value) => getQueryParamString(value ?? undefined)), + ...extraParams, + }; + }, [ pathParams, queryParams, extraParams ]); + + const fetchFactorySync = React.useCallback((data?: FormFields) => { + return async(recaptchaToken?: string) => { + const url = buildUrl(resourceName, pathParams, { + ...mapValues(data || {}, (value) => dayjs(value).toISOString()), + ...queryParams, + }, undefined, chain); + + abortControllerRef.current = new AbortController(); + + const response = await fetch(url, { + headers: { + 'content-type': 'application/octet-stream', + ...(recaptchaToken && { 'recaptcha-v2-response': recaptchaToken }), + ...(isNeedProxy() && chain ? { 'x-endpoint': chain.app_config.apis.general?.endpoint } : {}), + }, + signal: abortControllerRef.current?.signal, + }); + + if (!response.ok) { + throw new Error(response.statusText, { + cause: { + status: response.status, + }, + }); + } + + return response; + }; + }, [ resourceName, pathParams, queryParams, chain ]); + + const fetchFactoryAsync = React.useCallback((data?: FormFields) => { + return async(recaptchaToken?: string) => { + abortControllerRef.current = new AbortController(); + + return apiFetch(resourceName, { + pathParams, + queryParams: { + ...mapValues(data || {}, (value) => dayjs(value).toISOString()), + ...queryParams, + }, + chain, + fetchParams: { + headers: { + ...(recaptchaToken && { 'recaptcha-v2-response': recaptchaToken }), + }, + signal: abortControllerRef.current?.signal, + }, + }) as Promise; + }; + }, [ apiFetch, chain, pathParams, queryParams, resourceName ]); + + const downloadFileSync = React.useCallback(async(data?: FormFields) => { + try { + setIsPending(true); + const response = await recaptcha.fetchProtectedResource(fetchFactorySync(data)); + const blob = await response.blob(); + downloadBlob( + blob, + getFileName({ + type, + params: { ...mergedParams, ...data }, + chainConfig, + }), + ); + return true; + } catch (error) { + toaster.error({ + title: 'Error', + description: (error as Error)?.message || 'Something went wrong. Try again later.', + }); + } finally { + setIsPending(false); + } + }, [ chainConfig, fetchFactorySync, mergedParams, recaptcha, type ]); + + const downloadFileAsync = React.useCallback(async(data?: FormFields) => { + try { + setIsPending(true); + const downloadResponse = await recaptcha.fetchProtectedResource(fetchFactoryAsync(data)); + + if (downloadResponse && 'request_id' in downloadResponse) { + const newItem: StorageItem = { + request_id: downloadResponse.request_id, + file_id: null, + expires_at: null, + status: 'pending', + type, + params: pickBy({ + ...mapValues(data || {}, (value) => dayjs(value).toISOString()), + ...mergedParams, + chain_id: chain?.id, + }, (value) => value !== '' && value !== undefined && value !== null), + is_highlighted: false, + }; + + csvExportContext.addItems([ newItem ]); + // we have to wait a little bit to let new item to be added to the list before opening the popup + await delay(200); + csvExportContext.onDialogOpenChange({ open: true }); + return true; + } + + throw new Error('Something went wrong. Try again later.'); + } catch (error) { + const statusCode = getErrorObjStatusCode(error); + if (statusCode === 409) { + toaster.warning({ + description: 'You cannot start a new export, please wait until the current exports finish.', + }); + return; + } + + toaster.error({ + title: 'Error', + description: getErrorMessage(error) || 'Something went wrong. Try again later.', + }); + } finally { + setIsPending(false); + } + }, [ chain, csvExportContext, fetchFactoryAsync, mergedParams, recaptcha, type ]); + + const handleButtonClick = React.useCallback(() => { + if (periodFilter) { + dialog.onOpen(); + } else { + const downloadFn = isAsyncDownload ? downloadFileAsync : downloadFileSync; + downloadFn(); + } + }, [ isAsyncDownload, periodFilter, dialog, downloadFileAsync, downloadFileSync ]); + + const handleFormSubmit = React.useCallback(async(data: FormFields) => { + const downloadFn = isAsyncDownload ? downloadFileAsync : downloadFileSync; + const isSuccess = await downloadFn(data); + if (isSuccess) { + dialog.onClose(); + } + }, [ isAsyncDownload, downloadFileAsync, downloadFileSync, dialog ]); + + const handleDownloadCancel = React.useCallback(() => { + abortControllerRef.current?.abort(); + abortControllerRef.current = null; + }, [ ]); + + if (!chainConfig.features.csvExport.isEnabled) { + return null; + } + + return ( + + + + + + + + + + + + ); +}; + +export default React.memo(CsvExport); diff --git a/client/features/csv-export/components/dialog/CsvExportDialog.tsx b/client/features/csv-export/components/dialog/CsvExportDialog.tsx new file mode 100644 index 0000000000..3d1355398f --- /dev/null +++ b/client/features/csv-export/components/dialog/CsvExportDialog.tsx @@ -0,0 +1,86 @@ +import { chakra, Flex } from '@chakra-ui/react'; +import React from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import type { FormFields } from './types'; + +import dayjs from 'lib/date/dayjs'; +import { Button } from 'toolkit/chakra/button'; +import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; +import type { OnOpenChangeHandler } from 'toolkit/hooks/useDisclosure'; + +import CsvExportFormDateField from './CsvExportFormDateField'; + +interface Props { + children: React.ReactNode; + open: boolean; + onOpenChange: OnOpenChangeHandler; + onFormSubmit: (data: FormFields) => Promise; + onCancel: () => void; + isAsyncDownload?: boolean; +} + +const CsvExportDialog = ({ open, onOpenChange, onFormSubmit, onCancel, children, isAsyncDownload }: Props) => { + const formApi = useForm({ + mode: 'onBlur', + defaultValues: { + from_period: dayjs().subtract(1, 'day').format('YYYY-MM-DDTHH:mm'), + to_period: dayjs().format('YYYY-MM-DDTHH:mm'), + }, + }); + + const { handleSubmit, formState } = formApi; + + const handleOpenChange: OnOpenChangeHandler = React.useCallback(({ open }) => { + if (formState.isSubmitting && !open) { + const confirm = window.confirm('Are you sure you want to close the dialog? The export will be cancelled.'); + if (!confirm) { + return; + } + onCancel(); + } + onOpenChange({ open }); + }, [ onOpenChange, formState.isSubmitting, onCancel ]); + + return ( + + + + + + Export data to CSV file + + + { children } + + + + + + + + + + + ); +}; + +export default React.memo(CsvExportDialog); diff --git a/client/features/csv-export/components/dialog/CsvExportDialogDescription.tsx b/client/features/csv-export/components/dialog/CsvExportDialogDescription.tsx new file mode 100644 index 0000000000..8dfa96bf27 --- /dev/null +++ b/client/features/csv-export/components/dialog/CsvExportDialogDescription.tsx @@ -0,0 +1,60 @@ +import { chakra, Flex, Text } from '@chakra-ui/react'; +import React from 'react'; + +import type { CsvExportType } from '../../types/client'; +import type { ClusterChainConfig } from 'types/multichain'; + +import shortenString from 'client/shared/text/shorten-string'; + +import ChainIcon from 'ui/shared/externalChains/ChainIcon'; + +import getPrefixByFilter from '../../utils/get-prefix-by-filter'; + +interface Props { + type: CsvExportType; + params?: Record; + chainInfo?: ClusterChainConfig; + recordsLimit: number; +} + +const CsvExportDialogDescription = ({ type, params, chainInfo, recordsLimit }: Props) => { + const chainInfoElement = chainInfo ? ( + + on + + { chainInfo.app_config.chain.name } + + ) : null; + + const limitText = recordsLimit.toLocaleString(undefined, { maximumFractionDigits: 3, notation: 'compact' }); + + if (type === 'token_holders') { + return ( + + Export holders for token + { params?.token_name && { params.token_name } } + { chainInfoElement } + to CSV file. + Limited to the top { limitText } holders by amount held. + + ); + } + + if (type.startsWith('address_')) { + const entityText = type.replace('address_', '').replace('_', ' '); + const entityPrefix = getPrefixByFilter(params?.filter_type, params?.filter_value); + return ( + + Export { entityPrefix ? `${ entityPrefix } ` : '' }{ entityText } for address + { params?.hash && { shortenString(params.hash) } } + { chainInfoElement } + to CSV file. + Limited to the last { limitText } { entityText }. + + ); + } + + return null; +}; + +export default React.memo(CsvExportDialogDescription); diff --git a/ui/csvExport/CsvExportFormField.tsx b/client/features/csv-export/components/dialog/CsvExportFormDateField.tsx similarity index 61% rename from ui/csvExport/CsvExportFormField.tsx rename to client/features/csv-export/components/dialog/CsvExportFormDateField.tsx index 9508879259..cde174fa90 100644 --- a/ui/csvExport/CsvExportFormField.tsx +++ b/client/features/csv-export/components/dialog/CsvExportFormDateField.tsx @@ -9,42 +9,41 @@ import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; interface Props { formApi: UseFormReturn; - name: 'from' | 'to'; + name: 'from_period' | 'to_period'; } -const CsvExportFormField = ({ formApi, name }: Props) => { +const CsvExportFormDateField = ({ formApi, name }: Props) => { const { formState, getValues, trigger } = formApi; const validate = React.useCallback((newValue: string) => { - if (name === 'from') { - const toValue = getValues('to'); + if (name === 'from_period') { + const toValue = getValues('to_period'); if (toValue && dayjs(newValue) > dayjs(toValue)) { return 'Incorrect date'; } - if (formState.errors.to) { - trigger('to'); + if (formState.errors.to_period) { + trigger('to_period'); } } else { - const fromValue = getValues('from'); + const fromValue = getValues('from_period'); if (fromValue && dayjs(fromValue) > dayjs(newValue)) { return 'Incorrect date'; } - if (formState.errors.from) { - trigger('from'); + if (formState.errors.from_period) { + trigger('from_period'); } } - }, [ formState.errors.from, formState.errors.to, getValues, name, trigger ]); + }, [ formState.errors.from_period, formState.errors.to_period, getValues, name, trigger ]); return ( name={ name } inputProps={{ type: 'datetime-local', max: dayjs().format('YYYY-MM-DDTHH:mm') }} - placeholder={ capitalize(name) } + placeholder={ capitalize(name.replace('_period', '')) } required rules={{ validate }} - maxW={{ base: 'auto', lg: '220px' }} /> ); }; -export default React.memo(CsvExportFormField); +export default React.memo(CsvExportFormDateField); diff --git a/client/features/csv-export/components/dialog/types.ts b/client/features/csv-export/components/dialog/types.ts new file mode 100644 index 0000000000..c4ea7dd027 --- /dev/null +++ b/client/features/csv-export/components/dialog/types.ts @@ -0,0 +1,4 @@ +export interface FormFields { + from_period: string; + to_period: string; +} diff --git a/client/features/csv-export/components/downloads/CsvExportDownloads.tsx b/client/features/csv-export/components/downloads/CsvExportDownloads.tsx new file mode 100644 index 0000000000..ccc03316d8 --- /dev/null +++ b/client/features/csv-export/components/downloads/CsvExportDownloads.tsx @@ -0,0 +1,65 @@ +import { Flex, Separator, VStack } from '@chakra-ui/react'; +import React from 'react'; + +import { IconButton } from 'toolkit/chakra/icon-button'; +import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; +import { Status } from 'toolkit/chakra/status'; +import IconSvg from 'ui/shared/IconSvg'; + +import { useCsvExportContext } from '../../utils/context'; +import CsvExportDownloadsItem from './CsvExportDownloadsItem'; + +const CsvExportDownloads = () => { + const { dialogOpen, onDialogOpenChange, items } = useCsvExportContext(); + + if (items.length === 0) { + return null; + } + + return ( + + + + + + + + { items.some((item) => item.is_highlighted) && ( + + ) } + + + + + + + } + alignItems="stretch" + gap={ 3 } + > + { items.map((item, index) => ( + + )) } + + + + + ); +}; + +export default React.memo(CsvExportDownloads); diff --git a/client/features/csv-export/components/downloads/CsvExportDownloadsItem.tsx b/client/features/csv-export/components/downloads/CsvExportDownloadsItem.tsx new file mode 100644 index 0000000000..3007dfcddb --- /dev/null +++ b/client/features/csv-export/components/downloads/CsvExportDownloadsItem.tsx @@ -0,0 +1,231 @@ +import { Text, HStack, VStack, Spinner } from '@chakra-ui/react'; +import { useQueryClient } from '@tanstack/react-query'; +import { upperFirst } from 'es-toolkit'; +import React from 'react'; +import { useInView } from 'react-intersection-observer'; + +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import isNeedProxy from 'client/api/is-need-proxy'; + +import shortenString from 'client/shared/text/shorten-string'; + +import config from 'configs/app'; +import multichainConfig from 'configs/multichain'; +import dayjs from 'lib/date/dayjs'; +import { Button } from 'toolkit/chakra/button'; +import { Link } from 'toolkit/chakra/link'; +import { Status } from 'toolkit/chakra/status'; +import { SECOND } from 'toolkit/utils/consts'; +import IconSvg from 'ui/shared/IconSvg'; + +import getPrefixByFilter from '../../utils/get-prefix-by-filter'; +import * as storage from '../../utils/storage'; +import type { StorageItem } from '../../utils/storage'; + +interface Props { + index: number; + data: StorageItem; +} + +// TODO @tom2drum check downloads on multichain +const CsvExportDownloadsItem = ({ index, data }: Props) => { + + const viewItemTimeoutRef = React.useRef(null); + + const [ expiresText, setExpiresText ] = React.useState( + data.expires_at && dayjs().isBefore(dayjs(data.expires_at)) ? dayjs(data.expires_at).fromNow() : 'soon', + ); + + const queryClient = useQueryClient(); + const chainData = data.params.chain_id ? multichainConfig()?.chains.find(({ id }) => id === data.params.chain_id) : undefined; + + React.useEffect(() => { + if (data.status !== 'completed' || !data.expires_at) { + return; + } + + const intervalId = window.setInterval(() => { + const isExpired = dayjs().isAfter(dayjs(data.expires_at)); + if (isExpired) { + window.clearInterval(intervalId); + queryClient.fetchQuery({ + queryKey: getResourceKey('general:csv_exports_item', { pathParams: { id: data.request_id }, chainId: chainData?.id }), + }); + } + setExpiresText(dayjs(data.expires_at).fromNow()); + }, SECOND); + + return () => { + window.clearInterval(intervalId); + }; + + // eslint-disable-next-line react-hooks/exhaustive-deps -- queryClient is intentionally omitted to avoid restarting interval on client identity changes + }, [ data.expires_at, data.status ]); + + const markItemAsViewed = React.useCallback(() => { + storage.removeItem(data.request_id); + queryClient.setQueryData( + getResourceKey('general:csv_exports_item', { + pathParams: { id: data.request_id }, + chainId: chainData?.id, + }), (prevData) => { + if (!prevData) { + return; + } + return { + ...prevData, + is_highlighted: false, + }; + }); + }, [ data.request_id, queryClient, chainData?.id ]); + + const handleInViewChange = React.useCallback((inView: boolean) => { + if (inView) { + viewItemTimeoutRef.current = window.setTimeout(() => { + markItemAsViewed(); + }, SECOND); + } + }, [ markItemAsViewed ]); + + const { ref } = useInView({ + triggerOnce: true, + skip: ![ 'failed', 'expired' ].includes(data.status), + threshold: 1, + onChange: handleInViewChange, + }); + + React.useEffect(() => { + return () => { + window.clearTimeout(viewItemTimeoutRef.current ?? undefined); + }; + }, []); + + const { title, color, description } = (() => { + switch (data.status) { + case 'pending': { + return { + title: `Export #${ index } in progress...`, + color: undefined, + description: 'We collect data and prepare file for export.', + }; + } + case 'completed': { + return { + title: `CSV file #${ index } is ${ !data.is_highlighted ? 'downloaded' : 'ready' }`, + color: data.is_highlighted ? 'green.500' : undefined, + description: data.is_highlighted ? `The file will expire ${ expiresText }. It will be deleted after download.` : undefined, + }; + } + case 'failed': { + return { + title: `Export #${ index } has failed`, + color: 'red.600', + description: 'The export failed. Please try again.', + }; + } + case 'expired': { + return { + title: `Export #${ index } has expired`, + color: 'red.600', + description: 'The file storage time has expired. Try generating the report again.', + }; + } + } + })(); + + const exportDetailsText = (() => { + const chainText = chainData ? `on ${ chainData.name }` : undefined; + const periodText = data.params.from_period && data.params.to_period ? + `from ${ dayjs(data.params.from_period).format('lll') } to ${ dayjs(data.params.to_period).format('lll') }` : + undefined; + + if (data.type === 'token_holders') { + return [ + 'Token holders for token', + data.params.token_name, + chainText, + periodText, + ].filter(Boolean).join(' '); + } + + if (data.type.startsWith('address_')) { + const entityPrefix = getPrefixByFilter(data.params?.filter_type, data.params?.filter_value); + + return upperFirst([ + entityPrefix, + data.type.replace('address_', '').replace('_', ' '), + 'for', + shortenString(data.params.hash), + chainText, + periodText, + ].filter(Boolean).join(' ')); + } + + if (data.type === 'advanced_filters') { + const dateText = data.params.created_at ? `at ${ dayjs(data.params.created_at).format('lll') }` : undefined; + return [ + 'Filtered txs', + chainText, + dateText, + ].filter(Boolean).join(' '); + } + })(); + + const downloadLink = (() => { + if (data.status !== 'completed' || !data.file_id) { + return; + } + + if (isNeedProxy()) { + return `${ (chainData?.app_config || config).apis.general?.endpoint ?? '' }/downloadFile?id=${ data.file_id }`; + } + + return `/downloadFile?id=${ data.file_id }`; + })(); + + return ( + + { data.status === 'pending' ? : } + + + + { title } + + { data.is_highlighted && } + + { description && ( + + { description } + + ) } + { exportDetailsText && ( + + { exportDetailsText } + + ) } + { data.status === 'completed' && data.is_highlighted && ( + + + + ) } + + + ); +}; + +export default React.memo(CsvExportDownloadsItem); diff --git a/client/features/csv-export/mocks/storage.ts b/client/features/csv-export/mocks/storage.ts new file mode 100644 index 0000000000..9652208eb3 --- /dev/null +++ b/client/features/csv-export/mocks/storage.ts @@ -0,0 +1,58 @@ +import type { StorageItem } from '../utils/storage'; + +export const itemCompleted = { + request_id: 'ba3e4b01-7921-4e34-a530-4ac3082b0a92', + file_id: 'IsPUL6HibKAFhrNs6Mwl', + status: 'completed', + expires_at: '2022-11-11T18:22:44Z', + type: 'address_txs', + params: { + from_period: '2026-03-30T18:22:00.000Z', + to_period: '2026-03-31T18:22:00.000Z', + hash: '0x774e9146e400D5cAe26aA1C90eee9225FFC2FF51', + }, + is_highlighted: true, +} satisfies StorageItem; + +export const itemPending = { + request_id: '2e16be21-ac33-40ea-98bf-9c6c4da160f2', + file_id: null, + expires_at: null, + status: 'pending', + type: 'address_token_transfers', + params: { + from_period: '2026-03-30T18:38:00.000Z', + to_period: '2026-03-31T18:38:00.000Z', + hash: '0x774e9146e400D5cAe26aA1C90eee9225FFC2FF51', + filter_type: 'address', + filter_value: 'to', + }, + is_highlighted: false, +} satisfies StorageItem; + +export const itemFailed = { + request_id: '6d3b431f-a339-4f66-99d9-264435cdb895', + file_id: null, + expires_at: null, + status: 'failed', + type: 'advanced_filters', + params: { + transaction_types: 'ERC-20,ERC-721,ERC-1155,ERC-404,contract_creation,contract_interaction', + to_address_hashes_to_include: '0x774e9146e400D5cAe26aA1C90eee9225FFC2FF51', + created_at: '2026-03-31T18:40:09.159Z', + }, + is_highlighted: true, +} satisfies StorageItem; + +export const itemExpired = { + request_id: 'd5daa2af-41a6-4f99-a324-7146994e6f47', + file_id: null, + expires_at: '2022-11-10T18:22:44Z', + status: 'pending', + type: 'token_holders', + params: { + hash: '0x63eD0d9139edAF9d4195a4fA0879cb33C0AB8D70', + token_name: 'R2Credential', + }, + is_highlighted: false, +} satisfies StorageItem; diff --git a/client/features/csv-export/types/api.ts b/client/features/csv-export/types/api.ts new file mode 100644 index 0000000000..8618120448 --- /dev/null +++ b/client/features/csv-export/types/api.ts @@ -0,0 +1,16 @@ +export type CsvExportItemStatus = 'pending' | 'completed' | 'failed'; + +export interface CsvExportItemResponse { + file_id: string | null; + status: CsvExportItemStatus; + expires_at: string | null; +} + +export interface CsvExportDownloadResponse { + request_id: string; +} + +export interface CsvExportConfig { + limit: number; + async_enabled: boolean; +} diff --git a/client/features/csv-export/types/client.ts b/client/features/csv-export/types/client.ts new file mode 100644 index 0000000000..bef3bf6258 --- /dev/null +++ b/client/features/csv-export/types/client.ts @@ -0,0 +1,12 @@ +import type { CsvExportItemStatus } from './api'; + +export type CsvExportType = + 'address_txs' | + 'address_internal_txs' | + 'address_token_transfers' | + 'address_logs' | + 'token_holders' | + 'address_epoch_rewards' | + 'advanced_filters'; + +export type CsvExportDownloadStatus = CsvExportItemStatus | 'expired'; diff --git a/client/features/csv-export/utils/context.tsx b/client/features/csv-export/utils/context.tsx new file mode 100644 index 0000000000..f7f7aa5a26 --- /dev/null +++ b/client/features/csv-export/utils/context.tsx @@ -0,0 +1,139 @@ +import type { UseQueryResult } from '@tanstack/react-query'; +import { queryOptions, useQueries } from '@tanstack/react-query'; +import React from 'react'; + +import type { CsvExportItemResponse } from '../types/api'; + +import useApiFetch from 'client/api/hooks/useApiFetch'; +import { getResourceKey } from 'client/api/hooks/useApiQuery'; + +import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; + +import multichainConfig from 'configs/multichain'; +import dayjs from 'lib/date/dayjs'; +import type { OnOpenChangeHandler } from 'toolkit/hooks/useDisclosure'; +import { useDisclosure } from 'toolkit/hooks/useDisclosure'; +import { SECOND } from 'toolkit/utils/consts'; +import { isBrowser } from 'toolkit/utils/isBrowser'; + +import type { StorageItem } from './storage'; +import * as storage from './storage'; + +interface CsvExportContextProviderProps { + children: React.ReactNode; +} + +export interface TCsvExportContext { + dialogOpen: boolean; + onDialogOpenChange: OnOpenChangeHandler; + items: Array; + addItems: (items: Array) => void; +} + +export const CsvExportContext = React.createContext(null); + +export function CsvExportContextProvider({ children }: CsvExportContextProviderProps) { + const dialog = useDisclosure(); + const apiFetch = useApiFetch(); + + const [ items, setItems ] = React.useState>(isBrowser() ? storage.getItems() : []); + + const queriesOptions = React.useMemo(() => { + const multichain = multichainConfig(); + return items.map((item) => { + const chain = item.params.chain_id ? multichain?.chains.find(({ id }) => id === item.params.chain_id) : undefined; + + return queryOptions({ + queryKey: getResourceKey('general:csv_exports_item', { pathParams: { id: item.request_id }, chainId: chain?.id }), + queryFn: async({ signal }) => { + try { + if (item.status === 'pending') { + const response = await (apiFetch('general:csv_exports_item', { + pathParams: { id: item.request_id }, + fetchParams: { signal }, + chain, + }) as Promise); + if (response.status !== 'pending') { + const newItem = { + ...item, + status: response.status, + expires_at: response.expires_at, + file_id: response.file_id, + is_highlighted: true, + }; + storage.updateItems([ newItem ]); + return newItem; + } + } + + const isExpired = item.status !== 'expired' && item.expires_at && dayjs().isAfter(dayjs(item.expires_at)); + if (isExpired) { + const newItem = { + ...item, + status: 'expired' as const, + is_highlighted: true, + }; + storage.updateItems([ newItem ]); + return newItem; + } + } catch (error) { + const statusCode = getErrorObjStatusCode(error); + if (statusCode === 404) { + const newItem = { + ...item, + status: 'expired' as const, + is_highlighted: true, + }; + storage.updateItems([ newItem ]); + return newItem; + } + } + + return item; + }, + refetchInterval: (query) => { + const status = query.state.data?.status; + return status === 'pending' ? 10 * SECOND : false; + }, + refetchOnMount: false, + }); + }); + }, [ items, apiFetch ]); + + const combineQueriesResult = React.useCallback((results: Array>) => { + return results.map(({ data }) => data).filter(Boolean); + }, []); + + const queriesResult = useQueries({ + queries: queriesOptions, + combine: combineQueriesResult, + }); + + const addItems = React.useCallback((items: Array) => { + setItems((prev) => ([ ...items, ...prev ])); + storage.addItems(items); + }, [ ]); + + const value = React.useMemo(() => { + return { + dialogOpen: dialog.open, + onDialogOpenChange: dialog.onOpenChange, + items: queriesResult, + addItems, + }; + }, [ dialog.open, dialog.onOpenChange, queriesResult, addItems ]); + + return ( + + { children } + + ); +} + +export function useCsvExportContext() { + const context = React.useContext(CsvExportContext); + if (!context) { + throw new Error('useCsvExportContext must be used within a CsvExportContextProvider'); + } + return context; +} diff --git a/client/features/csv-export/utils/get-file-name.ts b/client/features/csv-export/utils/get-file-name.ts new file mode 100644 index 0000000000..f587cfec17 --- /dev/null +++ b/client/features/csv-export/utils/get-file-name.ts @@ -0,0 +1,47 @@ +import type { CsvExportType } from '../types/client'; + +import type config from 'configs/app'; +import dayjs from 'lib/date/dayjs'; + +import getPrefixByFilter from './get-prefix-by-filter'; + +interface Params { + type: CsvExportType; + params: Record; + chainConfig?: typeof config; +} + +export default function getFileName({ type, params, chainConfig }: Params): string { + const chainText = chainConfig?.chain.name ? `${ chainConfig.chain.name.replace(' ', '_').toLowerCase() }` : ''; + + if (type === 'token_holders') { + return [ + chainText, + 'token_holders', + params.hash, + ].filter(Boolean).join('_') + '.csv'; + } + + if (type.startsWith('address_')) { + const dateText = params.from_period && params.to_period ? `from_${ params.from_period }_to_${ params.to_period }` : ''; + const entityPrefix = getPrefixByFilter(params?.filter_type, params?.filter_value); + + return [ + chainText, + entityPrefix, + type, + params.hash, + dateText, + ].filter(Boolean).join('_') + '.csv'; + } + + if (type === 'advanced_filters') { + return [ + chainText, + 'filtered_txs', + dayjs().format('YYYY-MM-DD-HH-mm-ss'), + ].filter(Boolean).join('_') + '.csv'; + } + + return 'data.csv'; +} diff --git a/client/features/csv-export/utils/get-prefix-by-filter.ts b/client/features/csv-export/utils/get-prefix-by-filter.ts new file mode 100644 index 0000000000..814bca3f03 --- /dev/null +++ b/client/features/csv-export/utils/get-prefix-by-filter.ts @@ -0,0 +1,11 @@ +export default function getPrefixByFilter(filterType?: string, filterValue?: string): string { + if (filterType === 'address') { + if (filterValue === 'from') { + return 'outgoing'; + } + if (filterValue === 'to') { + return 'incoming'; + } + } + return ''; +} diff --git a/client/features/csv-export/utils/storage.ts b/client/features/csv-export/utils/storage.ts new file mode 100644 index 0000000000..0c93c4893f --- /dev/null +++ b/client/features/csv-export/utils/storage.ts @@ -0,0 +1,66 @@ +import * as v from 'valibot'; + +export const STORAGE_KEY = 'csv_export_downloads'; +const ITEMS_LIMIT = 20; + +export const itemSchema = v.object({ + request_id: v.string(), + file_id: v.nullable(v.string()), + status: v.union([ v.literal('pending'), v.literal('completed'), v.literal('failed'), v.literal('expired') ]), + expires_at: v.nullable(v.string()), + type: v.union([ + v.literal('address_txs'), + v.literal('address_internal_txs'), + v.literal('address_token_transfers'), + v.literal('address_logs'), + v.literal('token_holders'), + v.literal('address_epoch_rewards'), + v.literal('advanced_filters'), + ]), + params: v.record(v.string(), v.string()), + is_highlighted: v.optional(v.boolean()), +}); + +export type StorageItem = v.InferOutput; + +export function getItems() { + try { + const items = localStorage.getItem(STORAGE_KEY); + if (!items) { + return []; + } + return v.parse(v.array(itemSchema), JSON.parse(items)); + } catch (error) { + return []; + } +} + +export function addItems(items: Array) { + const currentItems = getItems(); + const newItems = [ ...items, ...currentItems ].slice(0, ITEMS_LIMIT); + localStorage.setItem(STORAGE_KEY, JSON.stringify(newItems)); +} + +export function updateItems(items: Array) { + if (items.length === 0) { + return; + } + const currentItems = getItems(); + const newItems = currentItems.map((item) => { + const itemToUpdate = items.find((itemToUpdate) => itemToUpdate.request_id === item.request_id); + if (itemToUpdate) { + return { + ...item, + ...itemToUpdate, + }; + } + return item; + }); + localStorage.setItem(STORAGE_KEY, JSON.stringify(newItems)); +} + +export function removeItem(id: string) { + const items = getItems(); + const newItems = items.filter((item) => item.request_id !== id); + localStorage.setItem(STORAGE_KEY, JSON.stringify(newItems)); +} diff --git a/ui/block/useBlockBlobTxsQuery.tsx b/client/features/data-availability/hooks/useBlockBlobTxsQuery.ts similarity index 86% rename from ui/block/useBlockBlobTxsQuery.tsx rename to client/features/data-availability/hooks/useBlockBlobTxsQuery.ts index be0c819d44..fbb0dde4ee 100644 --- a/ui/block/useBlockBlobTxsQuery.tsx +++ b/client/features/data-availability/hooks/useBlockBlobTxsQuery.ts @@ -1,9 +1,9 @@ -import { TX } from 'stubs/tx'; +import type { BlockQuery } from 'client/slices/block/hooks/useBlockQuery'; +import { TX } from 'client/slices/tx/stubs/tx'; + import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import type { BlockQuery } from './useBlockQuery'; - interface Params { heightOrHash: string; blockQuery: BlockQuery; diff --git a/client/features/data-availability/mocks/block.ts b/client/features/data-availability/mocks/block.ts new file mode 100644 index 0000000000..76d9951b27 --- /dev/null +++ b/client/features/data-availability/mocks/block.ts @@ -0,0 +1,12 @@ +import type { Block } from 'client/slices/block/types/api'; + +import { base } from 'client/slices/block/mocks/block'; + +export const withBlobTxs: Block = { + ...base, + blob_gas_price: '21518435987', + blob_gas_used: '393216', + burnt_blob_fees: '8461393325064192', + excess_blob_gas: '79429632', + blob_transactions_count: 1, +}; diff --git a/client/features/data-availability/mocks/tx.ts b/client/features/data-availability/mocks/tx.ts new file mode 100644 index 0000000000..7e6bb45e62 --- /dev/null +++ b/client/features/data-availability/mocks/tx.ts @@ -0,0 +1,17 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +import { base } from 'client/slices/tx/mocks/tx'; + +export const withBlob: Transaction = { + ...base, + blob_gas_price: '21518435987', + blob_gas_used: '131072', + blob_versioned_hashes: [ + '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09', + '0x0197fdb17195c176b23160f335daabd4b6a231aaaadd73ec567877c66a3affd1', + ], + burnt_blob_fee: '2820464441688064', + max_fee_per_blob_gas: '60000000000', + transaction_types: [ 'blob_transaction' as const ], + type: 3, +}; diff --git a/ui/block/details/BlockDetailsBlobInfo.tsx b/client/features/data-availability/pages/block/BlockDetailsBlobInfo.tsx similarity index 96% rename from ui/block/details/BlockDetailsBlobInfo.tsx rename to client/features/data-availability/pages/block/BlockDetailsBlobInfo.tsx index e09f91a77b..35a35d4a13 100644 --- a/ui/block/details/BlockDetailsBlobInfo.tsx +++ b/client/features/data-availability/pages/block/BlockDetailsBlobInfo.tsx @@ -2,9 +2,10 @@ import { Text } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; + +import { currencyUnits } from 'client/shared/chain/units'; -import { currencyUnits } from 'lib/units'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { ZERO } from 'toolkit/utils/consts'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; diff --git a/ui/tx/blobs/TxBlobListItem.tsx b/client/features/data-availability/pages/tx/TxBlobListItem.tsx similarity index 100% rename from ui/tx/blobs/TxBlobListItem.tsx rename to client/features/data-availability/pages/tx/TxBlobListItem.tsx diff --git a/ui/tx/TxBlobs.pw.tsx b/client/features/data-availability/pages/tx/TxBlobs.pw.tsx similarity index 84% rename from ui/tx/TxBlobs.pw.tsx rename to client/features/data-availability/pages/tx/TxBlobs.pw.tsx index 58d2edfdb6..484080d6ad 100644 --- a/ui/tx/TxBlobs.pw.tsx +++ b/client/features/data-availability/pages/tx/TxBlobs.pw.tsx @@ -1,11 +1,12 @@ import React from 'react'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import * as blobsMock from 'mocks/blobs/blobs'; -import * as txMock from 'mocks/txs/tx'; import { test, expect } from 'playwright/lib'; import TxBlobs from './TxBlobs'; -import type { TxQuery } from './useTxQuery'; const hooksConfig = { router: { diff --git a/ui/tx/TxBlobs.tsx b/client/features/data-availability/pages/tx/TxBlobs.tsx similarity index 86% rename from ui/tx/TxBlobs.tsx rename to client/features/data-availability/pages/tx/TxBlobs.tsx index d3b83cd249..d132b3b55e 100644 --- a/ui/tx/TxBlobs.tsx +++ b/client/features/data-availability/pages/tx/TxBlobs.tsx @@ -1,6 +1,10 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import { TX_BLOB } from 'stubs/blobs'; import { generateListStub } from 'stubs/utils'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; @@ -8,11 +12,8 @@ import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import TxBlobsList from './blobs/TxBlobsList'; -import TxBlobsTable from './blobs/TxBlobsTable'; -import TxPendingAlert from './TxPendingAlert'; -import TxSocketAlert from './TxSocketAlert'; -import type { TxQuery } from './useTxQuery'; +import TxBlobsList from './TxBlobsList'; +import TxBlobsTable from './TxBlobsTable'; interface Props { txQuery: TxQuery; diff --git a/ui/tx/blobs/TxBlobsList.tsx b/client/features/data-availability/pages/tx/TxBlobsList.tsx similarity index 100% rename from ui/tx/blobs/TxBlobsList.tsx rename to client/features/data-availability/pages/tx/TxBlobsList.tsx diff --git a/ui/tx/blobs/TxBlobsTable.tsx b/client/features/data-availability/pages/tx/TxBlobsTable.tsx similarity index 100% rename from ui/tx/blobs/TxBlobsTable.tsx rename to client/features/data-availability/pages/tx/TxBlobsTable.tsx diff --git a/ui/tx/blobs/TxBlobsTableItem.tsx b/client/features/data-availability/pages/tx/TxBlobsTableItem.tsx similarity index 100% rename from ui/tx/blobs/TxBlobsTableItem.tsx rename to client/features/data-availability/pages/tx/TxBlobsTableItem.tsx diff --git a/ui/tx/__screenshots__/TxBlobs.pw.tsx_default_base-view-mobile-1.png b/client/features/data-availability/pages/tx/__screenshots__/TxBlobs.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxBlobs.pw.tsx_default_base-view-mobile-1.png rename to client/features/data-availability/pages/tx/__screenshots__/TxBlobs.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/tx/__screenshots__/TxBlobs.pw.tsx_mobile_base-view-mobile-1.png b/client/features/data-availability/pages/tx/__screenshots__/TxBlobs.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxBlobs.pw.tsx_mobile_base-view-mobile-1.png rename to client/features/data-availability/pages/tx/__screenshots__/TxBlobs.pw.tsx_mobile_base-view-mobile-1.png diff --git a/client/features/data-availability/types/api.ts b/client/features/data-availability/types/api.ts new file mode 100644 index 0000000000..cc34642cec --- /dev/null +++ b/client/features/data-availability/types/api.ts @@ -0,0 +1,30 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +export interface TransactionDataAvailability { + blob_versioned_hashes?: Array; + blob_gas_used?: string; + blob_gas_price?: string; + burnt_blob_fee?: string; + max_fee_per_blob_gas?: string; +} + +export interface TransactionsResponseWithBlobs { + items: Array; + next_page_params: { + block_number: number; + index: number; + items_count: number; + } | null; +} + +export type TxsWithBlobsFilters = { + type: 'blob_transaction'; +}; + +export interface BlockDataAvailability { + blob_gas_price?: string; + blob_gas_used?: string; + burnt_blob_fees?: string; + excess_blob_gas?: string; + blob_transactions_count?: number; +} diff --git a/ui/tx/TxExternalTxs.pw.tsx b/client/features/external-txs/components/TxExternalTxs.pw.tsx similarity index 100% rename from ui/tx/TxExternalTxs.pw.tsx rename to client/features/external-txs/components/TxExternalTxs.pw.tsx diff --git a/ui/tx/TxExternalTxs.tsx b/client/features/external-txs/components/TxExternalTxs.tsx similarity index 96% rename from ui/tx/TxExternalTxs.tsx rename to client/features/external-txs/components/TxExternalTxs.tsx index 53fb1c20df..c99f6d981a 100644 --- a/ui/tx/TxExternalTxs.tsx +++ b/client/features/external-txs/components/TxExternalTxs.tsx @@ -1,11 +1,12 @@ import { Box, Flex } from '@chakra-ui/react'; import React from 'react'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import config from 'configs/app'; import { Image } from 'toolkit/chakra/image'; import { Link } from 'toolkit/chakra/link'; import { Tooltip } from 'toolkit/chakra/tooltip'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; const externalTxFeature = config.features.externalTxs; diff --git a/ui/tx/__screenshots__/TxExternalTxs.pw.tsx_default_base-view-1.png b/client/features/external-txs/components/__screenshots__/TxExternalTxs.pw.tsx_default_base-view-1.png similarity index 100% rename from ui/tx/__screenshots__/TxExternalTxs.pw.tsx_default_base-view-1.png rename to client/features/external-txs/components/__screenshots__/TxExternalTxs.pw.tsx_default_base-view-1.png diff --git a/ui/tx/TxFHEOperations.tsx b/client/features/fhe-operations/pages/tx/TxFheOperations.tsx similarity index 79% rename from ui/tx/TxFHEOperations.tsx rename to client/features/fhe-operations/pages/tx/TxFheOperations.tsx index b2f7140fb9..3a6af1ab87 100644 --- a/ui/tx/TxFHEOperations.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperations.tsx @@ -1,17 +1,19 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import { FHE_OPERATIONS_RESPONSE } from 'stubs/fheOperations'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataListDisplay from 'ui/shared/DataListDisplay'; -import TxFHEOperationsList from 'ui/tx/fheOperations/TxFHEOperationsList'; -import TxFHEOperationsStats from 'ui/tx/fheOperations/TxFHEOperationsStats'; -import TxFHEOperationsTable from 'ui/tx/fheOperations/TxFHEOperationsTable'; -import TxPendingAlert from 'ui/tx/TxPendingAlert'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; -import type { TxQuery } from './useTxQuery'; +import TxFHEOperationsList from './TxFheOperationsList'; +import TxFHEOperationsStats from './TxFheOperationsStats'; +import TxFHEOperationsTable from './TxFheOperationsTable'; interface Props { txQuery: TxQuery; diff --git a/ui/tx/fheOperations/TxFHEOperationsList.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsList.tsx similarity index 86% rename from ui/tx/fheOperations/TxFHEOperationsList.tsx rename to client/features/fhe-operations/pages/tx/TxFheOperationsList.tsx index fc8417a102..ec0e832260 100644 --- a/ui/tx/fheOperations/TxFHEOperationsList.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsList.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type { FheOperation } from 'types/api/fheOperations'; -import TxFHEOperationsListItem from 'ui/tx/fheOperations/TxFHEOperationsListItem'; +import TxFHEOperationsListItem from './TxFheOperationsListItem'; interface Props { data: Array; diff --git a/ui/tx/fheOperations/TxFHEOperationsListItem.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsListItem.tsx similarity index 94% rename from ui/tx/fheOperations/TxFHEOperationsListItem.tsx rename to client/features/fhe-operations/pages/tx/TxFheOperationsListItem.tsx index 8bee80f425..bec1ad6f8e 100644 --- a/ui/tx/fheOperations/TxFHEOperationsListItem.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsListItem.tsx @@ -4,11 +4,13 @@ import React from 'react'; import type { FheOperation } from 'types/api/fheOperations'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import { getTypeColor } from 'ui/tx/fheOperations/utils'; + +import { getTypeColor } from '../../utils/utils'; type Props = FheOperation & { isLoading?: boolean }; diff --git a/ui/tx/fheOperations/TxFHEOperationsStats.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsStats.tsx similarity index 100% rename from ui/tx/fheOperations/TxFHEOperationsStats.tsx rename to client/features/fhe-operations/pages/tx/TxFheOperationsStats.tsx diff --git a/ui/tx/fheOperations/TxFHEOperationsTable.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsTable.tsx similarity index 90% rename from ui/tx/fheOperations/TxFHEOperationsTable.tsx rename to client/features/fhe-operations/pages/tx/TxFheOperationsTable.tsx index 9ee8a91ad7..7526af64fe 100644 --- a/ui/tx/fheOperations/TxFHEOperationsTable.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsTable.tsx @@ -3,9 +3,11 @@ import React from 'react'; import type { FheOperation } from 'types/api/fheOperations'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { TableBody, TableColumnHeader, TableHeader, TableRoot, TableRow } from 'toolkit/chakra/table'; -import TxFHEOperationsTableItem from 'ui/tx/fheOperations/TxFHEOperationsTableItem'; + +import TxFHEOperationsTableItem from './TxFheOperationsTableItem'; interface Props { data: Array; diff --git a/ui/tx/fheOperations/TxFHEOperationsTableItem.tsx b/client/features/fhe-operations/pages/tx/TxFheOperationsTableItem.tsx similarity index 94% rename from ui/tx/fheOperations/TxFHEOperationsTableItem.tsx rename to client/features/fhe-operations/pages/tx/TxFheOperationsTableItem.tsx index 88dff37714..df89b36ac0 100644 --- a/ui/tx/fheOperations/TxFHEOperationsTableItem.tsx +++ b/client/features/fhe-operations/pages/tx/TxFheOperationsTableItem.tsx @@ -4,11 +4,13 @@ import React from 'react'; import type { FheOperation } from 'types/api/fheOperations'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import { getTypeColor } from 'ui/tx/fheOperations/utils'; + +import { getTypeColor } from '../../utils/utils'; type Props = FheOperation & { isLoading?: boolean }; diff --git a/client/features/fhe-operations/types/api.ts b/client/features/fhe-operations/types/api.ts new file mode 100644 index 0000000000..5fddaf371e --- /dev/null +++ b/client/features/fhe-operations/types/api.ts @@ -0,0 +1,3 @@ +export interface TransactionFheOperations { + fhe_operations_count?: number; +} diff --git a/ui/tx/fheOperations/utils.ts b/client/features/fhe-operations/utils/utils.ts similarity index 100% rename from ui/tx/fheOperations/utils.ts rename to client/features/fhe-operations/utils/utils.ts diff --git a/ui/blocks/flashblocks/useFlashblocksSocketData.ts b/client/features/flashblocks/hooks/useFlashblocksSocketData.ts similarity index 96% rename from ui/blocks/flashblocks/useFlashblocksSocketData.ts rename to client/features/flashblocks/hooks/useFlashblocksSocketData.ts index d8cc12d790..c32a411d2e 100644 --- a/ui/blocks/flashblocks/useFlashblocksSocketData.ts +++ b/client/features/flashblocks/hooks/useFlashblocksSocketData.ts @@ -2,12 +2,12 @@ import React from 'react'; import type { FlashblockItem } from 'types/client/flashblocks'; +import { formatFlashblockItemMegaEth, formatFlashblockItemOptimism } from 'client/features/flashblocks/utils/format-flashblock-item'; +import { parseSocketEventDataMegaEth, parseSocketEventDataOptimism } from 'client/features/flashblocks/utils/parse-socket-event-data'; + import config from 'configs/app'; import { SECOND } from 'toolkit/utils/consts'; -import { formatFlashblockItemMegaEth, formatFlashblockItemOptimism } from './formatFlashblockItem'; -import { parseSocketEventDataMegaEth, parseSocketEventDataOptimism } from './parseSocketEventData'; - const flashblocksFeature = config.features.flashblocks; const MAX_FLASHBLOCKS_COUNT = 50; diff --git a/ui/blocks/Flashblocks.tsx b/client/features/flashblocks/pages/index/Flashblocks.tsx similarity index 88% rename from ui/blocks/Flashblocks.tsx rename to client/features/flashblocks/pages/index/Flashblocks.tsx index 74541cde13..bfb5269b0e 100644 --- a/ui/blocks/Flashblocks.tsx +++ b/client/features/flashblocks/pages/index/Flashblocks.tsx @@ -1,14 +1,15 @@ import { Box, HStack } from '@chakra-ui/react'; import React from 'react'; +import useFlashblocksSocketData from 'client/features/flashblocks/hooks/useFlashblocksSocketData'; + import config from 'configs/app'; import { Switch } from 'toolkit/chakra/switch'; import { Hint } from 'toolkit/components/Hint/Hint'; -import FlashblocksList from './flashblocks/FlashblocksList'; -import FlashblocksStats from './flashblocks/FlashblocksStats'; -import FlashblocksTable from './flashblocks/FlashblocksTable'; -import useFlashblocksSocketData from './flashblocks/useFlashblocksSocketData'; +import FlashblocksList from './FlashblocksList'; +import FlashblocksStats from './FlashblocksStats'; +import FlashblocksTable from './FlashblocksTable'; const flashblocksFeature = config.features.flashblocks; diff --git a/ui/blocks/flashblocks/FlashblocksList.tsx b/client/features/flashblocks/pages/index/FlashblocksList.tsx similarity index 100% rename from ui/blocks/flashblocks/FlashblocksList.tsx rename to client/features/flashblocks/pages/index/FlashblocksList.tsx diff --git a/ui/blocks/flashblocks/FlashblocksListItem.tsx b/client/features/flashblocks/pages/index/FlashblocksListItem.tsx similarity index 100% rename from ui/blocks/flashblocks/FlashblocksListItem.tsx rename to client/features/flashblocks/pages/index/FlashblocksListItem.tsx diff --git a/ui/blocks/flashblocks/FlashblocksStats.tsx b/client/features/flashblocks/pages/index/FlashblocksStats.tsx similarity index 100% rename from ui/blocks/flashblocks/FlashblocksStats.tsx rename to client/features/flashblocks/pages/index/FlashblocksStats.tsx diff --git a/ui/blocks/flashblocks/FlashblocksTable.tsx b/client/features/flashblocks/pages/index/FlashblocksTable.tsx similarity index 100% rename from ui/blocks/flashblocks/FlashblocksTable.tsx rename to client/features/flashblocks/pages/index/FlashblocksTable.tsx diff --git a/ui/blocks/flashblocks/FlashblocksTableItem.tsx b/client/features/flashblocks/pages/index/FlashblocksTableItem.tsx similarity index 100% rename from ui/blocks/flashblocks/FlashblocksTableItem.tsx rename to client/features/flashblocks/pages/index/FlashblocksTableItem.tsx diff --git a/ui/blocks/flashblocks/formatFlashblockItem.tsx b/client/features/flashblocks/utils/format-flashblock-item.ts similarity index 100% rename from ui/blocks/flashblocks/formatFlashblockItem.tsx rename to client/features/flashblocks/utils/format-flashblock-item.ts diff --git a/ui/blocks/flashblocks/parseSocketEventData.tsx b/client/features/flashblocks/utils/parse-socket-event-data.ts similarity index 100% rename from ui/blocks/flashblocks/parseSocketEventData.tsx rename to client/features/flashblocks/utils/parse-socket-event-data.ts diff --git a/ui/address/details/AddressMultichainButton.module.css b/client/features/multichain-button/pages/address/AddressMultichainButton.module.css similarity index 100% rename from ui/address/details/AddressMultichainButton.module.css rename to client/features/multichain-button/pages/address/AddressMultichainButton.module.css diff --git a/ui/address/details/AddressMultichainButton.tsx b/client/features/multichain-button/pages/address/AddressMultichainButton.tsx similarity index 100% rename from ui/address/details/AddressMultichainButton.tsx rename to client/features/multichain-button/pages/address/AddressMultichainButton.tsx diff --git a/ui/address/AddressMultichainInfoButton.tsx b/client/features/multichain-button/pages/address/AddressMultichainInfoButton.tsx similarity index 95% rename from ui/address/AddressMultichainInfoButton.tsx rename to client/features/multichain-button/pages/address/AddressMultichainInfoButton.tsx index 2adbfe39a0..6aa37315e2 100644 --- a/ui/address/AddressMultichainInfoButton.tsx +++ b/client/features/multichain-button/pages/address/AddressMultichainInfoButton.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import config from 'configs/app'; import type { LinkProps } from 'toolkit/chakra/link'; diff --git a/ui/address/clusters/AddressClusters.tsx b/client/features/name-services/clusters/pages/address/AddressClusters.tsx similarity index 98% rename from ui/address/clusters/AddressClusters.tsx rename to client/features/name-services/clusters/pages/address/AddressClusters.tsx index 9a06abe00e..a5cbca610a 100644 --- a/ui/address/clusters/AddressClusters.tsx +++ b/client/features/name-services/clusters/pages/address/AddressClusters.tsx @@ -6,7 +6,8 @@ import type { ClustersByAddressResponse } from 'types/api/clusters'; import { route } from 'nextjs-routes'; -import type { ResourceError } from 'lib/api/resources'; +import type { ResourceError } from 'client/api/resources'; + import { filterOwnedClusters, getTotalRecordsDisplay, diff --git a/ui/address/utils/useCheckDomainNameParam.ts b/client/features/name-services/domains/hooks/useCheckDomainNameParam.ts similarity index 96% rename from ui/address/utils/useCheckDomainNameParam.ts rename to client/features/name-services/domains/hooks/useCheckDomainNameParam.ts index 36c0518b27..a23d54165e 100644 --- a/ui/address/utils/useCheckDomainNameParam.ts +++ b/client/features/name-services/domains/hooks/useCheckDomainNameParam.ts @@ -1,8 +1,9 @@ import { useRouter } from 'next/router'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; const DOMAIN_NAME_REGEXP = /.\../; diff --git a/ui/address/ensDomains/AddressEnsDomains.pw.tsx b/client/features/name-services/domains/pages/address/AddressEnsDomains.pw.tsx similarity index 95% rename from ui/address/ensDomains/AddressEnsDomains.pw.tsx rename to client/features/name-services/domains/pages/address/AddressEnsDomains.pw.tsx index 342073a84c..a19e9d2fbc 100644 --- a/ui/address/ensDomains/AddressEnsDomains.pw.tsx +++ b/client/features/name-services/domains/pages/address/AddressEnsDomains.pw.tsx @@ -3,7 +3,8 @@ import React from 'react'; import type * as bens from '@blockscout/bens-types'; -import type { ResourceError } from 'lib/api/resources'; +import type { ResourceError } from 'client/api/resources'; + import * as ensDomainMock from 'mocks/ens/domain'; import { test, expect } from 'playwright/lib'; diff --git a/ui/address/ensDomains/AddressEnsDomains.tsx b/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx similarity index 98% rename from ui/address/ensDomains/AddressEnsDomains.tsx rename to client/features/name-services/domains/pages/address/AddressEnsDomains.tsx index 66675fb915..da3c52478e 100644 --- a/ui/address/ensDomains/AddressEnsDomains.tsx +++ b/client/features/name-services/domains/pages/address/AddressEnsDomains.tsx @@ -7,7 +7,8 @@ import type * as bens from '@blockscout/bens-types'; import { route } from 'nextjs-routes'; -import type { ResourceError } from 'lib/api/resources'; +import type { ResourceError } from 'client/api/resources'; + import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; diff --git a/ui/address/ensDomains/__screenshots__/AddressEnsDomains.pw.tsx_default_base-view-1.png b/client/features/name-services/domains/pages/address/__screenshots__/AddressEnsDomains.pw.tsx_default_base-view-1.png similarity index 100% rename from ui/address/ensDomains/__screenshots__/AddressEnsDomains.pw.tsx_default_base-view-1.png rename to client/features/name-services/domains/pages/address/__screenshots__/AddressEnsDomains.pw.tsx_default_base-view-1.png diff --git a/ui/shared/entities/address/AddressEntityInterop.pw.tsx b/client/features/op-interop/components/AddressEntityInterop.pw.tsx similarity index 100% rename from ui/shared/entities/address/AddressEntityInterop.pw.tsx rename to client/features/op-interop/components/AddressEntityInterop.pw.tsx diff --git a/ui/shared/entities/address/AddressEntityInterop.tsx b/client/features/op-interop/components/AddressEntityInterop.tsx similarity index 94% rename from ui/shared/entities/address/AddressEntityInterop.tsx rename to client/features/op-interop/components/AddressEntityInterop.tsx index c3127fba9a..7d480b1b44 100644 --- a/ui/shared/entities/address/AddressEntityInterop.tsx +++ b/client/features/op-interop/components/AddressEntityInterop.tsx @@ -5,13 +5,13 @@ import type { ChainInfo } from 'types/api/interop'; import { route } from 'nextjs-routes'; +import * as AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Image } from 'toolkit/chakra/image'; import { Tooltip } from 'toolkit/chakra/tooltip'; +import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import IconSvg from 'ui/shared/IconSvg'; -import { distributeEntityProps } from '../base/utils'; -import * as AddressEntity from './AddressEntity'; - interface Props extends Omit { chain: ChainInfo | null; } diff --git a/ui/shared/entities/tx/TxEntityInterop.pw.tsx b/client/features/op-interop/components/TxEntityInterop.pw.tsx similarity index 100% rename from ui/shared/entities/tx/TxEntityInterop.pw.tsx rename to client/features/op-interop/components/TxEntityInterop.pw.tsx diff --git a/ui/shared/entities/tx/TxEntityInterop.tsx b/client/features/op-interop/components/TxEntityInterop.tsx similarity index 95% rename from ui/shared/entities/tx/TxEntityInterop.tsx rename to client/features/op-interop/components/TxEntityInterop.tsx index aa2a1016c1..d1724fa153 100644 --- a/ui/shared/entities/tx/TxEntityInterop.tsx +++ b/client/features/op-interop/components/TxEntityInterop.tsx @@ -5,15 +5,15 @@ import type { ChainInfo } from 'types/api/interop'; import { route } from 'nextjs-routes'; +import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import { Image } from 'toolkit/chakra/image'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { stripTrailingSlash } from 'toolkit/utils/url'; +import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import IconSvg from 'ui/shared/IconSvg'; -import { distributeEntityProps } from '../base/utils'; -import * as TxEntity from './TxEntity'; - type Props = { chain?: ChainInfo | null; hash?: string | null; diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png b/client/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png rename to client/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-1.png b/client/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-1.png rename to client/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png b/client/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png rename to client/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png b/client/features/op-interop/components/__screenshots__/TxEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png rename to client/features/op-interop/components/__screenshots__/TxEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-1.png b/client/features/op-interop/components/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-1.png rename to client/features/op-interop/components/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png b/client/features/op-interop/components/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png rename to client/features/op-interop/components/__screenshots__/TxEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png diff --git a/client/features/op-interop/mocks/tx.ts b/client/features/op-interop/mocks/tx.ts new file mode 100644 index 0000000000..e40cb64609 --- /dev/null +++ b/client/features/op-interop/mocks/tx.ts @@ -0,0 +1,33 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +import { base } from 'client/slices/tx/mocks/tx'; + +import * as addressMock from 'mocks/address/address'; +import * as interopMock from 'mocks/interop/interop'; + +export const withInteropInMessage: Transaction = { + ...base, + op_interop_messages: [ { + init_chain: interopMock.chain, + nonce: 1, + payload: '0x', + init_transaction_hash: '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09', + sender_address_hash: addressMock.hash, + status: 'Sent', + target_address_hash: addressMock.hash, + } ], +}; + +export const withInteropOutMessage: Transaction = { + ...base, + op_interop_messages: [ { + relay_chain: interopMock.chain, + nonce: 1, + // eslint-disable-next-line max-len + payload: '0xfa4b78b90000000000000000000000000000000000000000000000000000000005001bcfe835d1028984e9e6e7d016b77164eacbcc6cc061e9333c0b37982b504f7ea791000000000000000000000000a79b29ad7e0196c95b87f4663ded82fbf2e3add8', + relay_transaction_hash: '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09', + sender_address_hash: addressMock.hash, + status: 'Sent', + target_address_hash: addressMock.hash, + } ], +}; diff --git a/ui/tx/details/TxDetailsInterop.tsx b/client/features/op-interop/pages/tx/TxDetailsInterop.tsx similarity index 93% rename from ui/tx/details/TxDetailsInterop.tsx rename to client/features/op-interop/pages/tx/TxDetailsInterop.tsx index 6774dfa55e..82bbb9fcd5 100644 --- a/ui/tx/details/TxDetailsInterop.tsx +++ b/client/features/op-interop/pages/tx/TxDetailsInterop.tsx @@ -1,7 +1,11 @@ import { Grid, Text, Flex, Box } from '@chakra-ui/react'; import React from 'react'; -import type { InteropTransactionInfo } from 'types/api/transaction'; +import type { InteropTransactionInfo } from 'client/features/op-interop/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import AddressEntityInterop from 'client/features/op-interop/components/AddressEntityInterop'; import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; @@ -10,8 +14,6 @@ import InteropMessageDestinationTx from 'ui/interopMessages/InteropMessageDestin import InteropMessageSourceTx from 'ui/interopMessages/InteropMessageSourceTx'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import AddressEntityInterop from 'ui/shared/entities/address/AddressEntityInterop'; import InteropMessageStatus from 'ui/shared/statusTag/InteropMessageStatus'; const rollupFeature = config.features.rollup; diff --git a/client/features/op-interop/types/api.ts b/client/features/op-interop/types/api.ts new file mode 100644 index 0000000000..cd58bed572 --- /dev/null +++ b/client/features/op-interop/types/api.ts @@ -0,0 +1,17 @@ +import type { ChainInfo, MessageStatus } from 'types/api/interop'; + +export interface InteropTransactionInfo { + nonce: number; + payload: string; + init_chain?: ChainInfo | null; + relay_chain?: ChainInfo | null; + init_transaction_hash?: string; + relay_transaction_hash?: string; + sender_address_hash: string; + status: MessageStatus; + target_address_hash: string; +} + +export interface TransactionOpInterop { + op_interop_messages?: Array; +} diff --git a/client/features/rollup/arbitrum/mocks/tx.ts b/client/features/rollup/arbitrum/mocks/tx.ts new file mode 100644 index 0000000000..39813c1bdd --- /dev/null +++ b/client/features/rollup/arbitrum/mocks/tx.ts @@ -0,0 +1,30 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +import { base } from 'client/slices/tx/mocks/tx'; + +export const arbitrumTxn: Transaction = { + ...base, + arbitrum: { + batch_number: 743991, + commitment_transaction: { + hash: '0x71a25e01dde129a308704de217d200ea42e0f5b8c221c8ba8b2b680ff347f708', + status: 'unfinalized', + timestamp: '2024-11-19T14:26:23.000000Z', + }, + confirmation_transaction: { + hash: null, + status: null, + timestamp: null, + }, + contains_message: null, + gas_used_for_l1: '129773', + gas_used_for_l2: '128313', + message_related_info: { + associated_l1_transaction_hash: null, + message_status: 'Relayed', + }, + network_fee: '1283130000000', + poster_fee: '1297730000000', + status: 'Sent to base', + }, +}; diff --git a/ui/tx/details/TxDetailsWithdrawalStatusArbitrum.tsx b/client/features/rollup/arbitrum/pages/tx/TxDetailsWithdrawalStatusArbitrum.tsx similarity index 92% rename from ui/tx/details/TxDetailsWithdrawalStatusArbitrum.tsx rename to client/features/rollup/arbitrum/pages/tx/TxDetailsWithdrawalStatusArbitrum.tsx index 4616a3ebf5..625607fe63 100644 --- a/ui/tx/details/TxDetailsWithdrawalStatusArbitrum.tsx +++ b/client/features/rollup/arbitrum/pages/tx/TxDetailsWithdrawalStatusArbitrum.tsx @@ -1,14 +1,16 @@ import { Text } from '@chakra-ui/react'; import React from 'react'; -import type { ArbitrumTransactionMessageStatus, Transaction } from 'types/api/transaction'; +import type { ArbitrumTransactionMessageStatus } from 'client/features/rollup/arbitrum/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; import { route } from 'nextjs-routes'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import { layerLabels } from 'lib/rollups/utils'; import { Link } from 'toolkit/chakra/link'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps'; const WITHDRAWAL_STATUS_STEPS: Array = [ diff --git a/client/features/rollup/arbitrum/types/api.ts b/client/features/rollup/arbitrum/types/api.ts new file mode 100644 index 0000000000..5dd17d8f28 --- /dev/null +++ b/client/features/rollup/arbitrum/types/api.ts @@ -0,0 +1,36 @@ +import type { ArbitrumBatchStatus, ArbitrumL2TxData } from 'types/api/arbitrumL2'; + +export type ArbitrumTransactionMessageStatus = 'Relayed' | 'Syncing with base layer' | 'Waiting for confirmation' | 'Ready for relay' | 'Settlement pending'; + +export interface TransactionArbitrum { + arbitrum?: { + batch_number: number; + commitment_transaction: ArbitrumL2TxData; + confirmation_transaction: ArbitrumL2TxData; + contains_message: 'incoming' | 'outcoming' | null; + gas_used_for_l1: string; + gas_used_for_l2: string; + network_fee: string; + poster_fee: string; + status: ArbitrumBatchStatus; + message_related_info: { + associated_l1_transaction_hash: string | null; + message_status: ArbitrumTransactionMessageStatus; + }; + }; +} + +export interface BlockArbitrum { + arbitrum?: ArbitrumBlockData; +} + +export type ArbitrumBlockData = { + batch_number: number; + commitment_transaction: ArbitrumL2TxData; + confirmation_transaction: ArbitrumL2TxData; + delayed_messages: number; + l1_block_number: number; + send_count: number | null; + send_root: string; + status: ArbitrumBatchStatus; +}; diff --git a/ui/shared/entities/address/AddressEntityL1.tsx b/client/features/rollup/common/components/AddressEntityL1.tsx similarity index 88% rename from ui/shared/entities/address/AddressEntityL1.tsx rename to client/features/rollup/common/components/AddressEntityL1.tsx index cde9f3c6ea..fe45d776e3 100644 --- a/ui/shared/entities/address/AddressEntityL1.tsx +++ b/client/features/rollup/common/components/AddressEntityL1.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import config from 'configs/app'; +import * as AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import * as AddressEntity from './AddressEntity'; +import config from 'configs/app'; const rollupFeature = config.features.rollup; diff --git a/ui/shared/entities/block/BatchEntityL2.tsx b/client/features/rollup/common/components/BatchEntityL2.tsx similarity index 88% rename from ui/shared/entities/block/BatchEntityL2.tsx rename to client/features/rollup/common/components/BatchEntityL2.tsx index 783f54f7d5..d312faed42 100644 --- a/ui/shared/entities/block/BatchEntityL2.tsx +++ b/client/features/rollup/common/components/BatchEntityL2.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import config from 'configs/app'; +import * as BlockEntity from 'client/slices/block/components/entity/BlockEntity'; -import * as BlockEntity from './BlockEntity'; +import config from 'configs/app'; const rollupFeature = config.features.rollup; diff --git a/ui/shared/entities/block/BlockEntityL1.tsx b/client/features/rollup/common/components/BlockEntityL1.tsx similarity index 88% rename from ui/shared/entities/block/BlockEntityL1.tsx rename to client/features/rollup/common/components/BlockEntityL1.tsx index 5404bad97a..28840990c7 100644 --- a/ui/shared/entities/block/BlockEntityL1.tsx +++ b/client/features/rollup/common/components/BlockEntityL1.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import config from 'configs/app'; +import * as BlockEntity from 'client/slices/block/components/entity/BlockEntity'; -import * as BlockEntity from './BlockEntity'; +import config from 'configs/app'; const rollupFeature = config.features.rollup; diff --git a/ui/shared/entities/block/BlockEntityL2.tsx b/client/features/rollup/common/components/BlockEntityL2.tsx similarity index 82% rename from ui/shared/entities/block/BlockEntityL2.tsx rename to client/features/rollup/common/components/BlockEntityL2.tsx index 6cbf03393f..6e4959b9c3 100644 --- a/ui/shared/entities/block/BlockEntityL2.tsx +++ b/client/features/rollup/common/components/BlockEntityL2.tsx @@ -1,9 +1,9 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; -import config from 'configs/app'; +import * as BlockEntity from 'client/slices/block/components/entity/BlockEntity'; -import * as BlockEntity from './BlockEntity'; +import config from 'configs/app'; const rollupFeature = config.features.rollup; diff --git a/ui/shared/entities/tx/TxEntityL1.tsx b/client/features/rollup/common/components/TxEntityL1.tsx similarity index 88% rename from ui/shared/entities/tx/TxEntityL1.tsx rename to client/features/rollup/common/components/TxEntityL1.tsx index 95f364d818..18e931d75c 100644 --- a/ui/shared/entities/tx/TxEntityL1.tsx +++ b/client/features/rollup/common/components/TxEntityL1.tsx @@ -3,9 +3,9 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import config from 'configs/app'; +import * as TxEntity from 'client/slices/tx/components/entity/TxEntity'; -import * as TxEntity from './TxEntity'; +import config from 'configs/app'; const rollupFeature = config.features.rollup; diff --git a/client/features/rollup/common/mocks/tx.ts b/client/features/rollup/common/mocks/tx.ts new file mode 100644 index 0000000000..599898103f --- /dev/null +++ b/client/features/rollup/common/mocks/tx.ts @@ -0,0 +1,12 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +import { base } from 'client/slices/tx/mocks/tx'; + +export const l2tx: Transaction = { + ...base, + l1_gas_price: '82702201886', + l1_fee_scalar: '1.0', + l1_gas_used: '17060', + l1_fee: '1584574188135760', + operator_fee: '2769347953', +}; diff --git a/client/features/rollup/common/types/api.ts b/client/features/rollup/common/types/api.ts new file mode 100644 index 0000000000..9f9cc6ca8a --- /dev/null +++ b/client/features/rollup/common/types/api.ts @@ -0,0 +1,6 @@ +export interface TransactionRollup { + l1_fee?: string; + l1_fee_scalar?: string; + l1_gas_price?: string; + l1_gas_used?: string; +} diff --git a/ui/tx/details/TxDetailsWithdrawalStatusOptimistic.pw.tsx b/client/features/rollup/optimistic/pages/tx/TxDetailsWithdrawalStatusOptimistic.pw.tsx similarity index 97% rename from ui/tx/details/TxDetailsWithdrawalStatusOptimistic.pw.tsx rename to client/features/rollup/optimistic/pages/tx/TxDetailsWithdrawalStatusOptimistic.pw.tsx index 8f868bb4a2..157ed25bb8 100644 --- a/ui/tx/details/TxDetailsWithdrawalStatusOptimistic.pw.tsx +++ b/client/features/rollup/optimistic/pages/tx/TxDetailsWithdrawalStatusOptimistic.pw.tsx @@ -1,7 +1,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { OpWithdrawal } from 'types/api/transaction'; +import type { OpWithdrawal } from 'client/features/rollup/optimistic/types/api'; import * as addressMock from 'mocks/address/address'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; diff --git a/ui/tx/details/TxDetailsWithdrawalStatusOptimistic.tsx b/client/features/rollup/optimistic/pages/tx/TxDetailsWithdrawalStatusOptimistic.tsx similarity index 90% rename from ui/tx/details/TxDetailsWithdrawalStatusOptimistic.tsx rename to client/features/rollup/optimistic/pages/tx/TxDetailsWithdrawalStatusOptimistic.tsx index bb24a5ea6e..00deb68618 100644 --- a/ui/tx/details/TxDetailsWithdrawalStatusOptimistic.tsx +++ b/client/features/rollup/optimistic/pages/tx/TxDetailsWithdrawalStatusOptimistic.tsx @@ -1,13 +1,16 @@ import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; -import type { AddressParam } from 'types/api/addressParams'; +import type { OpWithdrawal } from 'client/features/rollup/optimistic/types/api'; +import type { AddressParam } from 'client/slices/address/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; import type { OptimisticL2WithdrawalStatus } from 'types/api/optimisticL2'; -import type { OpWithdrawal, Transaction } from 'types/api/transaction'; + +import { getResourceKey } from 'client/api/hooks/useApiQuery'; + +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; import config from 'configs/app'; -import { getResourceKey } from 'lib/api/useApiQuery'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps'; import OptimisticL2ClaimButton, { canClaimDirectlyGuard } from 'ui/withdrawals/optimisticL2/OptimisticL2ClaimButton'; diff --git a/ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-with-claim-button-1.png b/client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-with-claim-button-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-with-claim-button-1.png rename to client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-with-claim-button-1.png diff --git a/ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-without-claim-button-1.png b/client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-without-claim-button-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-without-claim-button-1.png rename to client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Ready-for-relay-without-claim-button-1.png diff --git a/ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Relayed-1.png b/client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Relayed-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Relayed-1.png rename to client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Relayed-1.png diff --git a/ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Waiting-for-state-root-1.png b/client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Waiting-for-state-root-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Waiting-for-state-root-1.png rename to client/features/rollup/optimistic/pages/tx/__screenshots__/TxDetailsWithdrawalStatusOptimistic.pw.tsx_default_status-Waiting-for-state-root-1.png diff --git a/client/features/rollup/optimistic/types/api.ts b/client/features/rollup/optimistic/types/api.ts new file mode 100644 index 0000000000..579c64d45c --- /dev/null +++ b/client/features/rollup/optimistic/types/api.ts @@ -0,0 +1,30 @@ +import type { + OptimisticL2BatchDataContainer, + OptimisticL2BlobTypeCelestia, + OptimisticL2BlobTypeEip4844, + OptimisticL2WithdrawalClaimInfo, + OptimisticL2WithdrawalStatus, +} from 'types/api/optimisticL2'; + +export interface OpWithdrawal extends OptimisticL2WithdrawalClaimInfo { + l1_transaction_hash: string; + nonce: number; + status: OptimisticL2WithdrawalStatus; +} + +export interface TransactionOptimistic { + op_withdrawals?: Array; + operator_fee?: string; +} + +export interface OptimismBlockData { + batch_data_container: OptimisticL2BatchDataContainer; + number: number; + blobs: Array | Array | null; + l1_timestamp: string; + l1_transaction_hashes: Array; +} + +export interface BlockOptimism { + optimism?: OptimismBlockData; +} diff --git a/ui/tx/details/TxInfoScrollFees.tsx b/client/features/rollup/scroll/pages/tx/TxInfoScrollFees.tsx similarity index 98% rename from ui/tx/details/TxInfoScrollFees.tsx rename to client/features/rollup/scroll/pages/tx/TxInfoScrollFees.tsx index c80bad3bf9..94fcc76113 100644 --- a/ui/tx/details/TxInfoScrollFees.tsx +++ b/client/features/rollup/scroll/pages/tx/TxInfoScrollFees.tsx @@ -1,7 +1,7 @@ import { Text } from '@chakra-ui/react'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import { layerLabels } from 'lib/rollups/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/client/features/rollup/scroll/types/api.ts b/client/features/rollup/scroll/types/api.ts new file mode 100644 index 0000000000..d0fd0fb72b --- /dev/null +++ b/client/features/rollup/scroll/types/api.ts @@ -0,0 +1,18 @@ +import type { TransactionFee } from 'client/slices/tx/types/api'; +import type { ScrollL2BlockStatus } from 'types/api/scrollL2'; + +export interface TransactionScroll { + scroll?: { + l1_fee: string; + l2_fee: TransactionFee; + l1_fee_commit_scalar: number; + l1_base_fee: number; + l1_blob_base_fee: number; + l1_fee_scalar: number; + l1_fee_overhead: number; + l1_fee_blob_scalar: number; + l1_gas_used: number; + l2_block_status: ScrollL2BlockStatus; + queue_index: number; + }; +} diff --git a/client/features/rollup/zk-sync/types/api.ts b/client/features/rollup/zk-sync/types/api.ts new file mode 100644 index 0000000000..7dc3cbce58 --- /dev/null +++ b/client/features/rollup/zk-sync/types/api.ts @@ -0,0 +1,11 @@ +import type { ZkSyncBatchesItem } from 'types/api/zkSyncL2'; + +export interface TransactionZkSync { + zksync?: Omit & { + batch_number: number | null; + }; +} + +export interface BlockZkSync { + zksync?: Omit & { batch_number: number | null }; +} diff --git a/ui/address/SolidityscanReport.pw.tsx b/client/features/solidity-scan/components/SolidityscanReport.pw.tsx similarity index 100% rename from ui/address/SolidityscanReport.pw.tsx rename to client/features/solidity-scan/components/SolidityscanReport.pw.tsx diff --git a/ui/address/SolidityscanReport.tsx b/client/features/solidity-scan/components/SolidityscanReport.tsx similarity index 100% rename from ui/address/SolidityscanReport.tsx rename to client/features/solidity-scan/components/SolidityscanReport.tsx diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-1.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-1.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-1.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-1.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_dark-color-mode_average-report-dark-mode-mobile-2.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-1.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-1.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-1.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-1.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_average-report-dark-mode-mobile-2.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-1.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-1.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-1.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-1.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_great-report-2.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-1.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-1.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-1.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-1.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_default_low-report-2.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-1.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-1.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-1.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-1.png diff --git a/ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png b/client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png similarity index 100% rename from ui/address/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png rename to client/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png diff --git a/client/features/tx-actions/mocks/tx.ts b/client/features/tx-actions/mocks/tx.ts new file mode 100644 index 0000000000..a263a0ea90 --- /dev/null +++ b/client/features/tx-actions/mocks/tx.ts @@ -0,0 +1,72 @@ +import type { Transaction } from 'client/slices/tx/types/api'; + +import { base } from 'client/slices/tx/mocks/tx'; + +export const withActionsUniswap: Transaction = { + ...base, + actions: [ + { + data: { + address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', + address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', + amount0: '7143.488560357232097378', + amount1: '10', + symbol0: 'Ring ding ding daa baa Baa aramba baa bom baa barooumba Wh-wha-what's going on-on? Ding, ding This is the Crazy Frog Ding, ding Bem', + symbol1: 'Ether', + }, + protocol: 'uniswap_v3', + type: 'mint', + }, + { + data: { + address: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88', + ids: [ + '53699', + '53700123456', + '42', + ], + name: 'Uniswap V3: Positions NFT', + symbol: 'UNI-V3-POS', + to: '0x6d872Fb5F5B2B1f71fA9AadE159bc3976c1946B7', + }, + protocol: 'uniswap_v3', + type: 'mint_nft', + }, + { + data: { + address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', + address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', + amount0: '42876.488560357232', + amount1: '345.908098203434', + symbol0: 'SHAVUHA', + symbol1: 'BOB', + }, + protocol: 'uniswap_v3', + type: 'swap', + }, + { + data: { + address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', + address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', + amount0: '42', + amount1: '0.523523223232', + symbol0: 'VIC', + symbol1: 'USDT', + }, + protocol: 'uniswap_v3', + type: 'burn', + }, + { + data: { + address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', + address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', + amount0: '42', + amount1: '0.523523223232', + symbol0: 'BOB', + symbol1: 'UNI', + }, + protocol: 'uniswap_v3', + type: 'collect', + }, + ], +}; diff --git a/ui/tx/details/txDetailsActions/TxDetailsAction.tsx b/client/features/tx-actions/pages/tx/TxDetailsAction.tsx similarity index 98% rename from ui/tx/details/txDetailsActions/TxDetailsAction.tsx rename to client/features/tx-actions/pages/tx/TxDetailsAction.tsx index 9c346caabb..5369ea5ef6 100644 --- a/ui/tx/details/txDetailsActions/TxDetailsAction.tsx +++ b/client/features/tx-actions/pages/tx/TxDetailsAction.tsx @@ -4,8 +4,9 @@ import React from 'react'; import type { TxAction, TxActionGeneral } from 'types/api/txAction'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import config from 'configs/app'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import NftEntity from 'ui/shared/entities/nft/NftEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/ui/tx/details/txDetailsActions/TxDetailsActions.tsx b/client/features/tx-actions/pages/tx/TxDetailsActions.tsx similarity index 78% rename from ui/tx/details/txDetailsActions/TxDetailsActions.tsx rename to client/features/tx-actions/pages/tx/TxDetailsActions.tsx index 63382d5537..3cdb0c1912 100644 --- a/ui/tx/details/txDetailsActions/TxDetailsActions.tsx +++ b/client/features/tx-actions/pages/tx/TxDetailsActions.tsx @@ -3,8 +3,9 @@ import React from 'react'; import type { TxAction } from 'types/api/txAction'; import config from 'configs/app'; -import TxDetailsActionsInterpretation from 'ui/tx/details/txDetailsActions/TxDetailsActionsInterpretation'; -import TxDetailsActionsRaw from 'ui/tx/details/txDetailsActions/TxDetailsActionsRaw'; + +import TxDetailsActionsInterpretation from './TxDetailsActionsInterpretation'; +import TxDetailsActionsRaw from './TxDetailsActionsRaw'; type Props = { isTxDataLoading: boolean; diff --git a/ui/tx/details/txDetailsActions/TxDetailsActionsInterpretation.tsx b/client/features/tx-actions/pages/tx/TxDetailsActionsInterpretation.tsx similarity index 89% rename from ui/tx/details/txDetailsActions/TxDetailsActionsInterpretation.tsx rename to client/features/tx-actions/pages/tx/TxDetailsActionsInterpretation.tsx index 1c909069a7..5cd8baa62a 100644 --- a/ui/tx/details/txDetailsActions/TxDetailsActionsInterpretation.tsx +++ b/client/features/tx-actions/pages/tx/TxDetailsActionsInterpretation.tsx @@ -1,10 +1,12 @@ import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; + import { TX_INTERPRETATION } from 'stubs/txInterpretation'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoActionsWrapper from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; -import TxInterpretation from 'ui/shared/tx/interpretation/TxInterpretation'; interface Props { hash?: string; diff --git a/ui/tx/details/txDetailsActions/TxDetailsActionsRaw.tsx b/client/features/tx-actions/pages/tx/TxDetailsActionsRaw.tsx similarity index 100% rename from ui/tx/details/txDetailsActions/TxDetailsActionsRaw.tsx rename to client/features/tx-actions/pages/tx/TxDetailsActionsRaw.tsx diff --git a/client/features/tx-actions/types/api.ts b/client/features/tx-actions/types/api.ts new file mode 100644 index 0000000000..fa8027095f --- /dev/null +++ b/client/features/tx-actions/types/api.ts @@ -0,0 +1,5 @@ +import type { TxAction } from 'types/api/txAction'; + +export interface TransactionActions { + actions?: Array; +} diff --git a/ui/tx/TxAuthorizations.tsx b/client/features/tx-authorization/pages/tx/TxAuthorizations.tsx similarity index 75% rename from ui/tx/TxAuthorizations.tsx rename to client/features/tx-authorization/pages/tx/TxAuthorizations.tsx index b97fa7c212..287c26d299 100644 --- a/ui/tx/TxAuthorizations.tsx +++ b/client/features/tx-authorization/pages/tx/TxAuthorizations.tsx @@ -1,13 +1,14 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import DataListDisplay from 'ui/shared/DataListDisplay'; -import TxPendingAlert from 'ui/tx/TxPendingAlert'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; -import TxAuthorizationsList from './authorizations/TxAuthorizationsList'; -import TxAuthorizationsTable from './authorizations/TxAuthorizationsTable'; -import type { TxQuery } from './useTxQuery'; +import TxAuthorizationsList from './TxAuthorizationsList'; +import TxAuthorizationsTable from './TxAuthorizationsTable'; interface Props { txQuery: TxQuery; diff --git a/ui/tx/authorizations/TxAuthorizationsList.tsx b/client/features/tx-authorization/pages/tx/TxAuthorizationsList.tsx similarity index 86% rename from ui/tx/authorizations/TxAuthorizationsList.tsx rename to client/features/tx-authorization/pages/tx/TxAuthorizationsList.tsx index 8e1d48e025..79961d8c55 100644 --- a/ui/tx/authorizations/TxAuthorizationsList.tsx +++ b/client/features/tx-authorization/pages/tx/TxAuthorizationsList.tsx @@ -1,7 +1,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { TxAuthorization } from 'types/api/transaction'; +import type { TxAuthorization } from 'client/features/tx-authorization/types/api'; import TxAuthorizationsListItem from './TxAuthorizationsListItem'; diff --git a/ui/tx/authorizations/TxAuthorizationsListItem.tsx b/client/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx similarity index 91% rename from ui/tx/authorizations/TxAuthorizationsListItem.tsx rename to client/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx index 999c5dc06c..7d0a1636ca 100644 --- a/ui/tx/authorizations/TxAuthorizationsListItem.tsx +++ b/client/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx @@ -1,11 +1,12 @@ import { HStack } from '@chakra-ui/react'; import React from 'react'; -import type { TxAuthorization } from 'types/api/transaction'; +import type { TxAuthorization } from 'client/features/tx-authorization/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TxAuthorizationStatus from 'ui/shared/statusTag/TxAuthorizationStatus'; diff --git a/ui/tx/authorizations/TxAuthorizationsTable.tsx b/client/features/tx-authorization/pages/tx/TxAuthorizationsTable.tsx similarity index 87% rename from ui/tx/authorizations/TxAuthorizationsTable.tsx rename to client/features/tx-authorization/pages/tx/TxAuthorizationsTable.tsx index e4781def5d..25c9bd5813 100644 --- a/ui/tx/authorizations/TxAuthorizationsTable.tsx +++ b/client/features/tx-authorization/pages/tx/TxAuthorizationsTable.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import type { TxAuthorization } from 'types/api/transaction'; +import type { TxAuthorization } from 'client/features/tx-authorization/types/api'; + +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TxAuthorizationsTableItem from './TxAuthorizationsTableItem'; diff --git a/ui/tx/authorizations/TxAuthorizationsTableItem.tsx b/client/features/tx-authorization/pages/tx/TxAuthorizationsTableItem.tsx similarity index 89% rename from ui/tx/authorizations/TxAuthorizationsTableItem.tsx rename to client/features/tx-authorization/pages/tx/TxAuthorizationsTableItem.tsx index 2d4f10309c..41b0aff6c0 100644 --- a/ui/tx/authorizations/TxAuthorizationsTableItem.tsx +++ b/client/features/tx-authorization/pages/tx/TxAuthorizationsTableItem.tsx @@ -1,12 +1,13 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { TxAuthorization } from 'types/api/transaction'; +import type { TxAuthorization } from 'client/features/tx-authorization/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableRow, TableCell } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import TxAuthorizationStatus from 'ui/shared/statusTag/TxAuthorizationStatus'; interface Props extends TxAuthorization { diff --git a/client/features/tx-authorization/types/api.ts b/client/features/tx-authorization/types/api.ts new file mode 100644 index 0000000000..39906f4236 --- /dev/null +++ b/client/features/tx-authorization/types/api.ts @@ -0,0 +1,11 @@ +export interface TransactionAuthorization { + authorization_list?: Array; +} + +export interface TxAuthorization { + address_hash: string; + authority: string; + chain_id: number; + nonce: number; + status: 'ok' | 'invalid_chain_id' | 'invalid_nonce' | 'invalid_signature' | null; +} diff --git a/ui/shared/tx/interpretation/TxInterpretation.tsx b/client/features/tx-interpretation/common/components/TxInterpretation.tsx similarity index 96% rename from ui/shared/tx/interpretation/TxInterpretation.tsx rename to client/features/tx-interpretation/common/components/TxInterpretation.tsx index c0b92a65af..e8dccb052b 100644 --- a/ui/shared/tx/interpretation/TxInterpretation.tsx +++ b/client/features/tx-interpretation/common/components/TxInterpretation.tsx @@ -3,7 +3,7 @@ import { Box, chakra } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressParam } from 'types/api/addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; import type { TxInterpretationSummary, TxInterpretationVariable, @@ -13,10 +13,13 @@ import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; import dayjs from 'lib/date/dayjs'; -import * as mixpanel from 'lib/mixpanel/index'; -import { currencyUnits } from 'lib/units'; import { Badge } from 'toolkit/chakra/badge'; import { useColorModeValue } from 'toolkit/chakra/color-mode'; import { Image } from 'toolkit/chakra/image'; @@ -24,7 +27,6 @@ import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { SECOND } from 'toolkit/utils/consts'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; @@ -38,7 +40,7 @@ import { checkSummary, NATIVE_COIN_SYMBOL_VAR_NAME, WEI_VAR_NAME, -} from './utils'; +} from '../utils/utils'; const nameServicesFeature = config.features.nameServices; diff --git a/ui/shared/tx/interpretation/utils.spec.ts b/client/features/tx-interpretation/common/utils/utils.spec.ts similarity index 100% rename from ui/shared/tx/interpretation/utils.spec.ts rename to client/features/tx-interpretation/common/utils/utils.spec.ts diff --git a/ui/shared/tx/interpretation/utils.ts b/client/features/tx-interpretation/common/utils/utils.ts similarity index 100% rename from ui/shared/tx/interpretation/utils.ts rename to client/features/tx-interpretation/common/utils/utils.ts diff --git a/ui/tx/assetFlows/components/NovesActionSnippet.tsx b/client/features/tx-interpretation/noves/components/NovesActionSnippet.tsx similarity index 98% rename from ui/tx/assetFlows/components/NovesActionSnippet.tsx rename to client/features/tx-interpretation/noves/components/NovesActionSnippet.tsx index 036509efba..a9afae4c8d 100644 --- a/ui/tx/assetFlows/components/NovesActionSnippet.tsx +++ b/client/features/tx-interpretation/noves/components/NovesActionSnippet.tsx @@ -90,7 +90,6 @@ const NovesActionSnippet: FC = ({ item, isLoaded }) => { jointSymbol noLink={ !validTokenAddress } fontWeight="500" - color="link.primary" w="fit-content" />
diff --git a/ui/tx/assetFlows/components/NovesTokenTooltipContent.tsx b/client/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx similarity index 83% rename from ui/tx/assetFlows/components/NovesTokenTooltipContent.tsx rename to client/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx index 9e65de6522..6f4788a148 100644 --- a/ui/tx/assetFlows/components/NovesTokenTooltipContent.tsx +++ b/client/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx @@ -4,6 +4,8 @@ import React from 'react'; import type { NovesNft, NovesToken } from 'types/api/noves'; +import shortenString from 'client/shared/text/shorten-string'; + import { HEX_REGEXP } from 'toolkit/utils/regexp'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; @@ -21,7 +23,7 @@ const NovesTokenTooltipContent: FC = ({ token, amount }) => { const showTokenAddress = HEX_REGEXP.test(token.address); return ( - + { amount } @@ -40,9 +42,9 @@ const NovesTokenTooltipContent: FC = ({ token, amount }) => { { showTokenAddress && ( - { token.address } + { shortenString(token.address) } - + ) } diff --git a/ui/txs/TxTranslationType.tsx b/client/features/tx-interpretation/noves/components/TxTranslationType.tsx similarity index 76% rename from ui/txs/TxTranslationType.tsx rename to client/features/tx-interpretation/noves/components/TxTranslationType.tsx index af75b1cbe1..e105d79b78 100644 --- a/ui/txs/TxTranslationType.tsx +++ b/client/features/tx-interpretation/noves/components/TxTranslationType.tsx @@ -1,11 +1,12 @@ import React from 'react'; -import type { TransactionType } from 'types/api/transaction'; +import type { TransactionType } from 'client/slices/tx/types/api'; + +import TxType from 'client/slices/tx/components/TxType'; import { Badge } from 'toolkit/chakra/badge'; -import { camelCaseToSentence } from './noves/utils'; -import TxType from './TxType'; +import { camelCaseToSentence } from '../utils/translation'; export interface Props { txTypes: Array; diff --git a/ui/txs/noves/useDescribeTxs.tsx b/client/features/tx-interpretation/noves/hooks/useDescribeTxs.ts similarity index 84% rename from ui/txs/noves/useDescribeTxs.tsx rename to client/features/tx-interpretation/noves/hooks/useDescribeTxs.ts index db8ffa8de2..b09179f778 100644 --- a/ui/txs/noves/useDescribeTxs.tsx +++ b/client/features/tx-interpretation/noves/hooks/useDescribeTxs.ts @@ -1,11 +1,12 @@ import { uniq, chunk } from 'es-toolkit'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import type { ReturnType } from 'client/api/hooks/useApiQueries'; +import useApiQueries from 'client/api/hooks/useApiQueries'; import config from 'configs/app'; -import type { ReturnType } from 'lib/api/useApiQueries'; -import useApiQueries from 'lib/api/useApiQueries'; const feature = config.features.txInterpretation; diff --git a/ui/address/AddressAccountHistory.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx similarity index 91% rename from ui/address/AddressAccountHistory.tsx rename to client/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx index c09795797e..28bd409037 100644 --- a/ui/address/AddressAccountHistory.tsx +++ b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx @@ -5,21 +5,22 @@ import React from 'react'; import type { NovesHistoryFilterValue } from 'types/api/noves'; import { NovesHistoryFilterValues } from 'types/api/noves'; -import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate'; import { generateListStub } from 'stubs/utils'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; -import AddressAccountHistoryTableItem from 'ui/address/accountHistory/AddressAccountHistoryTableItem'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import { getFromToValue } from 'ui/shared/Noves/utils'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import AddressAccountHistoryListItem from './accountHistory/AddressAccountHistoryListItem'; import AccountHistoryFilter from './AddressAccountHistoryFilter'; +import AddressAccountHistoryListItem from './AddressAccountHistoryListItem'; +import AddressAccountHistoryTableItem from './AddressAccountHistoryTableItem'; const getFilterValue = (getFilterValueFromQuery).bind(null, NovesHistoryFilterValues); diff --git a/ui/address/AddressAccountHistoryFilter.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryFilter.tsx similarity index 93% rename from ui/address/AddressAccountHistoryFilter.tsx rename to client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryFilter.tsx index 7c202f0e99..aa006cafcf 100644 --- a/ui/address/AddressAccountHistoryFilter.tsx +++ b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryFilter.tsx @@ -3,7 +3,8 @@ import React from 'react'; import type { NovesHistoryFilterValue } from 'types/api/noves'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; + import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio'; const OPTIONS = [ diff --git a/ui/address/accountHistory/AddressAccountHistoryListItem.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx similarity index 100% rename from ui/address/accountHistory/AddressAccountHistoryListItem.tsx rename to client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx diff --git a/ui/address/accountHistory/AddressAccountHistoryTableItem.tsx b/client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx similarity index 100% rename from ui/address/accountHistory/AddressAccountHistoryTableItem.tsx rename to client/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx diff --git a/ui/tx/TxAssetFlows.tsx b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx similarity index 91% rename from ui/tx/TxAssetFlows.tsx rename to client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx index 182bf8bfd9..7bd55d6665 100644 --- a/ui/tx/TxAssetFlows.tsx +++ b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx @@ -4,18 +4,20 @@ import React, { useMemo, useState } from 'react'; import type { PaginationParams } from 'ui/shared/pagination/types'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import Pagination from 'ui/shared/pagination/Pagination'; -import TxAssetFlowsListItem from './assetFlows/TxAssetFlowsListItem'; -import TxAssetFlowsTableItem from './assetFlows/TxAssetFlowsTableItem'; -import { generateFlowViewData } from './assetFlows/utils/generateFlowViewData'; +import { generateFlowViewData } from '../../utils/generateFlowViewData'; +import TxAssetFlowsListItem from './TxAssetFlowsListItem'; +import TxAssetFlowsTableItem from './TxAssetFlowsTableItem'; interface FlowViewProps { hash: string; diff --git a/ui/tx/assetFlows/TxAssetFlowsListItem.tsx b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx similarity index 88% rename from ui/tx/assetFlows/TxAssetFlowsListItem.tsx rename to client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx index 64da979123..6e9795ad34 100644 --- a/ui/tx/assetFlows/TxAssetFlowsListItem.tsx +++ b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx @@ -6,8 +6,8 @@ import IconSvg from 'ui/shared/IconSvg'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; -import NovesActionSnippet from './components/NovesActionSnippet'; -import type { NovesFlowViewItem } from './utils/generateFlowViewData'; +import NovesActionSnippet from '../../components/NovesActionSnippet'; +import type { NovesFlowViewItem } from '../../utils/generateFlowViewData'; type Props = { isPlaceholderData: boolean; diff --git a/ui/tx/assetFlows/TxAssetFlowsTableItem.tsx b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx similarity index 84% rename from ui/tx/assetFlows/TxAssetFlowsTableItem.tsx rename to client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx index a64b742e3b..9cd45adac5 100644 --- a/ui/tx/assetFlows/TxAssetFlowsTableItem.tsx +++ b/client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTableItem.tsx @@ -3,8 +3,8 @@ import React from 'react'; import { TableRow, TableCell } from 'toolkit/chakra/table'; import NovesFromTo from 'ui/shared/Noves/NovesFromTo'; -import NovesActionSnippet from './components/NovesActionSnippet'; -import type { NovesFlowViewItem } from './utils/generateFlowViewData'; +import NovesActionSnippet from '../../components/NovesActionSnippet'; +import type { NovesFlowViewItem } from '../../utils/generateFlowViewData'; type Props = { isPlaceholderData: boolean; diff --git a/ui/tx/assetFlows/utils/createNovesSummaryObject.spec.ts b/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.spec.ts similarity index 100% rename from ui/tx/assetFlows/utils/createNovesSummaryObject.spec.ts rename to client/features/tx-interpretation/noves/utils/createNovesSummaryObject.spec.ts diff --git a/ui/tx/assetFlows/utils/createNovesSummaryObject.ts b/client/features/tx-interpretation/noves/utils/createNovesSummaryObject.ts similarity index 100% rename from ui/tx/assetFlows/utils/createNovesSummaryObject.ts rename to client/features/tx-interpretation/noves/utils/createNovesSummaryObject.ts diff --git a/ui/tx/assetFlows/utils/generateFlowViewData.spec.ts b/client/features/tx-interpretation/noves/utils/generateFlowViewData.spec.ts similarity index 100% rename from ui/tx/assetFlows/utils/generateFlowViewData.spec.ts rename to client/features/tx-interpretation/noves/utils/generateFlowViewData.spec.ts diff --git a/ui/tx/assetFlows/utils/generateFlowViewData.ts b/client/features/tx-interpretation/noves/utils/generateFlowViewData.ts similarity index 100% rename from ui/tx/assetFlows/utils/generateFlowViewData.ts rename to client/features/tx-interpretation/noves/utils/generateFlowViewData.ts diff --git a/ui/tx/assetFlows/utils/getAddressValues.spec.ts b/client/features/tx-interpretation/noves/utils/getAddressValues.spec.ts similarity index 100% rename from ui/tx/assetFlows/utils/getAddressValues.spec.ts rename to client/features/tx-interpretation/noves/utils/getAddressValues.spec.ts diff --git a/ui/tx/assetFlows/utils/getAddressValues.ts b/client/features/tx-interpretation/noves/utils/getAddressValues.ts similarity index 100% rename from ui/tx/assetFlows/utils/getAddressValues.ts rename to client/features/tx-interpretation/noves/utils/getAddressValues.ts diff --git a/ui/tx/assetFlows/utils/getTokensData.spec.ts b/client/features/tx-interpretation/noves/utils/getTokensData.spec.ts similarity index 100% rename from ui/tx/assetFlows/utils/getTokensData.spec.ts rename to client/features/tx-interpretation/noves/utils/getTokensData.spec.ts diff --git a/ui/tx/assetFlows/utils/getTokensData.ts b/client/features/tx-interpretation/noves/utils/getTokensData.ts similarity index 100% rename from ui/tx/assetFlows/utils/getTokensData.ts rename to client/features/tx-interpretation/noves/utils/getTokensData.ts diff --git a/ui/txs/noves/utils.ts b/client/features/tx-interpretation/noves/utils/translation.ts similarity index 80% rename from ui/txs/noves/utils.ts rename to client/features/tx-interpretation/noves/utils/translation.ts index 0197b38e7c..d5156a14b2 100644 --- a/ui/txs/noves/utils.ts +++ b/client/features/tx-interpretation/noves/utils/translation.ts @@ -1,4 +1,4 @@ -import capitalizeFirstLetter from 'lib/capitalizeFirstLetter'; +import capitalizeFirstLetter from 'client/shared/text/capitalize-first-letter'; export function camelCaseToSentence(camelCaseString: string | undefined) { if (!camelCaseString) { diff --git a/ui/address/AddressUserOps.tsx b/client/features/user-ops/pages/address/AddressUserOps.tsx similarity index 89% rename from ui/address/AddressUserOps.tsx rename to client/features/user-ops/pages/address/AddressUserOps.tsx index e958a80682..15c68b59e9 100644 --- a/ui/address/AddressUserOps.tsx +++ b/client/features/user-ops/pages/address/AddressUserOps.tsx @@ -1,8 +1,9 @@ import { useRouter } from 'next/router'; import React from 'react'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { USER_OPS_ITEM } from 'stubs/userOps'; import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/ui/tx/TxUserOps.tsx b/client/features/user-ops/pages/tx/TxUserOps.tsx similarity index 84% rename from ui/tx/TxUserOps.tsx rename to client/features/user-ops/pages/tx/TxUserOps.tsx index ee96710c07..5ecdc727d0 100644 --- a/ui/tx/TxUserOps.tsx +++ b/client/features/user-ops/pages/tx/TxUserOps.tsx @@ -1,15 +1,15 @@ import React from 'react'; +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import { USER_OPS_ITEM } from 'stubs/userOps'; import { generateListStub } from 'stubs/utils'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import TxPendingAlert from 'ui/tx/TxPendingAlert'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; import UserOpsContent from 'ui/userOps/UserOpsContent'; -import type { TxQuery } from './useTxQuery'; - interface Props { txQuery: TxQuery; } diff --git a/ui/shared/address/AddressAddToWallet.tsx b/client/features/web3-wallet/components/TokenAddToWallet.tsx similarity index 89% rename from ui/shared/address/AddressAddToWallet.tsx rename to client/features/web3-wallet/components/TokenAddToWallet.tsx index d92267967d..8d297f23db 100644 --- a/ui/shared/address/AddressAddToWallet.tsx +++ b/client/features/web3-wallet/components/TokenAddToWallet.tsx @@ -4,13 +4,14 @@ import type { WatchAssetParams } from 'viem'; import type { TokenInfo } from 'types/api/token'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useProvider from 'client/shared/web3/useProvider'; +import useSwitchOrAddChain from 'client/shared/web3/useSwitchOrAddChain'; +import { WALLETS_INFO } from 'client/shared/web3/wallets'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; import useRewardsActivity from 'lib/hooks/useRewardsActivity'; -import * as mixpanel from 'lib/mixpanel/index'; -import useProvider from 'lib/web3/useProvider'; -import useSwitchOrAddChain from 'lib/web3/useSwitchOrAddChain'; -import { WALLETS_INFO } from 'lib/web3/wallets'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { toaster } from 'toolkit/chakra/toaster'; @@ -58,7 +59,7 @@ interface Props { chainConfig?: typeof config; } -const AddressAddToWallet = ({ className, token, tokenId, isLoading, variant = 'icon', iconSize = 6, chainConfig }: Props) => { +const TokenAddToWallet = ({ className, token, tokenId, isLoading, variant = 'icon', iconSize = 6, chainConfig }: Props) => { const { data: { wallet, provider } = {} } = useProvider(); const switchOrAddChain = useSwitchOrAddChain({ chainConfig }); const isMobile = useIsMobile(); @@ -154,4 +155,4 @@ const AddressAddToWallet = ({ className, token, tokenId, isLoading, variant = 'i ); }; -export default React.memo(chakra(AddressAddToWallet)); +export default React.memo(chakra(TokenAddToWallet)); diff --git a/lib/mixpanel/getPageType.ts b/client/shared/analytics/mixpanel/get-page-type.ts similarity index 80% rename from lib/mixpanel/getPageType.ts rename to client/shared/analytics/mixpanel/get-page-type.ts index bc0d93e88a..f59f9b5025 100644 --- a/lib/mixpanel/getPageType.ts +++ b/client/shared/analytics/mixpanel/get-page-type.ts @@ -39,7 +39,6 @@ export const PAGE_TYPE_DICT: Record = { '/withdrawals': 'Withdrawals', '/txn-withdrawals': 'Txn withdrawals', '/visualize/sol2uml': 'Solidity UML diagram', - '/csv-export': 'Export data to CSV file', '/deposits': 'Deposits', '/output-roots': 'Output roots', '/dispute-games': 'Dispute games', @@ -68,19 +67,19 @@ export const PAGE_TYPE_DICT: Record = { '/operation/[id]': 'Operation details', '/cc/tx/[hash]': 'Cross-chain transaction details', '/cross-chain-tx/[id]': 'Cross-chain transaction details', + '/ictt-users': 'ICTT users', // multichain routes - '/chain/[chain_slug]/accounts/label/[slug]': 'Chain addresses search by label', - '/chain/[chain_slug]/advanced-filter': 'Chain advanced filter', - '/chain/[chain_slug]/block/[height_or_hash]': 'Chain block details', - '/chain/[chain_slug]/block/countdown': 'Chain block countdown index', - '/chain/[chain_slug]/block/countdown/[height]': 'Chain block countdown', - '/chain/[chain_slug]/csv-export': 'Chain export data to CSV', - '/chain/[chain_slug]/op/[hash]': 'Chain user operation details', - '/chain/[chain_slug]/token/[hash]': 'Chain token details', - '/chain/[chain_slug]/token/[hash]/instance/[id]': 'Chain token NFT instance', - '/chain/[chain_slug]/tx/[hash]': 'Chain transaction details', - '/chain/[chain_slug]/visualize/sol2uml': 'Chain Solidity UML diagram', + '/chain/[chain_slug_or_id]/accounts/label/[slug]': 'Chain addresses search by label', + '/chain/[chain_slug_or_id]/advanced-filter': 'Chain advanced filter', + '/chain/[chain_slug_or_id]/block/[height_or_hash]': 'Chain block details', + '/chain/[chain_slug_or_id]/block/countdown': 'Chain block countdown index', + '/chain/[chain_slug_or_id]/block/countdown/[height]': 'Chain block countdown', + '/chain/[chain_slug_or_id]/op/[hash]': 'Chain user operation details', + '/chain/[chain_slug_or_id]/token/[hash]': 'Chain token details', + '/chain/[chain_slug_or_id]/token/[hash]/instance/[id]': 'Chain token NFT instance', + '/chain/[chain_slug_or_id]/tx/[hash]': 'Chain transaction details', + '/chain/[chain_slug_or_id]/visualize/sol2uml': 'Chain Solidity UML diagram', '/ecosystems': 'Ecosystems', // service routes, added only to make typescript happy diff --git a/lib/mixpanel/getTabName.ts b/client/shared/analytics/mixpanel/get-tab-name.ts similarity index 100% rename from lib/mixpanel/getTabName.ts rename to client/shared/analytics/mixpanel/get-tab-name.ts diff --git a/lib/mixpanel/index.ts b/client/shared/analytics/mixpanel/index.ts similarity index 53% rename from lib/mixpanel/index.ts rename to client/shared/analytics/mixpanel/index.ts index bf88afd450..0dd176131e 100644 --- a/lib/mixpanel/index.ts +++ b/client/shared/analytics/mixpanel/index.ts @@ -1,9 +1,9 @@ -import getPageType, { PAGE_TYPE_DICT } from './getPageType'; -import logEvent from './logEvent'; +import getPageType, { PAGE_TYPE_DICT } from './get-page-type'; +import logEvent from './log-event'; import reset from './reset'; -import useInit from './useInit'; import useLogPageView from './useLogPageView'; -import * as userProfile from './userProfile'; +import useInit from './useMixpanelInit'; +import * as userProfile from './user-profile'; export * from './utils'; export { diff --git a/lib/mixpanel/logEvent.ts b/client/shared/analytics/mixpanel/log-event.ts similarity index 100% rename from lib/mixpanel/logEvent.ts rename to client/shared/analytics/mixpanel/log-event.ts diff --git a/lib/mixpanel/reset.ts b/client/shared/analytics/mixpanel/reset.ts similarity index 100% rename from lib/mixpanel/reset.ts rename to client/shared/analytics/mixpanel/reset.ts diff --git a/lib/mixpanel/useLogPageView.tsx b/client/shared/analytics/mixpanel/useLogPageView.tsx similarity index 86% rename from lib/mixpanel/useLogPageView.tsx rename to client/shared/analytics/mixpanel/useLogPageView.tsx index fc142e837e..c38d30d8e9 100644 --- a/lib/mixpanel/useLogPageView.tsx +++ b/client/shared/analytics/mixpanel/useLogPageView.tsx @@ -4,15 +4,16 @@ import React from 'react'; import type { ColorThemeId } from 'types/settings'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { getDefaultColorTheme } from 'lib/settings/colorTheme'; import { useColorMode } from 'toolkit/chakra/color-mode'; -import getPageType from './getPageType'; -import getTabName from './getTabName'; -import logEvent from './logEvent'; +import getPageType from './get-page-type'; +import getTabName from './get-tab-name'; +import logEvent from './log-event'; import { EventTypes } from './utils'; export default function useLogPageView(isInitialized: boolean) { diff --git a/lib/mixpanel/useInit.tsx b/client/shared/analytics/mixpanel/useMixpanelInit.tsx similarity index 91% rename from lib/mixpanel/useInit.tsx rename to client/shared/analytics/mixpanel/useMixpanelInit.tsx index 8cd5e9ad5b..032dd8f0a5 100644 --- a/lib/mixpanel/useInit.tsx +++ b/client/shared/analytics/mixpanel/useMixpanelInit.tsx @@ -5,12 +5,13 @@ import { useRouter } from 'next/router'; import React from 'react'; import { deviceType } from 'react-device-detect'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; import dayjs from 'lib/date/dayjs'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import * as userProfile from './userProfile'; +import * as userProfile from './user-profile'; const multichainFeature = config.features.multichain; diff --git a/lib/mixpanel/userProfile.ts b/client/shared/analytics/mixpanel/user-profile.ts similarity index 100% rename from lib/mixpanel/userProfile.ts rename to client/shared/analytics/mixpanel/user-profile.ts diff --git a/lib/mixpanel/utils.ts b/client/shared/analytics/mixpanel/utils.ts similarity index 100% rename from lib/mixpanel/utils.ts rename to client/shared/analytics/mixpanel/utils.ts diff --git a/lib/decodeJWT.ts b/client/shared/auth/decode-jwt.ts similarity index 100% rename from lib/decodeJWT.ts rename to client/shared/auth/decode-jwt.ts diff --git a/lib/networks/getNetworkTitle.ts b/client/shared/chain/get-chain-title.ts similarity index 81% rename from lib/networks/getNetworkTitle.ts rename to client/shared/chain/get-chain-title.ts index 5bcb602e39..28e1c5e795 100644 --- a/lib/networks/getNetworkTitle.ts +++ b/client/shared/chain/get-chain-title.ts @@ -1,6 +1,6 @@ import config from 'configs/app'; // TODO delete when page descriptions is refactored -export default function getNetworkTitle() { +export default function getChainTitle() { return config.chain.name + (config.chain.shortName ? ` (${ config.chain.shortName })` : '') + ' Explorer'; } diff --git a/lib/networks/getNetworkUtilizationParams.ts b/client/shared/chain/get-chain-utilization-params.ts similarity index 82% rename from lib/networks/getNetworkUtilizationParams.ts rename to client/shared/chain/get-chain-utilization-params.ts index 8168629a02..b0f7525ee9 100644 --- a/lib/networks/getNetworkUtilizationParams.ts +++ b/client/shared/chain/get-chain-utilization-params.ts @@ -1,4 +1,4 @@ -export default function getNetworkUtilizationParams(value: number) { +export default function getChainUtilizationParams(value: number) { const load = (() => { if (value > 80) { return 'high'; diff --git a/lib/networks/getNetworkValidationActionText.ts b/client/shared/chain/get-chain-validation-action-text.ts similarity index 71% rename from lib/networks/getNetworkValidationActionText.ts rename to client/shared/chain/get-chain-validation-action-text.ts index 3858f42b97..74ba2a3744 100644 --- a/lib/networks/getNetworkValidationActionText.ts +++ b/client/shared/chain/get-chain-validation-action-text.ts @@ -1,6 +1,6 @@ import config from 'configs/app'; -export default function getNetworkValidationActionText(chainConfig = config) { +export default function getChainValidationActionText(chainConfig = config) { switch (chainConfig.chain.verificationType) { case 'validation': { return 'validated'; @@ -11,9 +11,6 @@ export default function getNetworkValidationActionText(chainConfig = config) { case 'posting': { return 'posted'; } - case 'sequencing': { - return 'sequenced'; - } case 'fee reception': { return 'validated'; } diff --git a/lib/networks/getNetworkValidatorTitle.ts b/client/shared/chain/get-chain-validator-title.ts similarity index 75% rename from lib/networks/getNetworkValidatorTitle.ts rename to client/shared/chain/get-chain-validator-title.ts index 05ea00d5e3..4377cf617d 100644 --- a/lib/networks/getNetworkValidatorTitle.ts +++ b/client/shared/chain/get-chain-validator-title.ts @@ -1,6 +1,6 @@ import config from 'configs/app'; -export default function getNetworkValidatorTitle() { +export default function getChainValidatorTitle() { switch (config.chain.verificationType) { case 'validation': { return 'validator'; @@ -11,9 +11,6 @@ export default function getNetworkValidatorTitle() { case 'posting': { return 'poster'; } - case 'sequencing': { - return 'sequencer'; - } case 'fee reception': { return 'fee recipient'; } diff --git a/lib/units.ts b/client/shared/chain/units.ts similarity index 100% rename from lib/units.ts rename to client/shared/chain/units.ts diff --git a/lib/hooks/useTimeAgoIncrement.tsx b/client/shared/date-and-time/useTimeAgoIncrement.tsx similarity index 100% rename from lib/hooks/useTimeAgoIncrement.tsx rename to client/shared/date-and-time/useTimeAgoIncrement.tsx diff --git a/lib/errors/getErrorCauseStatusCode.ts b/client/shared/errors/get-error-cause-status-code.ts similarity index 83% rename from lib/errors/getErrorCauseStatusCode.ts rename to client/shared/errors/get-error-cause-status-code.ts index f9884f03cd..2618ff8fb0 100644 --- a/lib/errors/getErrorCauseStatusCode.ts +++ b/client/shared/errors/get-error-cause-status-code.ts @@ -1,4 +1,4 @@ -import getErrorCause from './getErrorCause'; +import getErrorCause from './get-error-cause'; export default function getErrorCauseStatusCode(error: Error | undefined): number | undefined { const cause = getErrorCause(error); diff --git a/lib/errors/getErrorCause.ts b/client/shared/errors/get-error-cause.ts similarity index 100% rename from lib/errors/getErrorCause.ts rename to client/shared/errors/get-error-cause.ts diff --git a/lib/errors/getErrorMessage.ts b/client/shared/errors/get-error-message.ts similarity index 84% rename from lib/errors/getErrorMessage.ts rename to client/shared/errors/get-error-message.ts index 5e15f29a1c..2a08062e8d 100644 --- a/lib/errors/getErrorMessage.ts +++ b/client/shared/errors/get-error-message.ts @@ -1,4 +1,4 @@ -import getErrorObj from './getErrorObj'; +import getErrorObj from './get-error-obj'; export default function getErrorMessage(error: unknown): string | undefined { const errorObj = getErrorObj(error); diff --git a/lib/errors/getErrorObjPayload.ts b/client/shared/errors/get-error-obj-payload.ts similarity index 90% rename from lib/errors/getErrorObjPayload.ts rename to client/shared/errors/get-error-obj-payload.ts index 524979fe41..44bd20ce5d 100644 --- a/lib/errors/getErrorObjPayload.ts +++ b/client/shared/errors/get-error-obj-payload.ts @@ -1,4 +1,4 @@ -import getErrorObj from './getErrorObj'; +import getErrorObj from './get-error-obj'; export default function getErrorObjPayload(error: unknown): Payload | undefined { const errorObj = getErrorObj(error); diff --git a/lib/errors/getErrorObjStatusCode.ts b/client/shared/errors/get-error-obj-status-code.ts similarity index 84% rename from lib/errors/getErrorObjStatusCode.ts rename to client/shared/errors/get-error-obj-status-code.ts index a672b9d7a4..18dad371a6 100644 --- a/lib/errors/getErrorObjStatusCode.ts +++ b/client/shared/errors/get-error-obj-status-code.ts @@ -1,4 +1,4 @@ -import getErrorObj from './getErrorObj'; +import getErrorObj from './get-error-obj'; export default function getErrorObjStatusCode(error: unknown) { const errorObj = getErrorObj(error); diff --git a/lib/errors/getErrorObj.ts b/client/shared/errors/get-error-obj.ts similarity index 100% rename from lib/errors/getErrorObj.ts rename to client/shared/errors/get-error-obj.ts diff --git a/lib/errors/getErrorProp.ts b/client/shared/errors/get-error-prop.ts similarity index 85% rename from lib/errors/getErrorProp.ts rename to client/shared/errors/get-error-prop.ts index 1e60b06b92..67ff82ad56 100644 --- a/lib/errors/getErrorProp.ts +++ b/client/shared/errors/get-error-prop.ts @@ -1,4 +1,4 @@ -import getErrorObj from './getErrorObj'; +import getErrorObj from './get-error-obj'; export default function getErrorProp(error: unknown, prop: string): T | undefined { const errorObj = getErrorObj(error); diff --git a/lib/errors/getErrorStack.ts b/client/shared/errors/get-error-stack.ts similarity index 100% rename from lib/errors/getErrorStack.ts rename to client/shared/errors/get-error-stack.ts diff --git a/lib/errors/getResourceErrorPayload.tsx b/client/shared/errors/get-resource-error-payload.ts similarity index 74% rename from lib/errors/getResourceErrorPayload.tsx rename to client/shared/errors/get-resource-error-payload.ts index bbe3981a38..7b1f79492c 100644 --- a/lib/errors/getResourceErrorPayload.tsx +++ b/client/shared/errors/get-resource-error-payload.ts @@ -1,6 +1,6 @@ -import type { ResourceError } from 'lib/api/resources'; +import type { ResourceError } from 'client/api/resources'; -import getErrorCause from './getErrorCause'; +import getErrorCause from './get-error-cause'; export default function getResourceErrorPayload | string>(error: Error | undefined): ResourceError['payload'] | undefined { diff --git a/lib/errors/throwOnAbsentParamError.ts b/client/shared/errors/throw-on-absent-param-error.ts similarity index 100% rename from lib/errors/throwOnAbsentParamError.ts rename to client/shared/errors/throw-on-absent-param-error.ts diff --git a/lib/errors/throwOnResourceLoadError.ts b/client/shared/errors/throw-on-resource-load-error.ts similarity index 85% rename from lib/errors/throwOnResourceLoadError.ts rename to client/shared/errors/throw-on-resource-load-error.ts index 64075213cf..582e537258 100644 --- a/lib/errors/throwOnResourceLoadError.ts +++ b/client/shared/errors/throw-on-resource-load-error.ts @@ -1,4 +1,4 @@ -import type { ResourceError, ResourceName } from 'lib/api/resources'; +import type { ResourceError, ResourceName } from 'client/api/resources'; type Params = ({ isError: true; diff --git a/lib/growthbook/consts.ts b/client/shared/feature-flags/consts.ts similarity index 100% rename from lib/growthbook/consts.ts rename to client/shared/feature-flags/consts.ts diff --git a/lib/growthbook/init.ts b/client/shared/feature-flags/init.ts similarity index 96% rename from lib/growthbook/init.ts rename to client/shared/feature-flags/init.ts index 655edc5b31..897fb6ebba 100644 --- a/lib/growthbook/init.ts +++ b/client/shared/feature-flags/init.ts @@ -1,7 +1,8 @@ import { GrowthBook } from '@growthbook/growthbook-react'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import config from 'configs/app'; -import * as mixpanel from 'lib/mixpanel'; import { STORAGE_KEY, STORAGE_LIMIT } from './consts'; diff --git a/lib/growthbook/useFeatureValue.ts b/client/shared/feature-flags/useFeatureValue.ts similarity index 100% rename from lib/growthbook/useFeatureValue.ts rename to client/shared/feature-flags/useFeatureValue.ts diff --git a/lib/growthbook/useLoadFeatures.ts b/client/shared/feature-flags/useLoadFeatures.ts similarity index 100% rename from lib/growthbook/useLoadFeatures.ts rename to client/shared/feature-flags/useLoadFeatures.ts diff --git a/lib/hooks/useDebounce.tsx b/client/shared/hooks/useDebounce.tsx similarity index 88% rename from lib/hooks/useDebounce.tsx rename to client/shared/hooks/useDebounce.tsx index 5dfc71c141..60a178958a 100644 --- a/lib/hooks/useDebounce.tsx +++ b/client/shared/hooks/useDebounce.tsx @@ -1,5 +1,6 @@ import React from 'react'; +// REFACTOR: replace with useDebounce from es-toolkit export default function useDebounce(value: string, delay: number) { const [ debouncedValue, setDebouncedValue ] = React.useState(value); React.useEffect( diff --git a/lib/hooks/useGradualIncrement.tsx b/client/shared/hooks/useGradualIncrement.tsx similarity index 100% rename from lib/hooks/useGradualIncrement.tsx rename to client/shared/hooks/useGradualIncrement.tsx diff --git a/lib/hooks/useIsInitialLoading.tsx b/client/shared/hooks/useIsInitialLoading.tsx similarity index 100% rename from lib/hooks/useIsInitialLoading.tsx rename to client/shared/hooks/useIsInitialLoading.tsx diff --git a/lib/hooks/useIsMobile.tsx b/client/shared/hooks/useIsMobile.tsx similarity index 100% rename from lib/hooks/useIsMobile.tsx rename to client/shared/hooks/useIsMobile.tsx diff --git a/lib/hooks/useIsMounted.tsx b/client/shared/hooks/useIsMounted.tsx similarity index 100% rename from lib/hooks/useIsMounted.tsx rename to client/shared/hooks/useIsMounted.tsx diff --git a/lib/hooks/usePreventFocusAfterModalClosing.tsx b/client/shared/hooks/usePreventFocusAfterModalClosing.tsx similarity index 100% rename from lib/hooks/usePreventFocusAfterModalClosing.tsx rename to client/shared/hooks/usePreventFocusAfterModalClosing.tsx diff --git a/lib/hooks/useTableViewValue.tsx b/client/shared/hooks/useTableViewValue.tsx similarity index 87% rename from lib/hooks/useTableViewValue.tsx rename to client/shared/hooks/useTableViewValue.tsx index 69cede86c6..9e26c400c7 100644 --- a/lib/hooks/useTableViewValue.tsx +++ b/client/shared/hooks/useTableViewValue.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import * as cookies from 'lib/cookies'; -import useFeatureValue from 'lib/growthbook/useFeatureValue'; -import * as mixpanel from 'lib/mixpanel'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useFeatureValue from 'client/shared/feature-flags/useFeatureValue'; +import * as cookies from 'client/shared/storage/cookies'; export default function useTableViewValue() { const cookieValue = cookies.get(cookies.NAMES.TABLE_VIEW_ON_MOBILE); diff --git a/lib/hooks/useUpdateValueEffect.tsx b/client/shared/hooks/useUpdateValueEffect.tsx similarity index 100% rename from lib/hooks/useUpdateValueEffect.tsx rename to client/shared/hooks/useUpdateValueEffect.tsx diff --git a/lib/setLocale.ts b/client/shared/i18n/set-locale.ts similarity index 100% rename from lib/setLocale.ts rename to client/shared/i18n/set-locale.ts diff --git a/lib/utils/stripUtmParams.ts b/client/shared/links/utils/strip-utm-params.ts similarity index 100% rename from lib/utils/stripUtmParams.ts rename to client/shared/links/utils/strip-utm-params.ts diff --git a/lib/getItemIndex.ts b/client/shared/lists/get-item-index.ts similarity index 100% rename from lib/getItemIndex.ts rename to client/shared/lists/get-item-index.ts diff --git a/lib/hooks/useInitialList.tsx b/client/shared/lists/useInitialList.tsx similarity index 100% rename from lib/hooks/useInitialList.tsx rename to client/shared/lists/useInitialList.tsx diff --git a/lib/hooks/useLazyRenderedList.tsx b/client/shared/lists/useLazyRenderedList.tsx similarity index 100% rename from lib/hooks/useLazyRenderedList.tsx rename to client/shared/lists/useLazyRenderedList.tsx diff --git a/lib/metadata/__snapshots__/generate.spec.ts.snap b/client/shared/metadata/__snapshots__/generate.spec.ts.snap similarity index 100% rename from lib/metadata/__snapshots__/generate.spec.ts.snap rename to client/shared/metadata/__snapshots__/generate.spec.ts.snap diff --git a/lib/metadata/compileValue.ts b/client/shared/metadata/compile-value.ts similarity index 100% rename from lib/metadata/compileValue.ts rename to client/shared/metadata/compile-value.ts diff --git a/lib/metadata/generateProductSchema.ts b/client/shared/metadata/generate-product-schema.ts similarity index 100% rename from lib/metadata/generateProductSchema.ts rename to client/shared/metadata/generate-product-schema.ts diff --git a/lib/metadata/generate.spec.ts b/client/shared/metadata/generate.spec.ts similarity index 100% rename from lib/metadata/generate.spec.ts rename to client/shared/metadata/generate.spec.ts diff --git a/lib/metadata/generate.ts b/client/shared/metadata/generate.ts similarity index 78% rename from lib/metadata/generate.ts rename to client/shared/metadata/generate.ts index e89348fd3c..efbabd1ce1 100644 --- a/lib/metadata/generate.ts +++ b/client/shared/metadata/generate.ts @@ -3,14 +3,15 @@ import type { RouteParams } from 'nextjs/types'; import type { Route } from 'nextjs-routes'; +import getChainTitle from 'client/shared/chain/get-chain-title'; +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; -import getNetworkTitle from 'lib/networks/getNetworkTitle'; -import { currencyUnits } from 'lib/units'; -import compileValue from './compileValue'; -import generateProductSchema from './generateProductSchema'; -import getCanonicalUrl from './getCanonicalUrl'; -import getPageOgType from './getPageOgType'; +import compileValue from './compile-value'; +import generateProductSchema from './generate-product-schema'; +import getCanonicalUrl from './get-canonical-url'; +import getPageOgType from './get-page-og-type'; import * as templates from './templates'; export default function generate(route: RouteParams, apiData: ApiData = null): Metadata { @@ -21,7 +22,7 @@ export default function generate(route: Rout ...route.query, ...apiData, network_name: config.chain.name, - network_title: getNetworkTitle(), + network_title: getChainTitle(), network_gwei: currencyUnits.gwei, id_cap: idCap, }; diff --git a/lib/metadata/getCanonicalUrl.ts b/client/shared/metadata/get-canonical-url.ts similarity index 100% rename from lib/metadata/getCanonicalUrl.ts rename to client/shared/metadata/get-canonical-url.ts diff --git a/lib/metadata/getPageOgType.ts b/client/shared/metadata/get-page-og-type.ts similarity index 82% rename from lib/metadata/getPageOgType.ts rename to client/shared/metadata/get-page-og-type.ts index ada871b6ff..1e8ad56f75 100644 --- a/lib/metadata/getPageOgType.ts +++ b/client/shared/metadata/get-page-og-type.ts @@ -41,7 +41,6 @@ const OG_TYPE_DICT: Record = { '/withdrawals': 'Root page', '/txn-withdrawals': 'Root page', '/visualize/sol2uml': 'Regular page', - '/csv-export': 'Regular page', '/deposits': 'Root page', '/output-roots': 'Root page', '/dispute-games': 'Root page', @@ -70,19 +69,19 @@ const OG_TYPE_DICT: Record = { '/operation/[id]': 'Regular page', '/cc/tx/[hash]': 'Regular page', '/cross-chain-tx/[id]': 'Regular page', + '/ictt-users': 'Root page', // multichain routes - '/chain/[chain_slug]/accounts/label/[slug]': 'Root page', - '/chain/[chain_slug]/advanced-filter': 'Regular page', - '/chain/[chain_slug]/block/[height_or_hash]': 'Regular page', - '/chain/[chain_slug]/block/countdown': 'Regular page', - '/chain/[chain_slug]/block/countdown/[height]': 'Regular page', - '/chain/[chain_slug]/csv-export': 'Regular page', - '/chain/[chain_slug]/op/[hash]': 'Regular page', - '/chain/[chain_slug]/token/[hash]': 'Regular page', - '/chain/[chain_slug]/token/[hash]/instance/[id]': 'Regular page', - '/chain/[chain_slug]/tx/[hash]': 'Regular page', - '/chain/[chain_slug]/visualize/sol2uml': 'Regular page', + '/chain/[chain_slug_or_id]/accounts/label/[slug]': 'Root page', + '/chain/[chain_slug_or_id]/advanced-filter': 'Regular page', + '/chain/[chain_slug_or_id]/block/[height_or_hash]': 'Regular page', + '/chain/[chain_slug_or_id]/block/countdown': 'Regular page', + '/chain/[chain_slug_or_id]/block/countdown/[height]': 'Regular page', + '/chain/[chain_slug_or_id]/op/[hash]': 'Regular page', + '/chain/[chain_slug_or_id]/token/[hash]': 'Regular page', + '/chain/[chain_slug_or_id]/token/[hash]/instance/[id]': 'Regular page', + '/chain/[chain_slug_or_id]/tx/[hash]': 'Regular page', + '/chain/[chain_slug_or_id]/visualize/sol2uml': 'Regular page', '/ecosystems': 'Root page', // service routes, added only to make typescript happy diff --git a/lib/metadata/index.ts b/client/shared/metadata/index.ts similarity index 100% rename from lib/metadata/index.ts rename to client/shared/metadata/index.ts diff --git a/lib/metadata/templates/description.ts b/client/shared/metadata/templates/description.ts similarity index 86% rename from lib/metadata/templates/description.ts rename to client/shared/metadata/templates/description.ts index e405a66c21..f1c0c986e6 100644 --- a/lib/metadata/templates/description.ts +++ b/client/shared/metadata/templates/description.ts @@ -44,7 +44,6 @@ const TEMPLATE_MAP: Record = { '/withdrawals': DEFAULT_TEMPLATE, '/txn-withdrawals': DEFAULT_TEMPLATE, '/visualize/sol2uml': DEFAULT_TEMPLATE, - '/csv-export': DEFAULT_TEMPLATE, '/deposits': DEFAULT_TEMPLATE, '/output-roots': DEFAULT_TEMPLATE, '/dispute-games': DEFAULT_TEMPLATE, @@ -73,19 +72,19 @@ const TEMPLATE_MAP: Record = { '/operation/[id]': DEFAULT_TEMPLATE, '/cc/tx/[hash]': DEFAULT_TEMPLATE, '/cross-chain-tx/[id]': DEFAULT_TEMPLATE, + '/ictt-users': DEFAULT_TEMPLATE, // multichain routes - '/chain/[chain_slug]/accounts/label/[slug]': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/advanced-filter': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/block/[height_or_hash]': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/block/countdown': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/block/countdown/[height]': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/csv-export': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/op/[hash]': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/token/[hash]': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/token/[hash]/instance/[id]': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/tx/[hash]': DEFAULT_TEMPLATE, - '/chain/[chain_slug]/visualize/sol2uml': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/accounts/label/[slug]': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/advanced-filter': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/block/[height_or_hash]': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/block/countdown': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/block/countdown/[height]': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/op/[hash]': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/token/[hash]': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/token/[hash]/instance/[id]': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/tx/[hash]': DEFAULT_TEMPLATE, + '/chain/[chain_slug_or_id]/visualize/sol2uml': DEFAULT_TEMPLATE, '/ecosystems': DEFAULT_TEMPLATE, // service routes, added only to make typescript happy diff --git a/lib/metadata/templates/index.ts b/client/shared/metadata/templates/index.ts similarity index 100% rename from lib/metadata/templates/index.ts rename to client/shared/metadata/templates/index.ts diff --git a/lib/metadata/templates/title.ts b/client/shared/metadata/templates/title.ts similarity index 86% rename from lib/metadata/templates/title.ts rename to client/shared/metadata/templates/title.ts index eaa77f6af5..940087d294 100644 --- a/lib/metadata/templates/title.ts +++ b/client/shared/metadata/templates/title.ts @@ -46,7 +46,6 @@ const TEMPLATE_MAP: Record = { '/withdrawals': '%network_name% withdrawals - track on %network_name% explorer', '/txn-withdrawals': `${ layerLabels.current } to ${ layerLabels.parent } message relayer`, '/visualize/sol2uml': '%network_name% Solidity UML diagram', - '/csv-export': '%network_name% export data to CSV', '/deposits': '%network_name% deposits - track on %network_name% explorer', '/output-roots': '%network_name% output roots', '/dispute-games': '%network_name% dispute games', @@ -75,19 +74,19 @@ const TEMPLATE_MAP: Record = { '/operation/[id]': '%network_name% operation %id%', '/cc/tx/[hash]': '%network_name% cross-chain transaction %hash% details', '/cross-chain-tx/[id]': '%network_name% cross-chain transaction %id% details', + '/ictt-users': '%network_name% ICTT users', // multichain routes - '/chain/[chain_slug]/accounts/label/[slug]': '%network_name% addresses search by label', - '/chain/[chain_slug]/advanced-filter': '%network_name% advanced filter', - '/chain/[chain_slug]/block/[height_or_hash]': '%network_name% block %height_or_hash% details', - '/chain/[chain_slug]/block/countdown': '%network_name% block countdown index', - '/chain/[chain_slug]/block/countdown/[height]': '%network_name% block %height% countdown', - '/chain/[chain_slug]/csv-export': '%network_name% export data to CSV', - '/chain/[chain_slug]/op/[hash]': '%network_name% user operation %hash% details', - '/chain/[chain_slug]/token/[hash]': '%network_name% token details', - '/chain/[chain_slug]/token/[hash]/instance/[id]': '%network_name% token NFT instance', - '/chain/[chain_slug]/tx/[hash]': '%network_name% transaction %hash% details', - '/chain/[chain_slug]/visualize/sol2uml': '%network_name% Solidity UML diagram', + '/chain/[chain_slug_or_id]/accounts/label/[slug]': '%network_name% addresses search by label', + '/chain/[chain_slug_or_id]/advanced-filter': '%network_name% advanced filter', + '/chain/[chain_slug_or_id]/block/[height_or_hash]': '%network_name% block %height_or_hash% details', + '/chain/[chain_slug_or_id]/block/countdown': '%network_name% block countdown index', + '/chain/[chain_slug_or_id]/block/countdown/[height]': '%network_name% block %height% countdown', + '/chain/[chain_slug_or_id]/op/[hash]': '%network_name% user operation %hash% details', + '/chain/[chain_slug_or_id]/token/[hash]': '%network_name% token details', + '/chain/[chain_slug_or_id]/token/[hash]/instance/[id]': '%network_name% token NFT instance', + '/chain/[chain_slug_or_id]/tx/[hash]': '%network_name% transaction %hash% details', + '/chain/[chain_slug_or_id]/visualize/sol2uml': '%network_name% Solidity UML diagram', '/ecosystems': '%network_name% ecosystems', // service routes, added only to make typescript happy diff --git a/lib/metadata/types.ts b/client/shared/metadata/types.ts similarity index 100% rename from lib/metadata/types.ts rename to client/shared/metadata/types.ts diff --git a/lib/metadata/update.ts b/client/shared/metadata/update.ts similarity index 100% rename from lib/metadata/update.ts rename to client/shared/metadata/update.ts diff --git a/lib/rollbar/index.tsx b/client/shared/monitoring/rollbar/index.tsx similarity index 93% rename from lib/rollbar/index.tsx rename to client/shared/monitoring/rollbar/index.tsx index b434bd33d2..bd239c4910 100644 --- a/lib/rollbar/index.tsx +++ b/client/shared/monitoring/rollbar/index.tsx @@ -2,9 +2,10 @@ import { Provider as DefaultProvider, useRollbar as useRollbarDefault } from '@r import type React from 'react'; import type { Configuration } from 'rollbar'; +import { ABSENT_PARAM_ERROR_MESSAGE } from 'client/shared/errors/throw-on-absent-param-error'; +import { RESOURCE_LOAD_ERROR_MESSAGE } from 'client/shared/errors/throw-on-resource-load-error'; + import config from 'configs/app'; -import { ABSENT_PARAM_ERROR_MESSAGE } from 'lib/errors/throwOnAbsentParamError'; -import { RESOURCE_LOAD_ERROR_MESSAGE } from 'lib/errors/throwOnResourceLoadError'; import { isBot, isHeadlessBrowser, isNextJsChunkError, getRequestInfo, getExceptionClass, getExceptionOriginFileName } from './utils'; diff --git a/lib/rollbar/utils.ts b/client/shared/monitoring/rollbar/utils.ts similarity index 100% rename from lib/rollbar/utils.ts rename to client/shared/monitoring/rollbar/utils.ts diff --git a/lib/getFilterValueFromQuery.ts b/client/shared/router/get-filter-value-from-query.ts similarity index 100% rename from lib/getFilterValueFromQuery.ts rename to client/shared/router/get-filter-value-from-query.ts diff --git a/lib/getFilterValuesFromQuery.ts b/client/shared/router/get-filter-values-from-query.ts similarity index 83% rename from lib/getFilterValuesFromQuery.ts rename to client/shared/router/get-filter-values-from-query.ts index 1ba9243d63..d39ccb3f53 100644 --- a/lib/getFilterValuesFromQuery.ts +++ b/client/shared/router/get-filter-values-from-query.ts @@ -1,4 +1,4 @@ -import getValuesArrayFromQuery from './getValuesArrayFromQuery'; +import getValuesArrayFromQuery from './get-values-array-from-query'; export default function getFilterValue(filterValues: ReadonlyArray, val: string | Array | undefined) { const valArray = getValuesArrayFromQuery(val); diff --git a/lib/router/getQueryParamString.ts b/client/shared/router/get-query-param-string.ts similarity index 100% rename from lib/router/getQueryParamString.ts rename to client/shared/router/get-query-param-string.ts diff --git a/lib/getValuesArrayFromQuery.ts b/client/shared/router/get-values-array-from-query.ts similarity index 100% rename from lib/getValuesArrayFromQuery.ts rename to client/shared/router/get-values-array-from-query.ts diff --git a/lib/router/removeQueryParam.ts b/client/shared/router/remove-query-param.ts similarity index 100% rename from lib/router/removeQueryParam.ts rename to client/shared/router/remove-query-param.ts diff --git a/client/shared/router/types.ts b/client/shared/router/types.ts new file mode 100644 index 0000000000..5956eb711e --- /dev/null +++ b/client/shared/router/types.ts @@ -0,0 +1 @@ +export type NextJsQueryParam = string | Array | undefined; diff --git a/lib/router/updateQueryParam.ts b/client/shared/router/update-query-param.ts similarity index 100% rename from lib/router/updateQueryParam.ts rename to client/shared/router/update-query-param.ts diff --git a/lib/router/useEtherscanRedirects.ts b/client/shared/router/useEtherscanRedirects.ts similarity index 98% rename from lib/router/useEtherscanRedirects.ts rename to client/shared/router/useEtherscanRedirects.ts index b41258ee83..9ebbddc5a4 100644 --- a/lib/router/useEtherscanRedirects.ts +++ b/client/shared/router/useEtherscanRedirects.ts @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import React from 'react'; -import getQueryParamString from './getQueryParamString'; +import getQueryParamString from './get-query-param-string'; export default function useEtherscanRedirects() { const router = useRouter(); diff --git a/lib/router/useQueryParams.ts b/client/shared/router/useQueryParams.ts similarity index 92% rename from lib/router/useQueryParams.ts rename to client/shared/router/useQueryParams.ts index dcec65c385..01bcc8368a 100644 --- a/lib/router/useQueryParams.ts +++ b/client/shared/router/useQueryParams.ts @@ -4,7 +4,7 @@ import { useCallback } from 'react'; export function useQueryParams() { const router = useRouter(); - const updateQuery = useCallback((updates: Record, replace = false) => { + const updateQuery = useCallback((updates: Record>, replace = false) => { const newQuery = { ...router.query }; Object.entries(updates).forEach(([ key, value ]) => { diff --git a/lib/cookies.ts b/client/shared/storage/cookies.ts similarity index 97% rename from lib/cookies.ts rename to client/shared/storage/cookies.ts index fb09e0128f..6a026c5239 100644 --- a/lib/cookies.ts +++ b/client/shared/storage/cookies.ts @@ -66,7 +66,7 @@ function isDisallowedInPrivateMode(name: NAMES): boolean { /** * Checks if the app is currently in private mode by reading the APP_PROFILE cookie. */ -function isPrivateMode(serverCookie?: string): boolean { +export function isPrivateMode(serverCookie?: string): boolean { const appProfile = get(NAMES.APP_PROFILE, serverCookie); return appProfile === 'private'; } diff --git a/lib/capitalizeFirstLetter.ts b/client/shared/text/capitalize-first-letter.ts similarity index 100% rename from lib/capitalizeFirstLetter.ts rename to client/shared/text/capitalize-first-letter.ts diff --git a/lib/escapeRegExp.ts b/client/shared/text/escape-reg-exp.ts similarity index 100% rename from lib/escapeRegExp.ts rename to client/shared/text/escape-reg-exp.ts diff --git a/lib/highlightText.ts b/client/shared/text/highlight-text.ts similarity index 77% rename from lib/highlightText.ts rename to client/shared/text/highlight-text.ts index 85bfe91066..2b42962e9b 100644 --- a/lib/highlightText.ts +++ b/client/shared/text/highlight-text.ts @@ -1,6 +1,6 @@ import xss from 'xss'; -import escapeRegExp from 'lib/escapeRegExp'; +import escapeRegExp from 'client/shared/text/escape-reg-exp'; export default function highlightText(text: string, query: string) { const regex = new RegExp('(' + escapeRegExp(query) + ')', 'gi'); diff --git a/lib/shortenString.ts b/client/shared/text/shorten-string.ts similarity index 100% rename from lib/shortenString.ts rename to client/shared/text/shorten-string.ts diff --git a/lib/base64ToHex.ts b/client/shared/transformers/base64-to-hex.ts similarity index 80% rename from lib/base64ToHex.ts rename to client/shared/transformers/base64-to-hex.ts index fa719a0a9c..bf6560ef81 100644 --- a/lib/base64ToHex.ts +++ b/client/shared/transformers/base64-to-hex.ts @@ -1,4 +1,4 @@ -import bytesToHex from './bytesToHex'; +import bytesToHex from './bytes-to-hex'; export default function base64ToHex(base64: string): string { const bytes = Uint8Array.from(atob(base64), c => c.charCodeAt(0)); diff --git a/lib/bytesToBase64.ts b/client/shared/transformers/bytes-to-base64.ts similarity index 100% rename from lib/bytesToBase64.ts rename to client/shared/transformers/bytes-to-base64.ts diff --git a/lib/bytesToHex.ts b/client/shared/transformers/bytes-to-hex.ts similarity index 100% rename from lib/bytesToHex.ts rename to client/shared/transformers/bytes-to-hex.ts diff --git a/lib/hexToAddress.ts b/client/shared/transformers/hex-to-address.ts similarity index 100% rename from lib/hexToAddress.ts rename to client/shared/transformers/hex-to-address.ts diff --git a/lib/hexToBase64.ts b/client/shared/transformers/hex-to-base64.ts similarity index 57% rename from lib/hexToBase64.ts rename to client/shared/transformers/hex-to-base64.ts index 6fced2dc39..8f7fe62d56 100644 --- a/lib/hexToBase64.ts +++ b/client/shared/transformers/hex-to-base64.ts @@ -1,5 +1,5 @@ -import bytesToBase64 from './bytesToBase64'; -import hexToBytes from './hexToBytes'; +import bytesToBase64 from './bytes-to-base64'; +import hexToBytes from './hex-to-bytes'; export default function hexToBase64(hex: string) { const bytes = hexToBytes(hex); diff --git a/lib/hexToBytes.ts b/client/shared/transformers/hex-to-bytes.ts similarity index 100% rename from lib/hexToBytes.ts rename to client/shared/transformers/hex-to-bytes.ts diff --git a/lib/hexToDecimal.ts b/client/shared/transformers/hex-to-decimal.ts similarity index 100% rename from lib/hexToDecimal.ts rename to client/shared/transformers/hex-to-decimal.ts diff --git a/lib/hexToUtf8.ts b/client/shared/transformers/hex-to-utf8.ts similarity index 71% rename from lib/hexToUtf8.ts rename to client/shared/transformers/hex-to-utf8.ts index 95e40ba090..94da63f5fa 100644 --- a/lib/hexToUtf8.ts +++ b/client/shared/transformers/hex-to-utf8.ts @@ -1,4 +1,4 @@ -import hexToBytes from 'lib/hexToBytes'; +import hexToBytes from 'client/shared/transformers/hex-to-bytes'; export default function hexToUtf8(hex: string) { const utf8decoder = new TextDecoder(); diff --git a/lib/delay.ts b/client/shared/utils/delay.ts similarity index 100% rename from lib/delay.ts rename to client/shared/utils/delay.ts diff --git a/lib/isMetaKey.tsx b/client/shared/utils/is-meta-key.ts similarity index 100% rename from lib/isMetaKey.tsx rename to client/shared/utils/is-meta-key.ts diff --git a/lib/web3/detectWallet.ts b/client/shared/web3/detect-wallet.ts similarity index 100% rename from lib/web3/detectWallet.ts rename to client/shared/web3/detect-wallet.ts diff --git a/lib/web3/useAddChain.tsx b/client/shared/web3/useAddChain.tsx similarity index 96% rename from lib/web3/useAddChain.tsx rename to client/shared/web3/useAddChain.tsx index 33503443f2..b57acfaa6a 100644 --- a/lib/web3/useAddChain.tsx +++ b/client/shared/web3/useAddChain.tsx @@ -3,9 +3,9 @@ import type { AddEthereumChainParameter } from 'viem'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; +import useRewardsActivity from 'lib/hooks/useRewardsActivity'; import { SECOND } from 'toolkit/utils/consts'; -import useRewardsActivity from '../hooks/useRewardsActivity'; import useProvider from './useProvider'; import { getHexadecimalChainId } from './utils'; diff --git a/lib/web3/useAddChainClick.ts b/client/shared/web3/useAddChainClick.ts similarity index 94% rename from lib/web3/useAddChainClick.ts rename to client/shared/web3/useAddChainClick.ts index fddb2f29a3..07d26cede0 100644 --- a/lib/web3/useAddChainClick.ts +++ b/client/shared/web3/useAddChainClick.ts @@ -1,6 +1,7 @@ import React from 'react'; -import * as mixpanel from 'lib/mixpanel/index'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import { toaster } from 'toolkit/chakra/toaster'; import useAddChain from './useAddChain'; diff --git a/lib/web3/useDetectWalletEip6963.ts b/client/shared/web3/useDetectWalletEip6963.ts similarity index 100% rename from lib/web3/useDetectWalletEip6963.ts rename to client/shared/web3/useDetectWalletEip6963.ts diff --git a/lib/web3/useProvider.tsx b/client/shared/web3/useProvider.tsx similarity index 98% rename from lib/web3/useProvider.tsx rename to client/shared/web3/useProvider.tsx index ac95440b78..cdb29a8ce0 100644 --- a/lib/web3/useProvider.tsx +++ b/client/shared/web3/useProvider.tsx @@ -5,7 +5,7 @@ import { getFeaturePayload } from 'configs/app/features/types'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import detectWallet from './detectWallet'; +import detectWallet from './detect-wallet'; import useDetectWalletEip6963 from './useDetectWalletEip6963'; export default function useProvider() { diff --git a/lib/web3/useSwitchChain.tsx b/client/shared/web3/useSwitchChain.tsx similarity index 100% rename from lib/web3/useSwitchChain.tsx rename to client/shared/web3/useSwitchChain.tsx diff --git a/lib/web3/useSwitchOrAddChain.tsx b/client/shared/web3/useSwitchOrAddChain.tsx similarity index 94% rename from lib/web3/useSwitchOrAddChain.tsx rename to client/shared/web3/useSwitchOrAddChain.tsx index 00536e6f33..e749292d97 100644 --- a/lib/web3/useSwitchOrAddChain.tsx +++ b/client/shared/web3/useSwitchOrAddChain.tsx @@ -1,8 +1,9 @@ import { get } from 'es-toolkit/compat'; import React from 'react'; +import getErrorObj from 'client/shared/errors/get-error-obj'; + import type config from 'configs/app'; -import getErrorObj from 'lib/errors/getErrorObj'; import useAddChain from './useAddChain'; import useProvider from './useProvider'; diff --git a/lib/web3/utils.ts b/client/shared/web3/utils.ts similarity index 100% rename from lib/web3/utils.ts rename to client/shared/web3/utils.ts diff --git a/lib/web3/wallets.ts b/client/shared/web3/wallets.ts similarity index 100% rename from lib/web3/wallets.ts rename to client/shared/web3/wallets.ts diff --git a/ui/shared/entities/address/AddressEntity.pw.tsx b/client/slices/address/components/entity/AddressEntity.pw.tsx similarity index 98% rename from ui/shared/entities/address/AddressEntity.pw.tsx rename to client/slices/address/components/entity/AddressEntity.pw.tsx index 2f467bd4bd..a0ac2ee0cd 100644 --- a/ui/shared/entities/address/AddressEntity.pw.tsx +++ b/client/slices/address/components/entity/AddressEntity.pw.tsx @@ -2,9 +2,11 @@ import { Box } from '@chakra-ui/react'; import type { BrowserContext } from '@playwright/test'; import React from 'react'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import * as cookies from 'lib/cookies'; import * as addressMock from 'mocks/address/address'; import * as implementationsMock from 'mocks/address/implementations'; import * as metadataMock from 'mocks/metadata/address'; diff --git a/ui/shared/entities/address/AddressEntity.tsx b/client/slices/address/components/entity/AddressEntity.tsx similarity index 95% rename from ui/shared/entities/address/AddressEntity.tsx rename to client/slices/address/components/entity/AddressEntity.tsx index ee05c3982d..28fa2a5c5a 100644 --- a/ui/shared/entities/address/AddressEntity.tsx +++ b/client/slices/address/components/entity/AddressEntity.tsx @@ -1,24 +1,25 @@ import { Box, Flex, chakra, VStack } from '@chakra-ui/react'; import React from 'react'; -import type { AddressParam } from 'types/api/addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; import { route } from 'nextjs/routes'; -import { toBech32Address } from 'lib/address/bech32'; -import { useAddressHighlightContext } from 'lib/contexts/addressHighlight'; +import { useAddressHighlightContext } from 'client/slices/address/contexts/address-highlight'; +import { toBech32Address } from 'client/slices/address/utils/bech32'; + import { useSettingsContext } from 'lib/contexts/settings'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import * as EntityBase from 'ui/shared/entities/base/components'; +import { distributeEntityProps, getContentProps, getIconProps } from 'ui/shared/entities/base/utils'; import { getTagName } from 'ui/shared/EntityTags/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; import type { IconName } from 'ui/shared/IconSvg'; -import { distributeEntityProps, getContentProps, getIconProps } from '../base/utils'; +import AddressIconDelegated from '../icon/AddressIconDelegated'; +import AddressIdenticon from '../icon/AddressIdenticon'; import AddressEntityContentProxy from './AddressEntityContentProxy'; -import AddressIconDelegated from './AddressIconDelegated'; -import AddressIdenticon from './AddressIdenticon'; type LinkProps = EntityBase.LinkBaseProps & Pick; diff --git a/ui/shared/entities/address/AddressEntityContentProxy.tsx b/client/slices/address/components/entity/AddressEntityContentProxy.tsx similarity index 100% rename from ui/shared/entities/address/AddressEntityContentProxy.tsx rename to client/slices/address/components/entity/AddressEntityContentProxy.tsx diff --git a/ui/shared/entities/address/AddressEntityExternal.tsx b/client/slices/address/components/entity/AddressEntityExternal.tsx similarity index 100% rename from ui/shared/entities/address/AddressEntityExternal.tsx rename to client/slices/address/components/entity/AddressEntityExternal.tsx diff --git a/ui/shared/entities/address/AddressEntityInterchain.tsx b/client/slices/address/components/entity/AddressEntityInterchain.tsx similarity index 100% rename from ui/shared/entities/address/AddressEntityInterchain.tsx rename to client/slices/address/components/entity/AddressEntityInterchain.tsx diff --git a/ui/shared/entities/address/AddressEntityWithTokenFilter.tsx b/client/slices/address/components/entity/AddressEntityWithTokenFilter.tsx similarity index 100% rename from ui/shared/entities/address/AddressEntityWithTokenFilter.tsx rename to client/slices/address/components/entity/AddressEntityWithTokenFilter.tsx diff --git a/ui/shared/entities/address/AddressStringOrParam.tsx b/client/slices/address/components/entity/AddressStringOrParam.tsx similarity index 87% rename from ui/shared/entities/address/AddressStringOrParam.tsx rename to client/slices/address/components/entity/AddressStringOrParam.tsx index 6005339e90..5fcd58047a 100644 --- a/ui/shared/entities/address/AddressStringOrParam.tsx +++ b/client/slices/address/components/entity/AddressStringOrParam.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { AddressParamBasic } from 'types/api/addressParams'; +import type { AddressParamBasic } from 'client/slices/address/types/api'; import AddressEntity from './AddressEntity'; import type { EntityProps } from './AddressEntity'; diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_dark-color-mode_delegated-address-dark-mode-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_dark-color-mode_delegated-address-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_dark-color-mode_delegated-address-dark-mode-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_dark-color-mode_delegated-address-dark-mode-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_bech32-format-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_bech32-format-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_bech32-format-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_bech32-format-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_contract-unverified-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-verified-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_contract-verified-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_contract-verified-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_contract-verified-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_customization-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_customization-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_customization-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_customization-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_delegated-address-dark-mode-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_delegated-address-dark-mode-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_delegated-address-dark-mode-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_delegated-address-dark-mode-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_external-link-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_hover-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_hover-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_hover-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_hover-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_loading-with-alias-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_loading-with-alias-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_loading-with-alias-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_loading-with-alias-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_loading-without-alias-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_loading-without-alias-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_loading-without-alias-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_loading-without-alias-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_no-link-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_no-link-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_no-link-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_no-link-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-implementation-name-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-implementation-name-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-implementation-name-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-implementation-name-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-multiple-implementations-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-multiple-implementations-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-multiple-implementations-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-multiple-implementations-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-name-tag-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-name-tag-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-name-tag-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-with-name-tag-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-any-name-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-any-name-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-any-name-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-any-name-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-implementation-name-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-implementation-name-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-implementation-name-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_proxy-contract-without-implementation-name-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_shield-contract-with-icon-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_shield-contract-with-icon-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_shield-contract-with-icon-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_shield-contract-with-icon-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_shield-regular-address-with-image-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_shield-regular-address-with-image-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_shield-regular-address-with-image-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_shield-regular-address-with-image-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_variant-content-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_variant-content-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_variant-content-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_variant-content-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_variant-subheading-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_variant-subheading-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_variant-subheading-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_variant-subheading-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_with-ENS-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_with-ENS-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_with-ENS-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_with-ENS-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_with-cex-deposit-tag-default-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_with-cex-deposit-tag-default-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_with-cex-deposit-tag-default-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_with-cex-deposit-tag-default-1.png diff --git a/ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_with-name-tag-1.png b/client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_with-name-tag-1.png similarity index 100% rename from ui/shared/entities/address/__screenshots__/AddressEntity.pw.tsx_default_with-name-tag-1.png rename to client/slices/address/components/entity/__screenshots__/AddressEntity.pw.tsx_default_with-name-tag-1.png diff --git a/ui/shared/address/AddressFromTo.pw.tsx b/client/slices/address/components/from-to/AddressFromTo.pw.tsx similarity index 100% rename from ui/shared/address/AddressFromTo.pw.tsx rename to client/slices/address/components/from-to/AddressFromTo.pw.tsx diff --git a/ui/shared/address/AddressFromTo.tsx b/client/slices/address/components/from-to/AddressFromTo.tsx similarity index 90% rename from ui/shared/address/AddressFromTo.tsx rename to client/slices/address/components/from-to/AddressFromTo.tsx index 75b41c30a4..90e0342b15 100644 --- a/ui/shared/address/AddressFromTo.tsx +++ b/client/slices/address/components/from-to/AddressFromTo.tsx @@ -2,13 +2,14 @@ import type { ConditionalValue } from '@chakra-ui/react'; import { Flex, Grid, chakra, useBreakpointValue } from '@chakra-ui/react'; import React from 'react'; -import type { EntityProps } from 'ui/shared/entities/address/AddressEntity'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import AddressEntityWithTokenFilter from 'ui/shared/entities/address/AddressEntityWithTokenFilter'; +import type { EntityProps } from 'client/slices/address/components/entity/AddressEntity'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import AddressEntityWithTokenFilter from 'client/slices/address/components/entity/AddressEntityWithTokenFilter'; +import { getTxCourseType } from 'client/slices/address/utils/tx'; + +import AddressEntityZetaChain from 'client/features/chain-variants/zeta-chain/components/AddressEntityZetaChain'; -import AddressEntityZetaChain from '../entities/address/AddressEntityZetaChain'; import AddressFromToIcon from './AddressFromToIcon'; -import { getTxCourseType } from './utils'; type Mode = 'compact' | 'long'; diff --git a/ui/shared/address/AddressFromToIcon.pw.tsx b/client/slices/address/components/from-to/AddressFromToIcon.pw.tsx similarity index 88% rename from ui/shared/address/AddressFromToIcon.pw.tsx rename to client/slices/address/components/from-to/AddressFromToIcon.pw.tsx index 16f6732fa6..e8ca465b91 100644 --- a/ui/shared/address/AddressFromToIcon.pw.tsx +++ b/client/slices/address/components/from-to/AddressFromToIcon.pw.tsx @@ -1,10 +1,11 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import type { TxCourseType } from 'client/slices/address/utils/tx'; + import { test, expect } from 'playwright/lib'; import AddressFromToIcon from './AddressFromToIcon'; -import type { TxCourseType } from './utils'; test.use({ viewport: { width: 36, height: 36 } }); diff --git a/ui/shared/address/AddressFromToIcon.tsx b/client/slices/address/components/from-to/AddressFromToIcon.tsx similarity index 95% rename from ui/shared/address/AddressFromToIcon.tsx rename to client/slices/address/components/from-to/AddressFromToIcon.tsx index 52a0c8538b..0fbabcb633 100644 --- a/ui/shared/address/AddressFromToIcon.tsx +++ b/client/slices/address/components/from-to/AddressFromToIcon.tsx @@ -1,11 +1,11 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; +import type { TxCourseType } from 'client/slices/address/utils/tx'; + import { Tooltip } from 'toolkit/chakra/tooltip'; import IconSvg from 'ui/shared/IconSvg'; -import type { TxCourseType } from './utils'; - interface Props { isLoading?: boolean; type: TxCourseType; diff --git a/ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_compact-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_compact-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_compact-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_compact-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_incoming-txn-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_incoming-txn-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_incoming-txn-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_incoming-txn-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_loading-state-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_loading-state-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_loading-state-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_loading-state-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_outgoing-txn-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_outgoing-txn-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromTo.pw.tsx_default_outgoing-txn-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromTo.pw.tsx_default_outgoing-txn-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_in-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_in-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_in-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_in-txn-type-dark-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_out-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_out-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_out-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_out-txn-type-dark-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_self-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_self-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_self-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_self-txn-type-dark-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_unspecified-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_unspecified-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_unspecified-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_dark-color-mode_unspecified-txn-type-dark-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_in-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_in-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_in-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_in-txn-type-dark-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_out-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_out-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_out-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_out-txn-type-dark-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_self-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_self-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_self-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_self-txn-type-dark-mode-1.png diff --git a/ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_unspecified-txn-type-dark-mode-1.png b/client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_unspecified-txn-type-dark-mode-1.png similarity index 100% rename from ui/shared/address/__screenshots__/AddressFromToIcon.pw.tsx_default_unspecified-txn-type-dark-mode-1.png rename to client/slices/address/components/from-to/__screenshots__/AddressFromToIcon.pw.tsx_default_unspecified-txn-type-dark-mode-1.png diff --git a/ui/shared/entities/address/AddressIconDelegated.tsx b/client/slices/address/components/icon/AddressIconDelegated.tsx similarity index 100% rename from ui/shared/entities/address/AddressIconDelegated.tsx rename to client/slices/address/components/icon/AddressIconDelegated.tsx diff --git a/ui/shared/entities/address/AddressIdenticon.tsx b/client/slices/address/components/icon/AddressIdenticon.tsx similarity index 86% rename from ui/shared/entities/address/AddressIdenticon.tsx rename to client/slices/address/components/icon/AddressIdenticon.tsx index 4197708f12..f76459ff8f 100644 --- a/ui/shared/entities/address/AddressIdenticon.tsx +++ b/client/slices/address/components/icon/AddressIdenticon.tsx @@ -2,10 +2,12 @@ import { Box } from '@chakra-ui/react'; import dynamic from 'next/dynamic'; import React from 'react'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; import { Image } from 'toolkit/chakra/image'; -import IdenticonGithub from 'ui/shared/IdenticonGithub'; + +import AddressIdenticonGithub from './AddressIdenticonGithub'; interface IconProps { hash: string; @@ -18,7 +20,7 @@ const Icon = dynamic( switch (type) { case 'github': { - return (props: IconProps) => ; + return (props: IconProps) => ; } case 'blockie': { @@ -58,7 +60,7 @@ const Icon = dynamic( } case 'nouns': { - const NounsIdenticon = (await import('./NounsIdenticon')).default; + const NounsIdenticon = (await import('./AddressIdenticonNouns')).default; return (props: IconProps) => { return ; diff --git a/ui/shared/IdenticonGithub.tsx b/client/slices/address/components/icon/AddressIdenticonGithub.tsx similarity index 88% rename from ui/shared/IdenticonGithub.tsx rename to client/slices/address/components/icon/AddressIdenticonGithub.tsx index 202c5e0e88..dda97127de 100644 --- a/ui/shared/IdenticonGithub.tsx +++ b/client/slices/address/components/icon/AddressIdenticonGithub.tsx @@ -21,7 +21,7 @@ interface Props { seed: string; } -const IdenticonGithub = ({ iconSize, seed }: Props) => { +const AddressIdenticonGithub = ({ iconSize, seed }: Props) => { return ( { ); }; -export default React.memo(chakra(IdenticonGithub)); +export default React.memo(chakra(AddressIdenticonGithub)); diff --git a/ui/shared/entities/address/NounsIdenticon.tsx b/client/slices/address/components/icon/AddressIdenticonNouns.tsx similarity index 90% rename from ui/shared/entities/address/NounsIdenticon.tsx rename to client/slices/address/components/icon/AddressIdenticonNouns.tsx index 6dd91bde5e..3e5176816d 100644 --- a/ui/shared/entities/address/NounsIdenticon.tsx +++ b/client/slices/address/components/icon/AddressIdenticonNouns.tsx @@ -7,7 +7,7 @@ import { Image } from 'toolkit/chakra/image'; const { palette } = ImageData; -interface NounsIdenticonProps { +interface Props { hash: string; size: number; } @@ -35,7 +35,7 @@ export const getNumberFromString = (input: string): number => { return sum * str.length; }; -const NounsIdenticon: React.FC = ({ hash, size }) => { +const AddressIdenticonNouns: React.FC = ({ hash, size }) => { const id = getNumberFromString(hash); const seed = getNounSeedFromBlockHash(id, MAGIC_HASH); @@ -56,4 +56,4 @@ const NounsIdenticon: React.FC = ({ hash, size }) => { ); }; -export default React.memo(NounsIdenticon); +export default React.memo(AddressIdenticonNouns); diff --git a/lib/contexts/addressHighlight.tsx b/client/slices/address/contexts/address-highlight.tsx similarity index 100% rename from lib/contexts/addressHighlight.tsx rename to client/slices/address/contexts/address-highlight.tsx diff --git a/ui/address/utils/useAddressCountersQuery.ts b/client/slices/address/hooks/useAddressCountersQuery.ts similarity index 86% rename from ui/address/utils/useAddressCountersQuery.ts rename to client/slices/address/hooks/useAddressCountersQuery.ts index 165b108394..b1fd214eb5 100644 --- a/ui/address/utils/useAddressCountersQuery.ts +++ b/client/slices/address/hooks/useAddressCountersQuery.ts @@ -1,13 +1,16 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; -import type { AddressCounters } from 'types/api/address'; +import type { AddressCounters } from 'client/slices/address/types/api'; import type { ClusterChainConfig } from 'types/multichain'; -import type { ResourceError } from 'lib/api/resources'; -import useApiQuery from 'lib/api/useApiQuery'; -import { publicClient } from 'lib/web3/client'; -import { ADDRESS_COUNTERS } from 'stubs/address'; +import useApiQuery from 'client/api/hooks/useApiQuery'; +import type { ResourceError } from 'client/api/resources'; + +import { ADDRESS_COUNTERS } from 'client/slices/address/stubs/address'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + import { GET_TRANSACTIONS_COUNT } from 'stubs/RPC'; type RpcResponseType = [ diff --git a/ui/address/utils/useAddressQuery.ts b/client/slices/address/hooks/useAddressQuery.ts similarity index 90% rename from ui/address/utils/useAddressQuery.ts rename to client/slices/address/hooks/useAddressQuery.ts index 29f43bf395..bc77ccd320 100644 --- a/ui/address/utils/useAddressQuery.ts +++ b/client/slices/address/hooks/useAddressQuery.ts @@ -2,13 +2,16 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; +import { retry } from 'client/api/hooks/useQueryClientConfig'; +import type { ResourceError } from 'client/api/resources'; + +import { ADDRESS_INFO } from 'client/slices/address/stubs/address'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; -import type { ResourceError } from 'lib/api/resources'; -import useApiQuery from 'lib/api/useApiQuery'; -import { retry } from 'lib/api/useQueryClientConfig'; -import { publicClient } from 'lib/web3/client'; -import { ADDRESS_INFO } from 'stubs/address'; import { GET_BALANCE } from 'stubs/RPC'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/ui/address/utils/useCheckAddressFormat.ts b/client/slices/address/hooks/useCheckAddressFormat.ts similarity index 89% rename from ui/address/utils/useCheckAddressFormat.ts rename to client/slices/address/hooks/useCheckAddressFormat.ts index f4b3315f09..7dbc79f555 100644 --- a/ui/address/utils/useCheckAddressFormat.ts +++ b/client/slices/address/hooks/useCheckAddressFormat.ts @@ -1,8 +1,9 @@ import { useRouter } from 'next/router'; import React from 'react'; +import { fromBech32Address, isBech32Address } from 'client/slices/address/utils/bech32'; + import config from 'configs/app'; -import { fromBech32Address, isBech32Address } from 'lib/address/bech32'; export default function useCheckAddressFormat(hash: string) { const router = useRouter(); diff --git a/ui/pages/Address.pw.tsx b/client/slices/address/pages/details/Address.pw.tsx similarity index 100% rename from ui/pages/Address.pw.tsx rename to client/slices/address/pages/details/Address.pw.tsx diff --git a/ui/pages/Address.tsx b/client/slices/address/pages/details/Address.tsx similarity index 83% rename from ui/pages/Address.tsx rename to client/slices/address/pages/details/Address.tsx index 6f07f7e1ab..f55ff0890f 100644 --- a/ui/pages/Address.tsx +++ b/client/slices/address/pages/details/Address.tsx @@ -5,56 +5,60 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; import type { EntityTag } from 'ui/shared/EntityTags/types'; +import useApiQuery from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import useAddressCountersQuery from 'client/slices/address/hooks/useAddressCountersQuery'; +import useAddressQuery from 'client/slices/address/hooks/useAddressQuery'; +import useCheckAddressFormat from 'client/slices/address/hooks/useCheckAddressFormat'; +import AddressBlocksValidated from 'client/slices/address/pages/details/blocks-validated/AddressBlocksValidated'; +import AddressCoinBalance from 'client/slices/address/pages/details/coin-balance/AddressCoinBalance'; +import AddressAlerts from 'client/slices/address/pages/details/info/AddressAlerts'; +import AddressDetails from 'client/slices/address/pages/details/info/AddressDetails'; +import AddressQrCode from 'client/slices/address/pages/details/info/AddressQrCode'; +import AddressInternalTxs from 'client/slices/address/pages/details/internal-txs/AddressInternalTxs'; +import AddressLogs from 'client/slices/address/pages/details/logs/AddressLogs'; +import AddressTokenTransfers, { ADDRESS_TOKEN_TRANSFERS_TAB_IDS } from 'client/slices/address/pages/details/token-transfers/AddressTokenTransfers'; +import AddressTokens from 'client/slices/address/pages/details/tokens/AddressTokens'; +import AddressTxs, { ADDRESS_TXS_TAB_IDS } from 'client/slices/address/pages/details/txs/AddressTxs'; +import AddressWithdrawals from 'client/slices/address/pages/details/withdrawals/AddressWithdrawals'; +import { ADDRESS_TABS_COUNTERS } from 'client/slices/address/stubs/address'; +import getCheckedSummedAddress from 'client/slices/address/utils/get-checked-summed-address'; + +import AddressFavoriteButton from 'client/features/account/pages/address/AddressFavoriteButton'; +import Address3rdPartyWidgets from 'client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgets'; +import useAddress3rdPartyWidgets from 'client/features/address-3rd-party-widgets/pages/address/useAddress3rdPartyWidgets'; +import useAddressMetadataInfoQuery from 'client/features/address-metadata/hooks/useAddressMetadataInfoQuery'; +import useAddressMetadataInitUpdate from 'client/features/address-metadata/hooks/useAddressMetadataInitUpdate'; +import useAddressProfileApiQuery from 'client/features/address-profile-api/hooks/useAddressProfileApiQuery'; +import AddressDeposits from 'client/features/chain-variants/beacon-chain/pages/address/AddressDeposits'; +import AddressEpochRewards from 'client/features/chain-variants/celo/pages/address/AddressEpochRewards'; +import AddressMud from 'client/features/chain-variants/mud/pages/address/AddressMud'; +import AddressMultichainInfoButton from 'client/features/multichain-button/pages/address/AddressMultichainInfoButton'; +import AddressClusters from 'client/features/name-services/clusters/pages/address/AddressClusters'; +import useCheckDomainNameParam from 'client/features/name-services/domains/hooks/useCheckDomainNameParam'; +import AddressEnsDomains from 'client/features/name-services/domains/pages/address/AddressEnsDomains'; +import SolidityscanReport from 'client/features/solidity-scan/components/SolidityscanReport'; +import AddressAccountHistory from 'client/features/tx-interpretation/noves/pages/address/AddressAccountHistory'; +import AddressUserOps from 'client/features/user-ops/pages/address/AddressUserOps'; +import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; + +import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import useEtherscanRedirects from 'client/shared/router/useEtherscanRedirects'; + import config from 'configs/app'; -import getCheckedSummedAddress from 'lib/address/getCheckedSummedAddress'; -import useAddressMetadataInfoQuery from 'lib/address/useAddressMetadataInfoQuery'; -import useAddressMetadataInitUpdate from 'lib/address/useAddressMetadataInitUpdate'; -import useApiQuery from 'lib/api/useApiQuery'; import { useAddressClusters } from 'lib/clusters/useAddressClusters'; -import useAddressProfileApiQuery from 'lib/hooks/useAddressProfileApiQuery'; import useIsSafeAddress from 'lib/hooks/useIsSafeAddress'; -import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useEtherscanRedirects from 'lib/router/useEtherscanRedirects'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; import useFetchXStarScore from 'lib/xStarScore/useFetchXStarScore'; -import { ADDRESS_TABS_COUNTERS } from 'stubs/address'; import { USER_OPS_ACCOUNT } from 'stubs/userOps'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import Address3rdPartyWidgets from 'ui/address/Address3rdPartyWidgets'; -import useAddress3rdPartyWidgets from 'ui/address/address3rdPartyWidgets/useAddress3rdPartyWidgets'; -import AddressAccountHistory from 'ui/address/AddressAccountHistory'; -import AddressBlocksValidated from 'ui/address/AddressBlocksValidated'; -import AddressCoinBalance from 'ui/address/AddressCoinBalance'; import AddressContract from 'ui/address/AddressContract'; -import AddressDeposits from 'ui/address/AddressDeposits'; -import AddressDetails from 'ui/address/AddressDetails'; -import AddressEpochRewards from 'ui/address/AddressEpochRewards'; -import AddressInternalTxs from 'ui/address/AddressInternalTxs'; -import AddressLogs from 'ui/address/AddressLogs'; -import AddressMud from 'ui/address/AddressMud'; -import AddressMultichainInfoButton from 'ui/address/AddressMultichainInfoButton'; -import AddressTokens from 'ui/address/AddressTokens'; -import AddressTokenTransfers, { ADDRESS_TOKEN_TRANSFERS_TAB_IDS } from 'ui/address/AddressTokenTransfers'; -import AddressTxs, { ADDRESS_TXS_TAB_IDS } from 'ui/address/AddressTxs'; -import AddressUserOps from 'ui/address/AddressUserOps'; -import AddressWithdrawals from 'ui/address/AddressWithdrawals'; -import AddressClusters from 'ui/address/clusters/AddressClusters'; import { CONTRACT_TAB_IDS } from 'ui/address/contract/utils'; -import AddressAlerts from 'ui/address/details/AddressAlerts'; -import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton'; -import AddressQrCode from 'ui/address/details/AddressQrCode'; -import AddressEnsDomains from 'ui/address/ensDomains/AddressEnsDomains'; -import SolidityscanReport from 'ui/address/SolidityscanReport'; -import useAddressCountersQuery from 'ui/address/utils/useAddressCountersQuery'; -import useAddressQuery from 'ui/address/utils/useAddressQuery'; -import useCheckAddressFormat from 'ui/address/utils/useCheckAddressFormat'; -import useCheckDomainNameParam from 'ui/address/utils/useCheckDomainNameParam'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import TextAd from 'ui/shared/ad/TextAd'; -import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import EntityTags from 'ui/shared/EntityTags/EntityTags'; import formatUserTags from 'ui/shared/EntityTags/formatUserTags'; @@ -285,7 +289,7 @@ const AddressPageContent = () => { addressTabsCountersQuery.data?.validations_count ? { id: 'blocks_validated', - title: `Blocks ${ getNetworkValidationActionText() }`, + title: `Blocks ${ getChainValidationActionText() }`, count: addressTabsCountersQuery.data?.validations_count, component: , } : @@ -459,7 +463,7 @@ const AddressPageContent = () => { icon={{ color: isSafeAddress ? { _light: 'black', _dark: 'white' } : undefined }} /> { !isLoading && addressQuery.data?.is_contract && addressQuery.data.token && - } + } { !isLoading && !addressQuery.data?.is_contract && config.features.account.isEnabled && ( ) } diff --git a/ui/address/testUtils/MockAddressPage.tsx b/client/slices/address/pages/details/AddressPageMock.tsx similarity index 89% rename from ui/address/testUtils/MockAddressPage.tsx rename to client/slices/address/pages/details/AddressPageMock.tsx index d136fee05a..63c2ada6ed 100644 --- a/ui/address/testUtils/MockAddressPage.tsx +++ b/client/slices/address/pages/details/AddressPageMock.tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; const MockAddressPage = ({ children }: { children: React.JSX.Element }): React.JSX.Element => { const router = useRouter(); diff --git a/ui/address/AddressBlocksValidated.tsx b/client/slices/address/pages/details/blocks-validated/AddressBlocksValidated.tsx similarity index 88% rename from ui/address/AddressBlocksValidated.tsx rename to client/slices/address/pages/details/blocks-validated/AddressBlocksValidated.tsx index cebe99b3c5..6cfa9a04a9 100644 --- a/ui/address/AddressBlocksValidated.tsx +++ b/client/slices/address/pages/details/blocks-validated/AddressBlocksValidated.tsx @@ -3,16 +3,19 @@ import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { AddressBlocksValidatedResponse } from 'types/api/address'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { AddressBlocksValidatedResponse } from 'client/slices/address/types/api'; + +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import { BLOCK } from 'client/slices/block/stubs/block'; + +import { currencyUnits } from 'client/shared/chain/units'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; import config from 'configs/app'; -import { getResourceKey } from 'lib/api/useApiQuery'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import { currencyUnits } from 'lib/units'; -import { BLOCK } from 'stubs/block'; import { generateListStub } from 'stubs/utils'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; @@ -22,8 +25,8 @@ import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; -import AddressBlocksValidatedListItem from './blocksValidated/AddressBlocksValidatedListItem'; -import AddressBlocksValidatedTableItem from './blocksValidated/AddressBlocksValidatedTableItem'; +import AddressBlocksValidatedListItem from './AddressBlocksValidatedListItem'; +import AddressBlocksValidatedTableItem from './AddressBlocksValidatedTableItem'; const OVERLOAD_COUNT = 75; diff --git a/ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx b/client/slices/address/pages/details/blocks-validated/AddressBlocksValidatedListItem.tsx similarity index 87% rename from ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx rename to client/slices/address/pages/details/blocks-validated/AddressBlocksValidatedListItem.tsx index 3a247c9069..3688e2e14a 100644 --- a/ui/address/blocksValidated/AddressBlocksValidatedListItem.tsx +++ b/client/slices/address/pages/details/blocks-validated/AddressBlocksValidatedListItem.tsx @@ -2,14 +2,16 @@ import { Text, Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; + +import BlockGasUsed from 'client/slices/block/components/BlockGasUsed'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import getBlockTotalReward from 'client/slices/block/utils/get-block-total-reward'; + +import { currencyUnits } from 'client/shared/chain/units'; import config from 'configs/app'; -import getBlockTotalReward from 'lib/block/getBlockTotalReward'; -import { currencyUnits } from 'lib/units'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import SimpleValue from 'ui/shared/value/SimpleValue'; diff --git a/ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx b/client/slices/address/pages/details/blocks-validated/AddressBlocksValidatedTableItem.tsx similarity index 87% rename from ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx rename to client/slices/address/pages/details/blocks-validated/AddressBlocksValidatedTableItem.tsx index 98cfb18061..0b299b48fa 100644 --- a/ui/address/blocksValidated/AddressBlocksValidatedTableItem.tsx +++ b/client/slices/address/pages/details/blocks-validated/AddressBlocksValidatedTableItem.tsx @@ -2,14 +2,15 @@ import { Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; + +import BlockGasUsed from 'client/slices/block/components/BlockGasUsed'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import getBlockTotalReward from 'client/slices/block/utils/get-block-total-reward'; import config from 'configs/app'; -import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import SimpleValue from 'ui/shared/value/SimpleValue'; diff --git a/ui/address/AddressCoinBalance.pw.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalance.pw.tsx similarity index 100% rename from ui/address/AddressCoinBalance.pw.tsx rename to client/slices/address/pages/details/coin-balance/AddressCoinBalance.pw.tsx diff --git a/ui/address/AddressCoinBalance.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalance.tsx similarity index 78% rename from ui/address/AddressCoinBalance.tsx rename to client/slices/address/pages/details/coin-balance/AddressCoinBalance.tsx index dd595fd6ce..e67185d106 100644 --- a/ui/address/AddressCoinBalance.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalance.tsx @@ -2,21 +2,24 @@ import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { AddressCoinBalanceHistoryResponse } from 'types/api/address'; - -import { getResourceKey } from 'lib/api/useApiQuery'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import { ADDRESS_COIN_BALANCE } from 'stubs/address'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { AddressCoinBalanceHistoryResponse } from 'client/slices/address/types/api'; + +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import { ADDRESS_COIN_BALANCE } from 'client/slices/address/stubs/address'; + +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import SocketAlert from 'ui/shared/SocketAlert'; -import AddressCoinBalanceChart from './coinBalance/AddressCoinBalanceChart'; -import AddressCoinBalanceHistory from './coinBalance/AddressCoinBalanceHistory'; +import AddressCoinBalanceChart from './AddressCoinBalanceChart'; +import AddressCoinBalanceHistory from './AddressCoinBalanceHistory'; type Props = { shouldRender?: boolean; diff --git a/ui/address/coinBalance/AddressCoinBalanceChart.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceChart.tsx similarity index 83% rename from ui/address/coinBalance/AddressCoinBalanceChart.tsx rename to client/slices/address/pages/details/coin-balance/AddressCoinBalanceChart.tsx index a661c4d8ff..10e9822b0c 100644 --- a/ui/address/coinBalance/AddressCoinBalanceChart.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceChart.tsx @@ -1,10 +1,12 @@ import BigNumber from 'bignumber.js'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; -import { currencyUnits } from 'lib/units'; -import { ChartWidget } from 'toolkit/components/charts/ChartWidget'; +import { LineChartWidget } from 'toolkit/components/charts/line/LineChartWidget'; import { useChartsConfig } from 'ui/shared/chart/config'; interface Props { @@ -37,13 +39,14 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => { }, [ chartsConfig, data ]); return ( - ); }; diff --git a/ui/address/coinBalance/AddressCoinBalanceHistory.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceHistory.tsx similarity index 93% rename from ui/address/coinBalance/AddressCoinBalanceHistory.tsx rename to client/slices/address/pages/details/coin-balance/AddressCoinBalanceHistory.tsx index a74e32ffc7..45bd152cbd 100644 --- a/ui/address/coinBalance/AddressCoinBalanceHistory.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceHistory.tsx @@ -2,12 +2,14 @@ import { Box } from '@chakra-ui/react'; import type { UseQueryResult } from '@tanstack/react-query'; import React from 'react'; -import type { AddressCoinBalanceHistoryResponse } from 'types/api/address'; +import type { AddressCoinBalanceHistoryResponse } from 'client/slices/address/types/api'; import type { PaginationParams } from 'ui/shared/pagination/types'; -import type { ResourceError } from 'lib/api/resources'; +import type { ResourceError } from 'client/api/resources'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import { currencyUnits } from 'lib/units'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/address/coinBalance/AddressCoinBalanceListItem.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceListItem.tsx similarity index 92% rename from ui/address/coinBalance/AddressCoinBalanceListItem.tsx rename to client/slices/address/pages/details/coin-balance/AddressCoinBalanceListItem.tsx index e1e6f1cb77..ae71a72424 100644 --- a/ui/address/coinBalance/AddressCoinBalanceListItem.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceListItem.tsx @@ -2,13 +2,14 @@ import { Stat, Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; +import type { AddressCoinBalanceHistoryItem } from 'client/slices/address/types/api'; import type { ClusterChainConfig } from 'types/multichain'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { ZERO } from 'toolkit/utils/consts'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceTableItem.tsx similarity index 91% rename from ui/address/coinBalance/AddressCoinBalanceTableItem.tsx rename to client/slices/address/pages/details/coin-balance/AddressCoinBalanceTableItem.tsx index 795b5d9dad..57480c3142 100644 --- a/ui/address/coinBalance/AddressCoinBalanceTableItem.tsx +++ b/client/slices/address/pages/details/coin-balance/AddressCoinBalanceTableItem.tsx @@ -2,14 +2,15 @@ import { Stat } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; +import type { AddressCoinBalanceHistoryItem } from 'client/slices/address/types/api'; import type { ClusterChainConfig } from 'types/multichain'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { ZERO } from 'toolkit/utils/consts'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png new file mode 100644 index 0000000000..53da5096ba Binary files /dev/null and b/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png b/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png new file mode 100644 index 0000000000..c89f861552 Binary files /dev/null and b/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png b/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png new file mode 100644 index 0000000000..3f5fe63da2 Binary files /dev/null and b/client/slices/address/pages/details/coin-balance/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/address/details/AddressAlerts.pw.tsx b/client/slices/address/pages/details/info/AddressAlerts.pw.tsx similarity index 100% rename from ui/address/details/AddressAlerts.pw.tsx rename to client/slices/address/pages/details/info/AddressAlerts.pw.tsx diff --git a/ui/address/details/AddressAlerts.tsx b/client/slices/address/pages/details/info/AddressAlerts.tsx similarity index 93% rename from ui/address/details/AddressAlerts.tsx rename to client/slices/address/pages/details/info/AddressAlerts.tsx index 92bbd04603..e009deb3cc 100644 --- a/ui/address/details/AddressAlerts.tsx +++ b/client/slices/address/pages/details/info/AddressAlerts.tsx @@ -1,7 +1,7 @@ import { Flex, chakra } from '@chakra-ui/react'; import React from 'react'; -import type { AddressMetadataTagFormatted } from 'types/client/addressMetadata'; +import type { AddressMetadataTagFormatted } from 'client/features/address-metadata/types/view'; import type { AlertProps } from 'toolkit/chakra/alert'; import { Alert } from 'toolkit/chakra/alert'; diff --git a/ui/address/details/AddressAlternativeFormat.tsx b/client/slices/address/pages/details/info/AddressAlternativeFormat.tsx similarity index 88% rename from ui/address/details/AddressAlternativeFormat.tsx rename to client/slices/address/pages/details/info/AddressAlternativeFormat.tsx index 023ed2c20e..3c0c5b816f 100644 --- a/ui/address/details/AddressAlternativeFormat.tsx +++ b/client/slices/address/pages/details/info/AddressAlternativeFormat.tsx @@ -1,10 +1,11 @@ import React from 'react'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import { BECH_32_SEPARATOR, toBech32Address } from 'client/slices/address/utils/bech32'; + import config from 'configs/app'; -import { BECH_32_SEPARATOR, toBech32Address } from 'lib/address/bech32'; import { useSettingsContext } from 'lib/contexts/settings'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; interface Props { isLoading?: boolean; diff --git a/ui/address/details/AddressBalance.tsx b/client/slices/address/pages/details/info/AddressBalance.tsx similarity index 87% rename from ui/address/details/AddressBalance.tsx rename to client/slices/address/pages/details/info/AddressBalance.tsx index dd26ef049d..efac00bb1f 100644 --- a/ui/address/details/AddressBalance.tsx +++ b/client/slices/address/pages/details/info/AddressBalance.tsx @@ -1,13 +1,15 @@ import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { Address } from 'types/api/address'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { Address } from 'client/slices/address/types/api'; + +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import { currencyUnits } from 'client/shared/chain/units'; -import { getResourceKey } from 'lib/api/useApiQuery'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import { currencyUnits } from 'lib/units'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import NativeTokenIcon from 'ui/shared/NativeTokenIcon'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/address/details/AddressCounterItem.tsx b/client/slices/address/pages/details/info/AddressCounterItem.tsx similarity index 93% rename from ui/address/details/AddressCounterItem.tsx rename to client/slices/address/pages/details/info/AddressCounterItem.tsx index 9e6a0d6fb0..33746b636c 100644 --- a/ui/address/details/AddressCounterItem.tsx +++ b/client/slices/address/pages/details/info/AddressCounterItem.tsx @@ -2,11 +2,12 @@ import type { UseQueryResult } from '@tanstack/react-query'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressCounters } from 'types/api/address'; +import type { AddressCounters } from 'client/slices/address/types/api'; import { route } from 'nextjs/routes'; -import type { ResourceError } from 'lib/api/resources'; +import type { ResourceError } from 'client/api/resources'; + import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/address/AddressDetails.pw.tsx b/client/slices/address/pages/details/info/AddressDetails.pw.tsx similarity index 96% rename from ui/address/AddressDetails.pw.tsx rename to client/slices/address/pages/details/info/AddressDetails.pw.tsx index b7110df1bc..e86c3dbecb 100644 --- a/ui/address/AddressDetails.pw.tsx +++ b/client/slices/address/pages/details/info/AddressDetails.pw.tsx @@ -1,5 +1,8 @@ import React from 'react'; +import type { AddressCountersQuery } from 'client/slices/address/hooks/useAddressCountersQuery'; +import type { AddressQuery } from 'client/slices/address/hooks/useAddressQuery'; + import * as addressMock from 'mocks/address/address'; import * as countersMock from 'mocks/address/counters'; import * as tokensMock from 'mocks/address/tokens'; @@ -8,10 +11,8 @@ import type { TestFnArgs } from 'playwright/lib'; import { test, expect, devices } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; +import MockAddressPage from '../AddressPageMock'; import AddressDetails from './AddressDetails'; -import MockAddressPage from './testUtils/MockAddressPage'; -import type { AddressCountersQuery } from './utils/useAddressCountersQuery'; -import type { AddressQuery } from './utils/useAddressQuery'; const WIDGETS_CONFIG_URL = 'http://localhost:4000/address-3rd-party-widgets-config.json'; const ADDRESS_HASH = addressMock.hash; diff --git a/ui/address/AddressDetails.tsx b/client/slices/address/pages/details/info/AddressDetails.tsx similarity index 85% rename from ui/address/AddressDetails.tsx rename to client/slices/address/pages/details/info/AddressDetails.tsx index 3858ff4709..755b09a008 100644 --- a/ui/address/AddressDetails.tsx +++ b/client/slices/address/pages/details/info/AddressDetails.tsx @@ -2,35 +2,38 @@ import { Box, Text } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import type { AddressCountersQuery } from 'client/slices/address/hooks/useAddressCountersQuery'; +import type { AddressQuery } from 'client/slices/address/hooks/useAddressQuery'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import Address3rdPartyWidgets from 'client/features/address-3rd-party-widgets/pages/address/Address3rdPartyWidgets'; +import useAddress3rdPartyWidgets from 'client/features/address-3rd-party-widgets/pages/address/useAddress3rdPartyWidgets'; +import AddressCeloAccount from 'client/features/chain-variants/celo/pages/address/AddressCeloAccount'; +import FilecoinActorTag from 'client/features/chain-variants/filecoin/pages/address/FilecoinActorTag'; + +import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import AddressCounterItem from 'ui/address/details/AddressCounterItem'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import ContractCreationStatus from 'ui/shared/statusTag/ContractCreationStatus'; -import Address3rdPartyWidgets from './Address3rdPartyWidgets'; -import useAddress3rdPartyWidgets from './address3rdPartyWidgets/useAddress3rdPartyWidgets'; -import AddressAlternativeFormat from './details/AddressAlternativeFormat'; -import AddressBalance from './details/AddressBalance'; -import AddressCeloAccount from './details/AddressCeloAccount'; -import AddressImplementations from './details/AddressImplementations'; -import AddressNameInfo from './details/AddressNameInfo'; -import AddressNetWorth from './details/AddressNetWorth'; -import FilecoinActorTag from './filecoin/FilecoinActorTag'; -import TokenSelect from './tokenSelect/TokenSelect'; -import type { AddressCountersQuery } from './utils/useAddressCountersQuery'; -import type { AddressQuery } from './utils/useAddressQuery'; +import AddressAlternativeFormat from './AddressAlternativeFormat'; +import AddressBalance from './AddressBalance'; +import AddressCounterItem from './AddressCounterItem'; +import AddressImplementations from './AddressImplementations'; +import AddressNameInfo from './AddressNameInfo'; +import AddressNetWorth from './AddressNetWorth'; +import TokenSelect from './token-select/TokenSelect'; interface Props { addressQuery: AddressQuery; @@ -269,10 +272,10 @@ const AddressDetails = ({ addressQuery, countersQuery, isLoading }: Props) => { { data.has_validated_blocks && ( <> - { `Blocks ${ getNetworkValidationActionText() }` } + { `Blocks ${ getChainValidationActionText() }` } { addressQuery.data ? ( diff --git a/ui/address/details/AddressImplementations.tsx b/client/slices/address/pages/details/info/AddressImplementations.tsx similarity index 90% rename from ui/address/details/AddressImplementations.tsx rename to client/slices/address/pages/details/info/AddressImplementations.tsx index f726e5557c..ca4af76771 100644 --- a/ui/address/details/AddressImplementations.tsx +++ b/client/slices/address/pages/details/info/AddressImplementations.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import type { AddressImplementation } from 'types/api/addressParams'; +import type { AddressImplementation } from 'client/slices/address/types/api'; import type { SmartContractProxyType } from 'types/api/contract'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; interface Props { data: Array; diff --git a/ui/address/details/AddressNameInfo.tsx b/client/slices/address/pages/details/info/AddressNameInfo.tsx similarity index 96% rename from ui/address/details/AddressNameInfo.tsx rename to client/slices/address/pages/details/info/AddressNameInfo.tsx index 8e08fe5e6a..d032db958f 100644 --- a/ui/address/details/AddressNameInfo.tsx +++ b/client/slices/address/pages/details/info/AddressNameInfo.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import { Skeleton } from 'toolkit/chakra/skeleton'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; diff --git a/ui/address/details/AddressNetWorth.pw.tsx b/client/slices/address/pages/details/info/AddressNetWorth.pw.tsx similarity index 100% rename from ui/address/details/AddressNetWorth.pw.tsx rename to client/slices/address/pages/details/info/AddressNetWorth.pw.tsx diff --git a/ui/address/details/AddressNetWorth.tsx b/client/slices/address/pages/details/info/AddressNetWorth.tsx similarity index 86% rename from ui/address/details/AddressNetWorth.tsx rename to client/slices/address/pages/details/info/AddressNetWorth.tsx index 973c91fc8f..e6bf5abef3 100644 --- a/ui/address/details/AddressNetWorth.tsx +++ b/client/slices/address/pages/details/info/AddressNetWorth.tsx @@ -1,20 +1,22 @@ import { Text, HStack } from '@chakra-ui/react'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; + +import useFetchTokens from 'client/slices/token/pages/address/useFetchTokens'; +import { getTokensTotalInfo } from 'client/slices/token/pages/address/utils'; + +import AddressMultichainButton from 'client/features/multichain-button/pages/address/AddressMultichainButton'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; import config from 'configs/app'; -import * as mixpanel from 'lib/mixpanel/index'; import { Skeleton } from 'toolkit/chakra/skeleton'; import TextSeparator from 'ui/shared/TextSeparator'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; import SimpleValue from 'ui/shared/value/SimpleValue'; import { DEFAULT_ACCURACY_USD } from 'ui/shared/value/utils'; -import { getTokensTotalInfo } from '../utils/tokenUtils'; -import useFetchTokens from '../utils/useFetchTokens'; -import AddressMultichainButton from './AddressMultichainButton'; - const multichainFeature = config.features.multichainButton; type Props = { diff --git a/ui/address/details/AddressQrCode.pw.tsx b/client/slices/address/pages/details/info/AddressQrCode.pw.tsx similarity index 100% rename from ui/address/details/AddressQrCode.pw.tsx rename to client/slices/address/pages/details/info/AddressQrCode.pw.tsx diff --git a/ui/address/details/AddressQrCode.tsx b/client/slices/address/pages/details/info/AddressQrCode.tsx similarity index 91% rename from ui/address/details/AddressQrCode.tsx rename to client/slices/address/pages/details/info/AddressQrCode.tsx index 5771112245..6601b3de6f 100644 --- a/ui/address/details/AddressQrCode.tsx +++ b/client/slices/address/pages/details/info/AddressQrCode.tsx @@ -3,16 +3,17 @@ import { useRouter } from 'next/router'; import QRCode from 'qrcode'; import React from 'react'; -import getPageType from 'lib/mixpanel/getPageType'; -import * as mixpanel from 'lib/mixpanel/index'; -import { useRollbar } from 'lib/rollbar'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import { useRollbar } from 'client/shared/monitoring/rollbar'; + import { Alert } from 'toolkit/chakra/alert'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import IconSvg from 'ui/shared/IconSvg'; const SVG_OPTIONS = { @@ -34,7 +35,7 @@ const AddressQrCode = ({ hash, className, isLoading }: Props) => { const [ qr, setQr ] = React.useState(''); const [ error, setError ] = React.useState(''); - const pageType = getPageType(router.pathname); + const pageType = mixpanel.getPageType(router.pathname); React.useEffect(() => { if (open) { diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_contract-1.png diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_filecoin-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_filecoin-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_filecoin-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_filecoin-1.png diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-contract-1.png diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-filecoin-1.png diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-validator-1.png diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-with-widgets-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-with-widgets-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_mobile-with-widgets-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_mobile-with-widgets-1.png diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_validator-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_validator-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_validator-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_validator-1.png diff --git a/ui/address/__screenshots__/AddressDetails.pw.tsx_default_with-widgets-1.png b/client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_with-widgets-1.png similarity index 100% rename from ui/address/__screenshots__/AddressDetails.pw.tsx_default_with-widgets-1.png rename to client/slices/address/pages/details/info/__screenshots__/AddressDetails.pw.tsx_default_with-widgets-1.png diff --git a/ui/address/tokenSelect/TokenSelect.pw.tsx b/client/slices/address/pages/details/info/token-select/TokenSelect.pw.tsx similarity index 98% rename from ui/address/tokenSelect/TokenSelect.pw.tsx rename to client/slices/address/pages/details/info/token-select/TokenSelect.pw.tsx index feeba1fbe5..d4b9c8f991 100644 --- a/ui/address/tokenSelect/TokenSelect.pw.tsx +++ b/client/slices/address/pages/details/info/token-select/TokenSelect.pw.tsx @@ -1,11 +1,12 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; +import MockAddressPage from 'client/slices/address/pages/details/AddressPageMock'; + import * as addressMock from 'mocks/address/address'; import * as tokensMock from 'mocks/address/tokens'; import { tokenInfoERC20c, tokenInfoERC20a } from 'mocks/tokens/tokenInfo'; import { test, expect, devices } from 'playwright/lib'; -import MockAddressPage from 'ui/address/testUtils/MockAddressPage'; import TokenSelect from './TokenSelect'; diff --git a/ui/address/tokenSelect/TokenSelect.tsx b/client/slices/address/pages/details/info/token-select/TokenSelect.tsx similarity index 87% rename from ui/address/tokenSelect/TokenSelect.tsx rename to client/slices/address/pages/details/info/token-select/TokenSelect.tsx index a0e318b885..75ea6b968d 100644 --- a/ui/address/tokenSelect/TokenSelect.tsx +++ b/client/slices/address/pages/details/info/token-select/TokenSelect.tsx @@ -4,22 +4,25 @@ import { sumBy } from 'es-toolkit'; import { useRouter } from 'next/router'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import { route } from 'nextjs/routes'; -import { getResourceKey } from 'lib/api/useApiQuery'; +import { getResourceKey } from 'client/api/hooks/useApiQuery'; + +import useFetchTokens from 'client/slices/token/pages/address/useFetchTokens'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import * as mixpanel from 'lib/mixpanel/index'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import IconSvg from 'ui/shared/IconSvg'; -import useFetchTokens from '../utils/useFetchTokens'; import TokenSelectDesktop from './TokenSelectDesktop'; import TokenSelectMobile from './TokenSelectMobile'; diff --git a/ui/address/tokenSelect/TokenSelectButton.tsx b/client/slices/address/pages/details/info/token-select/TokenSelectButton.tsx similarity index 92% rename from ui/address/tokenSelect/TokenSelectButton.tsx rename to client/slices/address/pages/details/info/token-select/TokenSelectButton.tsx index 7effe26b92..1c2687207d 100644 --- a/ui/address/tokenSelect/TokenSelectButton.tsx +++ b/client/slices/address/pages/details/info/token-select/TokenSelectButton.tsx @@ -3,13 +3,14 @@ import React from 'react'; import type { FormattedData } from './types'; -import * as mixpanel from 'lib/mixpanel/index'; +import { getTokensTotalInfo } from 'client/slices/token/pages/address/utils'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import { Button } from 'toolkit/chakra/button'; import { space, thinsp } from 'toolkit/utils/htmlEntities'; import IconSvg from 'ui/shared/IconSvg'; -import { getTokensTotalInfo } from '../utils/tokenUtils'; - interface Props { isOpen: boolean; isLoading?: boolean; diff --git a/ui/address/tokenSelect/TokenSelectDesktop.tsx b/client/slices/address/pages/details/info/token-select/TokenSelectDesktop.tsx similarity index 100% rename from ui/address/tokenSelect/TokenSelectDesktop.tsx rename to client/slices/address/pages/details/info/token-select/TokenSelectDesktop.tsx diff --git a/ui/address/tokenSelect/TokenSelectItem.tsx b/client/slices/address/pages/details/info/token-select/TokenSelectItem.tsx similarity index 98% rename from ui/address/tokenSelect/TokenSelectItem.tsx rename to client/slices/address/pages/details/info/token-select/TokenSelectItem.tsx index 15cb93f202..f364e093a9 100644 --- a/ui/address/tokenSelect/TokenSelectItem.tsx +++ b/client/slices/address/pages/details/info/token-select/TokenSelectItem.tsx @@ -4,6 +4,8 @@ import React from 'react'; import { route } from 'nextjs/routes'; +import type { TokenEnhancedData } from 'client/slices/token/pages/address/utils'; + import config from 'configs/app'; import multichainConfig from 'configs/multichain'; import { isConfidentialTokenType, isFungibleTokenType } from 'lib/token/tokenTypes'; @@ -13,8 +15,6 @@ import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; -import type { TokenEnhancedData } from '../utils/tokenUtils'; - interface Props { data: TokenEnhancedData; } diff --git a/ui/address/tokenSelect/TokenSelectMenu.tsx b/client/slices/address/pages/details/info/token-select/TokenSelectMenu.tsx similarity index 94% rename from ui/address/tokenSelect/TokenSelectMenu.tsx rename to client/slices/address/pages/details/info/token-select/TokenSelectMenu.tsx index 87092467ed..d6e06d9ae2 100644 --- a/ui/address/tokenSelect/TokenSelectMenu.tsx +++ b/client/slices/address/pages/details/info/token-select/TokenSelectMenu.tsx @@ -4,14 +4,15 @@ import React from 'react'; import type { FormattedData } from './types'; +import type { Sort } from 'client/slices/token/pages/address/utils'; +import { getSortingFn, sortTokenGroups } from 'client/slices/token/pages/address/utils'; + import { getTokenTypeName } from 'lib/token/tokenTypes'; import { Link } from 'toolkit/chakra/link'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import { thinsp } from 'toolkit/utils/htmlEntities'; import IconSvg from 'ui/shared/IconSvg'; -import type { Sort } from '../utils/tokenUtils'; -import { getSortingFn, sortTokenGroups } from '../utils/tokenUtils'; import TokenSelectItem from './TokenSelectItem'; interface Props { diff --git a/ui/address/tokenSelect/TokenSelectMobile.tsx b/client/slices/address/pages/details/info/token-select/TokenSelectMobile.tsx similarity index 100% rename from ui/address/tokenSelect/TokenSelectMobile.tsx rename to client/slices/address/pages/details/info/token-select/TokenSelectMobile.tsx diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png new file mode 100644 index 0000000000..ba1c4855d4 Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-2.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-2.png new file mode 100644 index 0000000000..a5f0b6d84f Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-2.png differ diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-1.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-1.png new file mode 100644 index 0000000000..494dba3ea5 Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-2.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-2.png new file mode 100644 index 0000000000..32655955d6 Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-2.png differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_filter-1.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_filter-1.png similarity index 100% rename from ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_filter-1.png rename to client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_filter-1.png diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_long-values-1.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_long-values-1.png new file mode 100644 index 0000000000..53b8417b7c Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_long-values-1.png differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_mobile-base-view-1.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_mobile-base-view-1.png rename to client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_mobile-base-view-1.png diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_native-token-1.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_native-token-1.png new file mode 100644 index 0000000000..e70b4c79ac Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_native-token-1.png differ diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_sort-1.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_sort-1.png new file mode 100644 index 0000000000..2cb69b1927 Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_sort-1.png differ diff --git a/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_sort-2.png b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_sort-2.png new file mode 100644 index 0000000000..43e65854b3 Binary files /dev/null and b/client/slices/address/pages/details/info/token-select/__screenshots__/TokenSelect.pw.tsx_default_sort-2.png differ diff --git a/ui/address/tokenSelect/types.ts b/client/slices/address/pages/details/info/token-select/types.ts similarity index 66% rename from ui/address/tokenSelect/types.ts rename to client/slices/address/pages/details/info/token-select/types.ts index 0508bcc160..941fabc71d 100644 --- a/ui/address/tokenSelect/types.ts +++ b/client/slices/address/pages/details/info/token-select/types.ts @@ -1,4 +1,4 @@ -import type { TokenEnhancedData } from 'ui/address/utils/tokenUtils'; +import type { TokenEnhancedData } from 'client/slices/token/pages/address/utils'; export type FormattedData = Record; diff --git a/ui/address/tokenSelect/useTokenSelect.ts b/client/slices/address/pages/details/info/token-select/useTokenSelect.ts similarity index 93% rename from ui/address/tokenSelect/useTokenSelect.ts rename to client/slices/address/pages/details/info/token-select/useTokenSelect.ts index 1d8f0846be..16b742074c 100644 --- a/ui/address/tokenSelect/useTokenSelect.ts +++ b/client/slices/address/pages/details/info/token-select/useTokenSelect.ts @@ -3,8 +3,8 @@ import React from 'react'; import type { FormattedData } from './types'; -import type { Sort } from '../utils/tokenUtils'; -import { filterTokens } from '../utils/tokenUtils'; +import type { Sort } from 'client/slices/token/pages/address/utils'; +import { filterTokens } from 'client/slices/token/pages/address/utils'; export default function useTokenSelect(data: FormattedData) { const [ searchTerm, setSearchTerm ] = React.useState(''); diff --git a/ui/address/AddressInternalTxs.pw.tsx b/client/slices/address/pages/details/internal-txs/AddressInternalTxs.pw.tsx similarity index 100% rename from ui/address/AddressInternalTxs.pw.tsx rename to client/slices/address/pages/details/internal-txs/AddressInternalTxs.pw.tsx diff --git a/ui/address/AddressInternalTxs.tsx b/client/slices/address/pages/details/internal-txs/AddressInternalTxs.tsx similarity index 76% rename from ui/address/AddressInternalTxs.tsx rename to client/slices/address/pages/details/internal-txs/AddressInternalTxs.tsx index f9f4de8b64..75c637d024 100644 --- a/ui/address/AddressInternalTxs.tsx +++ b/client/slices/address/pages/details/internal-txs/AddressInternalTxs.tsx @@ -1,15 +1,17 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import useIsMounted from 'lib/hooks/useIsMounted'; +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMounted from 'client/shared/hooks/useIsMounted'; + import InternalTxsList from 'ui/internalTxs/InternalTxsList'; import InternalTxsTable from 'ui/internalTxs/InternalTxsTable'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; -import AddressCsvExportLink from './AddressCsvExportLink'; -import AddressTxsFilter from './AddressTxsFilter'; +import AddressTxsFilter from '../txs/AddressTxsFilter'; import useAddressInternalTxsQuery from './useAddressInternalTxsQuery'; type Props = { @@ -45,13 +47,18 @@ const AddressInternalTxs = ({ shouldRender = true, isQueryEnabled = true }: Prop hasActiveFilter={ Boolean(filterValue) } isLoading={ pagination.isLoading } /> - - + ); diff --git a/client/slices/address/pages/details/internal-txs/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png b/client/slices/address/pages/details/internal-txs/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png new file mode 100644 index 0000000000..3493ea7796 Binary files /dev/null and b/client/slices/address/pages/details/internal-txs/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png differ diff --git a/client/slices/address/pages/details/internal-txs/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png b/client/slices/address/pages/details/internal-txs/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png new file mode 100644 index 0000000000..d86b0d5b7d Binary files /dev/null and b/client/slices/address/pages/details/internal-txs/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/address/useAddressInternalTxsQuery.ts b/client/slices/address/pages/details/internal-txs/useAddressInternalTxsQuery.ts similarity index 89% rename from ui/address/useAddressInternalTxsQuery.ts rename to client/slices/address/pages/details/internal-txs/useAddressInternalTxsQuery.ts index df1fdc5f12..aabe20fc83 100644 --- a/ui/address/useAddressInternalTxsQuery.ts +++ b/client/slices/address/pages/details/internal-txs/useAddressInternalTxsQuery.ts @@ -1,10 +1,11 @@ import { useRouter } from 'next/router'; import React from 'react'; -import { AddressFromToFilterValues, type AddressFromToFilter } from 'types/api/address'; +import { AddressFromToFilterValues, type AddressFromToFilter } from 'client/slices/address/types/api'; + +import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; -import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { INTERNAL_TX } from 'stubs/internalTx'; import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/ui/address/AddressLogs.tsx b/client/slices/address/pages/details/logs/AddressLogs.tsx similarity index 77% rename from ui/address/AddressLogs.tsx rename to client/slices/address/pages/details/logs/AddressLogs.tsx index 968912613d..428c51fe09 100644 --- a/ui/address/AddressLogs.tsx +++ b/client/slices/address/pages/details/logs/AddressLogs.tsx @@ -1,9 +1,14 @@ import { useRouter } from 'next/router'; import React from 'react'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { LOG } from 'stubs/log'; +import useAddressQuery from 'client/slices/address/hooks/useAddressQuery'; +import { LOG } from 'client/slices/log/stubs/log'; + +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { generateListStub } from 'stubs/utils'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; @@ -11,9 +16,6 @@ import LogItem from 'ui/shared/logs/LogItem'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import AddressCsvExportLink from './AddressCsvExportLink'; -import useAddressQuery from './utils/useAddressQuery'; - type Props = { shouldRender?: boolean; isQueryEnabled?: boolean; @@ -42,12 +44,13 @@ const AddressLogs = ({ shouldRender = true, isQueryEnabled = true }: Props) => { const actionBar = ( - - + ); diff --git a/ui/address/AddressTokenTransfers.pw.tsx b/client/slices/address/pages/details/token-transfers/AddressTokenTransfers.pw.tsx similarity index 100% rename from ui/address/AddressTokenTransfers.pw.tsx rename to client/slices/address/pages/details/token-transfers/AddressTokenTransfers.pw.tsx diff --git a/ui/address/AddressTokenTransfers.tsx b/client/slices/address/pages/details/token-transfers/AddressTokenTransfers.tsx similarity index 84% rename from ui/address/AddressTokenTransfers.tsx rename to client/slices/address/pages/details/token-transfers/AddressTokenTransfers.tsx index eeb54a78a6..15ca7b9d7e 100644 --- a/ui/address/AddressTokenTransfers.tsx +++ b/client/slices/address/pages/details/token-transfers/AddressTokenTransfers.tsx @@ -2,10 +2,13 @@ import { HStack } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { INTERCHAIN_TRANSFER } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; @@ -15,8 +18,7 @@ import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; -import AddressAdvancedFilterLink from './AddressAdvancedFilterLink'; -import AddressCsvExportLink from './AddressCsvExportLink'; +import AddressAdvancedFilterLink from '../txs/AddressAdvancedFilterLink'; import AddressTokenTransfersLocal from './AddressTokenTransfersLocal'; import useAddressTokenTransfersQuery from './useAddressTokenTransfersQuery'; @@ -114,9 +116,13 @@ const AddressTokenTransfers = ({ shouldRender = true, overloadCount, isQueryEnab const numActiveFilters = (localQuery.filters.type?.length || 0) + (localQuery.filters.filter ? 1 : 0); + if (!localQuery.query.data?.items.length && !numActiveFilters) { + return null; + } + return ( <> - + - - + - - + ); } diff --git a/ui/address/AddressTokenTransfersLocal.tsx b/client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal.tsx similarity index 87% rename from ui/address/AddressTokenTransfersLocal.tsx rename to client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal.tsx index dec9df2414..037e349ee1 100644 --- a/ui/address/AddressTokenTransfersLocal.tsx +++ b/client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal.tsx @@ -3,8 +3,11 @@ import React from 'react'; import type { TokenType } from 'types/api/token'; +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; @@ -14,8 +17,7 @@ import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList'; import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable'; -import AddressAdvancedFilterLink from './AddressAdvancedFilterLink'; -import AddressCsvExportLink from './AddressCsvExportLink'; +import AddressAdvancedFilterLink from '../txs/AddressAdvancedFilterLink'; import type { Filters } from './useAddressTokenTransfersQuery'; import useAddressTokenTransfersSocket from './useAddressTokenTransfersSocket'; @@ -92,17 +94,22 @@ const TokenTransfersLocal = ({ query, filters, addressHash, onTypeFilterChange, isLoading={ query.isPlaceholderData } chainConfig={ multichainContext?.chain?.app_config } /> + - diff --git a/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-pagination-1.png b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-pagination-1.png new file mode 100644 index 0000000000..7df8fa01bf Binary files /dev/null and b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-pagination-1.png differ diff --git a/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-without-pagination-1.png b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-without-pagination-1.png new file mode 100644 index 0000000000..0124da4a91 Binary files /dev/null and b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-without-pagination-1.png differ diff --git a/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-pagination-1.png b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-pagination-1.png new file mode 100644 index 0000000000..c025afbe39 Binary files /dev/null and b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-pagination-1.png differ diff --git a/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_without-pagination-1.png b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_without-pagination-1.png new file mode 100644 index 0000000000..255381f708 Binary files /dev/null and b/client/slices/address/pages/details/token-transfers/__screenshots__/AddressTokenTransfers.pw.tsx_default_without-pagination-1.png differ diff --git a/ui/address/useAddressTokenTransfersQuery.ts b/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersQuery.ts similarity index 87% rename from ui/address/useAddressTokenTransfersQuery.ts rename to client/slices/address/pages/details/token-transfers/useAddressTokenTransfersQuery.ts index 3e87b14e34..5fa173abc1 100644 --- a/ui/address/useAddressTokenTransfersQuery.ts +++ b/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersQuery.ts @@ -1,14 +1,15 @@ import { useRouter } from 'next/router'; import React from 'react'; -import type { AddressFromToFilter } from 'types/api/address'; -import { AddressFromToFilterValues } from 'types/api/address'; +import type { AddressFromToFilter } from 'client/slices/address/types/api'; +import { AddressFromToFilterValues } from 'client/slices/address/types/api'; import type { TokenType } from 'types/api/token'; +import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; +import getFilterValuesFromQuery from 'client/shared/router/get-filter-values-from-query'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; -import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { getTokenTypes } from 'lib/token/tokenTypes'; import { getTokenTransfersStub } from 'stubs/token'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/ui/address/useAddressTokenTransfersSocket.ts b/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersSocket.ts similarity index 90% rename from ui/address/useAddressTokenTransfersSocket.ts rename to client/slices/address/pages/details/token-transfers/useAddressTokenTransfersSocket.ts index 4e4213b367..d6a11f9e8c 100644 --- a/ui/address/useAddressTokenTransfersSocket.ts +++ b/client/slices/address/pages/details/token-transfers/useAddressTokenTransfersSocket.ts @@ -1,17 +1,19 @@ import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { AddressTokenTransferResponse } from 'types/api/address'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { AddressTokenTransferResponse } from 'client/slices/address/types/api'; import type { TokenTransfer } from 'types/api/tokenTransfer'; +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import { getResourceKey } from 'lib/api/useApiQuery'; import { useAppContext } from 'lib/contexts/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import * as cookies from 'lib/cookies'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; import type { Filters } from './useAddressTokenTransfersQuery'; diff --git a/ui/address/AddressTokens.pw.tsx b/client/slices/address/pages/details/tokens/AddressTokens.pw.tsx similarity index 99% rename from ui/address/AddressTokens.pw.tsx rename to client/slices/address/pages/details/tokens/AddressTokens.pw.tsx index fe4d882e8a..d960e3dbd9 100644 --- a/ui/address/AddressTokens.pw.tsx +++ b/client/slices/address/pages/details/tokens/AddressTokens.pw.tsx @@ -1,7 +1,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { AddressTokenBalance, AddressTokensResponse } from 'types/api/address'; +import type { AddressTokenBalance, AddressTokensResponse } from 'client/slices/address/types/api'; import * as addressMock from 'mocks/address/address'; import * as tokensMock from 'mocks/address/tokens'; diff --git a/ui/address/AddressTokens.tsx b/client/slices/address/pages/details/tokens/AddressTokens.tsx similarity index 84% rename from ui/address/AddressTokens.tsx rename to client/slices/address/pages/details/tokens/AddressTokens.tsx index d44c1056fe..637bb35d01 100644 --- a/ui/address/AddressTokens.tsx +++ b/client/slices/address/pages/details/tokens/AddressTokens.tsx @@ -4,24 +4,25 @@ import React from 'react'; import type { PaginationParams } from 'ui/shared/pagination/types'; +import { ADDRESS_TOKEN_BALANCE_ERC_20 } from 'client/slices/address/stubs/address'; +import AddressCollections from 'client/slices/token/pages/address/AddressCollections'; +import AddressNftDisplayTypeRadio from 'client/slices/token/pages/address/AddressNftDisplayTypeRadio'; +import AddressNFTs from 'client/slices/token/pages/address/AddressNFTs'; +import AddressNftTypeFilter from 'client/slices/token/pages/address/AddressNftTypeFilter'; +import ERC20Tokens from 'client/slices/token/pages/address/ERC20Tokens'; +import TokenBalances from 'client/slices/token/pages/address/TokenBalances'; +import useAddressNftQuery from 'client/slices/token/pages/address/useAddressNftQuery'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { ADDRESS_TOKEN_BALANCE_ERC_20 } from 'stubs/address'; import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import AddressCollections from './tokens/AddressCollections'; -import AddressNftDisplayTypeRadio from './tokens/AddressNftDisplayTypeRadio'; -import AddressNFTs from './tokens/AddressNFTs'; -import AddressNftTypeFilter from './tokens/AddressNftTypeFilter'; -import ERC20Tokens from './tokens/ERC20Tokens'; -import TokenBalances from './tokens/TokenBalances'; -import useAddressNftQuery from './tokens/useAddressNftQuery'; - const TAB_LIST_PROPS = { mt: 1, mb: { base: 6, lg: 1 }, diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_collections-dark-mode-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_erc20-dark-mode-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_dark-color-mode_nfts-dark-mode-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_collections-dark-mode-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_erc20-dark-mode-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_mobile-collections-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_mobile-erc20-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_mobile-nfts-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_native-token-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_native-token-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_native-token-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_native-token-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_nfts-dark-mode-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-base-flow-2.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-1.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-1.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-1.png diff --git a/ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-2.png b/client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-2.png similarity index 100% rename from ui/address/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-2.png rename to client/slices/address/pages/details/tokens/__screenshots__/AddressTokens.pw.tsx_default_update-balances-via-socket-custom-token-ZRC-2-2.png diff --git a/ui/address/AddressAdvancedFilterLink.tsx b/client/slices/address/pages/details/txs/AddressAdvancedFilterLink.tsx similarity index 92% rename from ui/address/AddressAdvancedFilterLink.tsx rename to client/slices/address/pages/details/txs/AddressAdvancedFilterLink.tsx index 5e58b2be40..2bcfc1fba7 100644 --- a/ui/address/AddressAdvancedFilterLink.tsx +++ b/client/slices/address/pages/details/txs/AddressAdvancedFilterLink.tsx @@ -1,13 +1,14 @@ import { pickBy } from 'es-toolkit'; import React from 'react'; -import type { AddressFromToFilter } from 'types/api/address'; +import type { AddressFromToFilter } from 'client/slices/address/types/api'; import type { TokenType } from 'types/api/token'; import type { ClusterChainConfig } from 'types/multichain'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; + import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import { getAdvancedFilterTypes } from 'ui/advancedFilter/constants'; import AdvancedFilterLink from 'ui/shared/links/AdvancedFilterLink'; diff --git a/ui/address/AddressTxs.pw.tsx b/client/slices/address/pages/details/txs/AddressTxs.pw.tsx similarity index 99% rename from ui/address/AddressTxs.pw.tsx rename to client/slices/address/pages/details/txs/AddressTxs.pw.tsx index c6c595d769..df26ce01ff 100644 --- a/ui/address/AddressTxs.pw.tsx +++ b/client/slices/address/pages/details/txs/AddressTxs.pw.tsx @@ -2,7 +2,8 @@ import { Box } from '@chakra-ui/react'; import type { Locator } from '@playwright/test'; import React from 'react'; -import * as txMock from 'mocks/txs/tx'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import * as socketServer from 'playwright/fixtures/socketServer'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/address/AddressTxs.tsx b/client/slices/address/pages/details/txs/AddressTxs.tsx similarity index 82% rename from ui/address/AddressTxs.tsx rename to client/slices/address/pages/details/txs/AddressTxs.tsx index 09e2e0f13e..f18b08e44b 100644 --- a/ui/address/AddressTxs.tsx +++ b/client/slices/address/pages/details/txs/AddressTxs.tsx @@ -2,10 +2,15 @@ import { HStack } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; +import TxsWithApiSorting from 'client/slices/tx/pages/index/list/TxsWithApiSorting'; + +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; @@ -13,9 +18,7 @@ import AddressTxsCrossChain from 'ui/crossChain/address/AddressTxsCrossChain'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import TxsWithAPISorting from 'ui/txs/TxsWithAPISorting'; -import AddressCsvExportLink from './AddressCsvExportLink'; import AddressTxsFilter from './AddressTxsFilter'; import useAddressTxsQuery from './useAddressTxsQuery'; @@ -79,7 +82,7 @@ const AddressTxs = ({ shouldRender = true, isQueryEnabled = true }: Props) => { id: [ 'txs_local', 'txs' ], title: 'Txns', component: ( - { return null; } + if (!localQuery.query.data?.items.length && !localQuery.filterValue) { + return null; + } + return ( <> - + { txsLocalFilter } + - diff --git a/ui/address/AddressTxsFilter.tsx b/client/slices/address/pages/details/txs/AddressTxsFilter.tsx similarity index 87% rename from ui/address/AddressTxsFilter.tsx rename to client/slices/address/pages/details/txs/AddressTxsFilter.tsx index 0d452770cf..6062172fe6 100644 --- a/ui/address/AddressTxsFilter.tsx +++ b/client/slices/address/pages/details/txs/AddressTxsFilter.tsx @@ -1,9 +1,10 @@ import { createListCollection } from '@chakra-ui/react'; import React from 'react'; -import type { AddressFromToFilter } from 'types/api/address'; +import type { AddressFromToFilter } from 'client/slices/address/types/api'; + +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio'; const OPTIONS = [ diff --git a/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png new file mode 100644 index 0000000000..37bc8d5f06 Binary files /dev/null and b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png differ diff --git a/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png new file mode 100644 index 0000000000..25d3e31381 Binary files /dev/null and b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png rename to client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-mobile-1.png diff --git a/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png new file mode 100644 index 0000000000..8e0d9e7550 Binary files /dev/null and b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png differ diff --git a/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_mobile-base-view-1.png b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_mobile-base-view-1.png new file mode 100644 index 0000000000..dd93fa2ad8 Binary files /dev/null and b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_mobile-base-view-1.png differ diff --git a/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_mobile-table-view-1.png b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_mobile-table-view-1.png new file mode 100644 index 0000000000..d69687edb6 Binary files /dev/null and b/client/slices/address/pages/details/txs/__screenshots__/AddressTxs.pw.tsx_default_mobile-table-view-1.png differ diff --git a/ui/address/useAddressTxsQuery.ts b/client/slices/address/pages/details/txs/useAddressTxsQuery.ts similarity index 83% rename from ui/address/useAddressTxsQuery.ts rename to client/slices/address/pages/details/txs/useAddressTxsQuery.ts index f9fb19e3c5..7756efe68e 100644 --- a/ui/address/useAddressTxsQuery.ts +++ b/client/slices/address/pages/details/txs/useAddressTxsQuery.ts @@ -1,17 +1,19 @@ import { useRouter } from 'next/router'; import React from 'react'; -import type { AddressFromToFilter } from 'types/api/address'; -import { AddressFromToFilterValues } from 'types/api/address'; -import type { TransactionsSorting, TransactionsSortingField, TransactionsSortingValue } from 'types/api/transaction'; +import type { AddressFromToFilter } from 'client/slices/address/types/api'; +import { AddressFromToFilterValues } from 'client/slices/address/types/api'; +import type { TransactionsSorting, TransactionsSortingField, TransactionsSortingValue } from 'client/slices/tx/types/api'; + +import { SORT_OPTIONS } from 'client/slices/tx/hooks/useTxsSort'; +import { TX } from 'client/slices/tx/stubs/tx'; + +import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; -import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; -import { TX } from 'stubs/tx'; import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; -import { SORT_OPTIONS } from 'ui/txs/useTxsSort'; const getFilterValue = (getFilterValueFromQuery).bind(null, AddressFromToFilterValues); diff --git a/ui/address/AddressWithdrawals.tsx b/client/slices/address/pages/details/withdrawals/AddressWithdrawals.tsx similarity index 94% rename from ui/address/AddressWithdrawals.tsx rename to client/slices/address/pages/details/withdrawals/AddressWithdrawals.tsx index f8bb6e0524..14d7fd70e1 100644 --- a/ui/address/AddressWithdrawals.tsx +++ b/client/slices/address/pages/details/withdrawals/AddressWithdrawals.tsx @@ -2,8 +2,9 @@ import { Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import useIsMounted from 'lib/hooks/useIsMounted'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { generateListStub } from 'stubs/utils'; import { WITHDRAWAL } from 'stubs/withdrawals'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; diff --git a/ui/pages/Accounts.pw.tsx b/client/slices/address/pages/index/Accounts.pw.tsx similarity index 93% rename from ui/pages/Accounts.pw.tsx rename to client/slices/address/pages/index/Accounts.pw.tsx index 45daa24103..f25fba424e 100644 --- a/ui/pages/Accounts.pw.tsx +++ b/client/slices/address/pages/index/Accounts.pw.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { AddressesResponse } from 'types/api/addresses'; +import type { AddressesResponse } from 'client/slices/address/types/api'; import * as addressMocks from 'mocks/address/address'; import { test, expect } from 'playwright/lib'; diff --git a/ui/pages/Accounts.tsx b/client/slices/address/pages/index/Accounts.tsx similarity index 91% rename from ui/pages/Accounts.tsx rename to client/slices/address/pages/index/Accounts.tsx index 04013b5e95..4153cc64d7 100644 --- a/ui/pages/Accounts.tsx +++ b/client/slices/address/pages/index/Accounts.tsx @@ -2,17 +2,20 @@ import { Box } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import getItemIndex from 'lib/getItemIndex'; -import { TOP_ADDRESS } from 'stubs/address'; +import { TOP_ADDRESS } from 'client/slices/address/stubs/address'; + +import getItemIndex from 'client/shared/lists/get-item-index'; + import { generateListStub } from 'stubs/utils'; -import AddressesListItem from 'ui/addresses/AddressesListItem'; -import AddressesTable from 'ui/addresses/AddressesTable'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PageTitle from 'ui/shared/Page/PageTitle'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; +import AddressesListItem from './AddressesListItem'; +import AddressesTable from './AddressesTable'; + const Accounts = () => { const { isError, isPlaceholderData, data, pagination } = useQueryWithPages({ resourceName: 'general:addresses', diff --git a/ui/addresses/AddressesListItem.tsx b/client/slices/address/pages/index/AddressesListItem.tsx similarity index 92% rename from ui/addresses/AddressesListItem.tsx rename to client/slices/address/pages/index/AddressesListItem.tsx index feb538893c..82af78a2b5 100644 --- a/ui/addresses/AddressesListItem.tsx +++ b/client/slices/address/pages/index/AddressesListItem.tsx @@ -2,14 +2,16 @@ import { Flex, HStack } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressesItem } from 'types/api/addresses'; +import type { AddressesItem } from 'client/slices/address/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import { currencyUnits } from 'client/shared/chain/units'; import config from 'configs/app'; -import { currencyUnits } from 'lib/units'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; import { ZERO } from 'toolkit/utils/consts'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; type Props = { diff --git a/ui/addresses/AddressesTable.tsx b/client/slices/address/pages/index/AddressesTable.tsx similarity index 92% rename from ui/addresses/AddressesTable.tsx rename to client/slices/address/pages/index/AddressesTable.tsx index 020d778e31..522b957ec0 100644 --- a/ui/addresses/AddressesTable.tsx +++ b/client/slices/address/pages/index/AddressesTable.tsx @@ -1,9 +1,10 @@ import type BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressesItem } from 'types/api/addresses'; +import type { AddressesItem } from 'client/slices/address/types/api'; + +import { currencyUnits } from 'client/shared/chain/units'; -import { currencyUnits } from 'lib/units'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { ZERO } from 'toolkit/utils/consts'; diff --git a/ui/addresses/AddressesTableItem.tsx b/client/slices/address/pages/index/AddressesTableItem.tsx similarity index 93% rename from ui/addresses/AddressesTableItem.tsx rename to client/slices/address/pages/index/AddressesTableItem.tsx index 07d65e826e..7a3649aba7 100644 --- a/ui/addresses/AddressesTableItem.tsx +++ b/client/slices/address/pages/index/AddressesTableItem.tsx @@ -2,13 +2,14 @@ import { Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { AddressesItem } from 'types/api/addresses'; +import type { AddressesItem } from 'client/slices/address/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tag } from 'toolkit/chakra/tag'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import SimpleValue from 'ui/shared/value/SimpleValue'; type Props = { diff --git a/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png new file mode 100644 index 0000000000..e6d4d0a11d Binary files /dev/null and b/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png differ diff --git a/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png b/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png new file mode 100644 index 0000000000..e8b45eb2cb Binary files /dev/null and b/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_default_base-view-mobile-dark-mode-1.png differ diff --git a/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png new file mode 100644 index 0000000000..b1c12ee274 Binary files /dev/null and b/client/slices/address/pages/index/__screenshots__/Accounts.pw.tsx_mobile_base-view-mobile-dark-mode-1.png differ diff --git a/stubs/addressParams.ts b/client/slices/address/stubs/address-params.ts similarity index 82% rename from stubs/addressParams.ts rename to client/slices/address/stubs/address-params.ts index 8548abca9c..d819dc83cf 100644 --- a/stubs/addressParams.ts +++ b/client/slices/address/stubs/address-params.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from 'types/api/addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export const ADDRESS_HASH = '0x2B51Ae4412F79c3c1cB12AA40Ea4ECEb4e80511a'; diff --git a/stubs/address.ts b/client/slices/address/stubs/address.ts similarity index 79% rename from stubs/address.ts rename to client/slices/address/stubs/address.ts index ec6f4c1a4f..43e1193f47 100644 --- a/stubs/address.ts +++ b/client/slices/address/stubs/address.ts @@ -3,18 +3,17 @@ import type { AddressCoinBalanceHistoryItem, AddressCollection, AddressCounters, - AddressEpochRewardsItem, - AddressMudTableItem, AddressNFT, AddressTabsCounters, AddressTokenBalance, -} from 'types/api/address'; -import type { AddressesItem } from 'types/api/addresses'; + AddressesItem, +} from 'client/slices/address/types/api'; -import { ADDRESS_HASH, ADDRESS_PARAMS } from './addressParams'; -import { MUD_SCHEMA, MUD_TABLE } from './mud'; -import { TOKEN_INFO_ERC_1155, TOKEN_INFO_ERC_20, TOKEN_INFO_ERC_721, TOKEN_INFO_ERC_404, TOKEN_INSTANCE } from './token'; -import { TX_HASH } from './tx'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; + +import { TOKEN_INFO_ERC_1155, TOKEN_INFO_ERC_20, TOKEN_INFO_ERC_721, TOKEN_INFO_ERC_404, TOKEN_INSTANCE } from 'stubs/token'; + +import { ADDRESS_HASH } from './address-params'; export const ADDRESS_INFO: Address = { block_number_balance_updated_at: 8774377, @@ -113,18 +112,3 @@ export const ADDRESS_COLLECTION: AddressCollection = { amount: '4', token_instances: Array(4).fill(TOKEN_INSTANCE), }; - -export const ADDRESS_MUD_TABLE_ITEM: AddressMudTableItem = { - schema: MUD_SCHEMA, - table: MUD_TABLE, -}; - -export const EPOCH_REWARD_ITEM: AddressEpochRewardsItem = { - amount: '136609473658452408568', - block_timestamp: '2022-05-15T13:16:24Z', - type: 'voter', - token: TOKEN_INFO_ERC_20, - account: ADDRESS_PARAMS, - epoch_number: 1234, - associated_account: ADDRESS_PARAMS, -}; diff --git a/types/api/address.ts b/client/slices/address/types/api.ts similarity index 71% rename from types/api/address.ts rename to client/slices/address/types/api.ts index 2ba23a29f3..44c42fdad6 100644 --- a/types/api/address.ts +++ b/client/slices/address/types/api.ts @@ -1,13 +1,66 @@ -import type { Transaction } from 'types/api/transaction'; +import type { AddressMetadataTagApi } from 'client/features/address-metadata/types/api'; +import type { AddressFilecoinParams } from 'client/features/chain-variants/filecoin/types/api'; +import type { AddressZilliqaParams } from 'client/features/chain-variants/zilliqa/types/api'; +import type { Block } from 'client/slices/block/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; +import type { SmartContractCreationStatus, SmartContractProxyType } from 'types/api/contract'; +import type { InternalTransaction } from 'types/api/internalTransaction'; +import type { NFTTokenType, TokenInfo, TokenInstance, TokenReputation, TokenType } from 'types/api/token'; +import type { TokenTransfer, TokenTransferPagination } from 'types/api/tokenTransfer'; + +export interface AddressImplementation { + address_hash: string; + filecoin_robust_address?: string | null; + name?: string | null; +} + +export interface AddressTag { + label: string; + display_name: string; + address_hash: string; +} + +export interface WatchlistName { + label: string; + display_name: string; +} + +export interface UserTags { + private_tags: Array | null; + watchlist_names: Array | null; + public_tags: Array | null; +} + +export type AddressParamBasic = { + hash: string; + implementations: Array | null; + name: string | null; + is_contract: boolean; + is_verified: boolean | null; + ens_domain_name: string | null; + metadata?: { + reputation: number | null; + tags: Array; + } | null; + filecoin?: AddressFilecoinParams; + proxy_type?: SmartContractProxyType | null; + reputation?: TokenReputation; +}; -import type { UserTags, AddressImplementation, AddressParam, AddressFilecoinParams } from './addressParams'; -import type { Block } from './block'; -import type { SmartContractCreationStatus, SmartContractProxyType } from './contract'; -import type { CeloEpochRewardsType } from './epochs'; -import type { InternalTransaction } from './internalTransaction'; -import type { MudWorldSchema, MudWorldTable } from './mudWorlds'; -import type { NFTTokenType, TokenInfo, TokenInstance, TokenType } from './token'; -import type { TokenTransfer, TokenTransferPagination } from './tokenTransfer'; +export type AddressParam = UserTags & AddressParamBasic; + +export interface AddressCeloParams { + account: { + locked_celo: string; + metadata_url: string | null; + name: string | null; + nonvoting_locked_celo: string; + type: string; + vote_signer_address: AddressParam | null; + validator_signer_address: AddressParam | null; + attestation_signer_address: AddressParam | null; + } | null; +} export interface Address extends UserTags { block_number_balance_updated_at: number | null; @@ -37,23 +90,6 @@ export interface Address extends UserTags { proxy_type?: SmartContractProxyType | null; } -export interface AddressZilliqaParams { - is_scilla_contract: boolean; -} - -export interface AddressCeloParams { - account: { - locked_celo: string; - metadata_url: string | null; - name: string | null; - nonvoting_locked_celo: string; - type: string; - vote_signer_address: AddressParam | null; - validator_signer_address: AddressParam | null; - attestation_signer_address: AddressParam | null; - } | null; -} - export interface AddressCounters { transactions_count: string; token_transfers_count: string; @@ -220,82 +256,20 @@ export type AddressTabsCounters = { celo_election_rewards_count?: number | null; }; -// MUD framework -export type AddressMudTableItem = { - schema: MudWorldSchema; - table: MudWorldTable; -}; - -export type AddressMudTables = { - items: Array; - next_page_params: { - items_count: number; - table_id: string; +export type AddressXStarResponse = { + data: { + level: string | null; }; }; -export type AddressMudTablesFilter = { - q?: string; -}; +export type AddressesItem = AddressParam & { transactions_count: string; coin_balance: string | null }; -export type AddressMudRecords = { - items: Array; - schema: MudWorldSchema; - table: MudWorldTable; +export type AddressesResponse = { + items: Array; next_page_params: { + fetched_coin_balance: string; + hash: string; items_count: number; - key0: string; - key1: string; - key_bytes: string; - }; -}; - -export type AddressMudRecordsItem = { - decoded: Record>; - id: string; - is_deleted: boolean; - timestamp: string; -}; - -export type AddressMudRecordsFilter = { - filter_key0?: string; - filter_key1?: string; -}; - -export type AddressMudRecordsSorting = { - sort: 'key0' | 'key1'; - order: 'asc' | 'desc' | undefined; -}; - -export type AddressMudRecord = { - record: AddressMudRecordsItem; - schema: MudWorldSchema; - table: MudWorldTable; -}; - -export type AddressEpochRewardsResponse = { - items: Array; - next_page_params: { - amount: string; - associated_account_address_hash: string; - epoch_number: number; - items_count: number; - type: CeloEpochRewardsType; } | null; -}; - -export type AddressEpochRewardsItem = { - type: CeloEpochRewardsType; - token: TokenInfo; - amount: string; - block_timestamp: string; - account: AddressParam; - epoch_number: number; - associated_account: AddressParam; -}; - -export type AddressXStarResponse = { - data: { - level: string | null; - }; + total_supply: string; }; diff --git a/types/views/address.ts b/client/slices/address/types/view.ts similarity index 59% rename from types/views/address.ts rename to client/slices/address/types/view.ts index b726a930eb..5dc5b7904b 100644 --- a/types/views/address.ts +++ b/client/slices/address/types/view.ts @@ -18,16 +18,3 @@ export type AddressViewId = ArrayElement; export const ADDRESS_FORMATS = [ 'base16', 'bech32' ] as const; export type AddressFormat = typeof ADDRESS_FORMATS[ number ]; - -export const ADDRESS_3RD_PARTY_WIDGET_PAGES = [ 'eoa', 'contract', 'token' ] as const; - -export type Address3rdPartyWidget = { - name: string; - url: string; - icon: string; - title: string; - hint?: string; - valuePath: string; - pages: Array; - chainIds?: Record; -}; diff --git a/lib/address/bech32.ts b/client/slices/address/utils/bech32.ts similarity index 91% rename from lib/address/bech32.ts rename to client/slices/address/utils/bech32.ts index 85fcfda3ed..a3d7380e1e 100644 --- a/lib/address/bech32.ts +++ b/client/slices/address/utils/bech32.ts @@ -1,8 +1,9 @@ import { bech32 } from '@scure/base'; +import bytesToHex from 'client/shared/transformers/bytes-to-hex'; +import hexToBytes from 'client/shared/transformers/hex-to-bytes'; + import config from 'configs/app'; -import bytesToHex from 'lib/bytesToHex'; -import hexToBytes from 'lib/hexToBytes'; export const DATA_PART_REGEXP = /^[\da-z]{38}$/; export const BECH_32_SEPARATOR = '1'; // https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32 diff --git a/client/slices/address/utils/consts.ts b/client/slices/address/utils/consts.ts new file mode 100644 index 0000000000..3f8a4f9691 --- /dev/null +++ b/client/slices/address/utils/consts.ts @@ -0,0 +1,12 @@ +import type { AddressParam } from 'client/slices/address/types/api'; + +export const unknownAddress: Omit = { + is_contract: false, + is_verified: false, + implementations: null, + name: '', + private_tags: [], + public_tags: [], + watchlist_names: [], + ens_domain_name: null, +}; diff --git a/lib/address/getCheckedSummedAddress.ts b/client/slices/address/utils/get-checked-summed-address.ts similarity index 100% rename from lib/address/getCheckedSummedAddress.ts rename to client/slices/address/utils/get-checked-summed-address.ts diff --git a/client/slices/address/utils/is-evm-address.spec.ts b/client/slices/address/utils/is-evm-address.spec.ts new file mode 100644 index 0000000000..04b6de1410 --- /dev/null +++ b/client/slices/address/utils/is-evm-address.spec.ts @@ -0,0 +1,27 @@ +import { it, expect } from 'vitest'; + +import { isEvmAddress } from './is-evm-address'; + +it('should return true for valid EVM address', () => { + expect(isEvmAddress('0x1234567890123456789012345678901234567890')).toBe(true); + expect(isEvmAddress('0xabcdef1234567890123456789012345678901234')).toBe(true); + expect(isEvmAddress('0xABCDEF1234567890123456789012345678901234')).toBe(true); +}); + +it('should return false for invalid EVM address', () => { + expect(isEvmAddress('0x123')).toBe(false); + expect(isEvmAddress('123456789012345678901234567890123456789')).toBe(false); + expect(isEvmAddress('0xGGGGGG1234567890123456789012345678901234')).toBe(false); + expect(isEvmAddress('0x12345678901234567890123456789012345678901')).toBe(false); +}); + +it('should return false for empty or null input', () => { + expect(isEvmAddress('')).toBe(false); + expect(isEvmAddress(null as unknown as string)).toBe(false); + expect(isEvmAddress(undefined as unknown as string)).toBe(false); +}); + +it('should handle addresses with extra whitespace', () => { + expect(isEvmAddress(' 0x1234567890123456789012345678901234567890 ')).toBe(true); + expect(isEvmAddress(' 0x123 ')).toBe(false); +}); diff --git a/lib/address/isEvmAddress.ts b/client/slices/address/utils/is-evm-address.ts similarity index 100% rename from lib/address/isEvmAddress.ts rename to client/slices/address/utils/is-evm-address.ts diff --git a/ui/shared/address/utils.ts b/client/slices/address/utils/tx.ts similarity index 67% rename from ui/shared/address/utils.ts rename to client/slices/address/utils/tx.ts index 6743e46446..b29611216b 100644 --- a/ui/shared/address/utils.ts +++ b/client/slices/address/utils/tx.ts @@ -1,5 +1,3 @@ -import type { AddressParam } from 'types/api/addressParams'; - export type TxCourseType = 'in' | 'out' | 'self' | 'unspecified'; export function getTxCourseType(from: string, to: string | undefined, current?: string): TxCourseType { @@ -25,14 +23,3 @@ export function getTxCourseType(from: string, to: string | undefined, current?: return 'unspecified'; } - -export const unknownAddress: Omit = { - is_contract: false, - is_verified: false, - implementations: null, - name: '', - private_tags: [], - public_tags: [], - watchlist_names: [], - ens_domain_name: null, -}; diff --git a/ui/shared/block/BlockGasUsed.tsx b/client/slices/block/components/BlockGasUsed.tsx similarity index 87% rename from ui/shared/block/BlockGasUsed.tsx rename to client/slices/block/components/BlockGasUsed.tsx index 89f0a68b07..65f97955f7 100644 --- a/ui/shared/block/BlockGasUsed.tsx +++ b/client/slices/block/components/BlockGasUsed.tsx @@ -4,10 +4,9 @@ import React from 'react'; import config from 'configs/app'; import { Tooltip } from 'toolkit/chakra/tooltip'; - -import GasUsedToTargetRatio from '../GasUsedToTargetRatio'; -import TextSeparator from '../TextSeparator'; -import Utilization from '../Utilization/Utilization'; +import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; +import TextSeparator from 'ui/shared/TextSeparator'; +import Utilization from 'ui/shared/Utilization/Utilization'; const rollupFeature = config.features.rollup; diff --git a/ui/shared/block/BlockPendingUpdateAlert.tsx b/client/slices/block/components/BlockPendingUpdateAlert.tsx similarity index 100% rename from ui/shared/block/BlockPendingUpdateAlert.tsx rename to client/slices/block/components/BlockPendingUpdateAlert.tsx diff --git a/ui/shared/block/BlockPendingUpdateHint.tsx b/client/slices/block/components/BlockPendingUpdateHint.tsx similarity index 100% rename from ui/shared/block/BlockPendingUpdateHint.tsx rename to client/slices/block/components/BlockPendingUpdateHint.tsx diff --git a/ui/shared/entities/block/BlockEntity.pw.tsx b/client/slices/block/components/entity/BlockEntity.pw.tsx similarity index 100% rename from ui/shared/entities/block/BlockEntity.pw.tsx rename to client/slices/block/components/entity/BlockEntity.pw.tsx diff --git a/ui/shared/entities/block/BlockEntity.tsx b/client/slices/block/components/entity/BlockEntity.tsx similarity index 97% rename from ui/shared/entities/block/BlockEntity.tsx rename to client/slices/block/components/entity/BlockEntity.tsx index a7021c9135..ef27a7398f 100644 --- a/ui/shared/entities/block/BlockEntity.tsx +++ b/client/slices/block/components/entity/BlockEntity.tsx @@ -6,10 +6,9 @@ import { route } from 'nextjs/routes'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; import * as EntityBase from 'ui/shared/entities/base/components'; +import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; -import { distributeEntityProps } from '../base/utils'; - type LinkProps = EntityBase.LinkBaseProps & Partial>; const Link = chakra((props: LinkProps) => { diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png b/client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png similarity index 100% rename from ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png rename to client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_dark-color-mode_external-link-dark-mode-1.png diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_customization-1.png b/client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_customization-1.png similarity index 100% rename from ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_customization-1.png rename to client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_customization-1.png diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png b/client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png similarity index 100% rename from ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png rename to client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_external-link-dark-mode-1.png diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-content-1.png b/client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-content-1.png similarity index 100% rename from ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-content-1.png rename to client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-content-1.png diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-subheading-1.png b/client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-subheading-1.png similarity index 100% rename from ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-subheading-1.png rename to client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_icon-sizes-subheading-1.png diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_loading-1.png b/client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_loading-1.png similarity index 100% rename from ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_loading-1.png rename to client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_loading-1.png diff --git a/ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_long-number-1.png b/client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_long-number-1.png similarity index 100% rename from ui/shared/entities/block/__screenshots__/BlockEntity.pw.tsx_default_long-number-1.png rename to client/slices/block/components/entity/__screenshots__/BlockEntity.pw.tsx_default_long-number-1.png diff --git a/ui/block/useBlockInternalTxsQuery.tsx b/client/slices/block/hooks/useBlockInternalTxsQuery.ts similarity index 100% rename from ui/block/useBlockInternalTxsQuery.tsx rename to client/slices/block/hooks/useBlockInternalTxsQuery.ts diff --git a/ui/block/useBlockQuery.tsx b/client/slices/block/hooks/useBlockQuery.ts similarity index 86% rename from ui/block/useBlockQuery.tsx rename to client/slices/block/hooks/useBlockQuery.ts index 98e8e3387a..a37507651a 100644 --- a/ui/block/useBlockQuery.tsx +++ b/client/slices/block/hooks/useBlockQuery.ts @@ -3,14 +3,17 @@ import { useQuery } from '@tanstack/react-query'; import React from 'react'; import type { Chain, GetBlockReturnType } from 'viem'; -import type { Block } from 'types/api/block'; - -import type { ResourceError } from 'lib/api/resources'; -import useApiQuery from 'lib/api/useApiQuery'; -import { retry } from 'lib/api/useQueryClientConfig'; -import { publicClient } from 'lib/web3/client'; -import formatBlockData from 'lib/web3/rpc/formatBlockData'; -import { BLOCK } from 'stubs/block'; +import type { Block } from 'client/slices/block/types/api'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; +import { retry } from 'client/api/hooks/useQueryClientConfig'; +import type { ResourceError } from 'client/api/resources'; + +import { BLOCK } from 'client/slices/block/stubs/block'; +import formatRpcData from 'client/slices/block/utils/format-rpc-data'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + import { GET_BLOCK } from 'stubs/RPC'; import { SECOND } from 'toolkit/utils/consts'; @@ -69,7 +72,7 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery { return publicClient.getBlock(blockParams).catch(() => null); }, select: (block) => { - return formatBlockData(block); + return formatRpcData(block); }, placeholderData: GET_BLOCK, enabled: !latestBlockQuery.isPending, diff --git a/ui/block/useBlockTxsQuery.tsx b/client/slices/block/hooks/useBlockTxsQuery.ts similarity index 92% rename from ui/block/useBlockTxsQuery.tsx rename to client/slices/block/hooks/useBlockTxsQuery.ts index c0df29a0d0..1b2978b5e3 100644 --- a/ui/block/useBlockTxsQuery.tsx +++ b/client/slices/block/hooks/useBlockTxsQuery.ts @@ -3,18 +3,22 @@ import { useQuery } from '@tanstack/react-query'; import React from 'react'; import type { Chain, GetBlockReturnType } from 'viem'; -import type { BlockTransactionsResponse } from 'types/api/block'; +import type { BlockTransactionsResponse } from 'client/slices/block/types/api'; + +import { retry } from 'client/api/hooks/useQueryClientConfig'; +import type { ResourceError } from 'client/api/resources'; + +import { unknownAddress } from 'client/slices/address/utils/consts'; +import { TX } from 'client/slices/tx/stubs/tx'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + +import hexToDecimal from 'client/shared/transformers/hex-to-decimal'; -import type { ResourceError } from 'lib/api/resources'; -import { retry } from 'lib/api/useQueryClientConfig'; import dayjs from 'lib/date/dayjs'; -import hexToDecimal from 'lib/hexToDecimal'; -import { publicClient } from 'lib/web3/client'; import { GET_BLOCK_WITH_TRANSACTIONS } from 'stubs/RPC'; -import { TX } from 'stubs/tx'; import { generateListStub } from 'stubs/utils'; import { SECOND } from 'toolkit/utils/consts'; -import { unknownAddress } from 'ui/shared/address/utils'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import { emptyPagination } from 'ui/shared/pagination/utils'; diff --git a/mocks/blocks/block.ts b/client/slices/block/mocks/block.ts similarity index 98% rename from mocks/blocks/block.ts rename to client/slices/block/mocks/block.ts index e58b41e6eb..82a1ef8623 100644 --- a/mocks/blocks/block.ts +++ b/client/slices/block/mocks/block.ts @@ -1,13 +1,13 @@ /* eslint-disable max-len */ import type { RpcBlock } from 'viem'; -import type { Block, BlocksResponse, ZilliqaBlockData } from 'types/api/block'; +import type { ZilliqaBlockData } from 'client/features/chain-variants/zilliqa/types/api'; +import type { Block, BlocksResponse } from 'client/slices/block/types/api'; +import * as addressMock from 'mocks/address/address'; +import * as tokenMock from 'mocks/tokens/tokenInfo'; import { ZERO_ADDRESS } from 'toolkit/utils/consts'; -import * as addressMock from '../address/address'; -import * as tokenMock from '../tokens/tokenInfo'; - export const base: Block = { base_fee_per_gas: '10000000000', burnt_fees: '5449200000000000', diff --git a/ui/pages/BlockCountdown.pw.tsx b/client/slices/block/pages/countdown-details/BlockCountdown.pw.tsx similarity index 100% rename from ui/pages/BlockCountdown.pw.tsx rename to client/slices/block/pages/countdown-details/BlockCountdown.pw.tsx diff --git a/ui/pages/BlockCountdown.tsx b/client/slices/block/pages/countdown-details/BlockCountdown.tsx similarity index 92% rename from ui/pages/BlockCountdown.tsx rename to client/slices/block/pages/countdown-details/BlockCountdown.tsx index 406b1741b7..bb2655fdf5 100644 --- a/ui/pages/BlockCountdown.tsx +++ b/client/slices/block/pages/countdown-details/BlockCountdown.tsx @@ -4,11 +4,13 @@ import React from 'react'; import { route } from 'nextjs/routes'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { useMultichainContext } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { Image } from 'toolkit/chakra/image'; @@ -16,15 +18,15 @@ import { Link } from 'toolkit/chakra/link'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; import { downloadBlob } from 'toolkit/utils/file'; -import BlockCountdownTimer from 'ui/blockCountdown/BlockCountdownTimer'; -import createGoogleCalendarLink from 'ui/blockCountdown/createGoogleCalendarLink'; -import createIcsFileBlob from 'ui/blockCountdown/createIcsFileBlob'; +import CapybaraRunner from 'ui/games/CapybaraRunner'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import IconSvg from 'ui/shared/IconSvg'; import StatsWidget from 'ui/shared/stats/StatsWidget'; import Time from 'ui/shared/time/Time'; -import CapybaraRunner from '../games/CapybaraRunner'; +import BlockCountdownTimer from './BlockCountdownTimer'; +import createGoogleCalendarLink from './create-google-calendar-link'; +import createIcsFileBlob from './create-ics-file-blob'; type Props = { hideCapybaraRunner?: boolean; diff --git a/ui/blockCountdown/BlockCountdownTimer.tsx b/client/slices/block/pages/countdown-details/BlockCountdownTimer.tsx similarity index 95% rename from ui/blockCountdown/BlockCountdownTimer.tsx rename to client/slices/block/pages/countdown-details/BlockCountdownTimer.tsx index 9bead9f672..1bdbcf3e97 100644 --- a/ui/blockCountdown/BlockCountdownTimer.tsx +++ b/client/slices/block/pages/countdown-details/BlockCountdownTimer.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { SECOND } from 'toolkit/utils/consts'; import BlockCountdownTimerItem from './BlockCountdownTimerItem'; -import splitSecondsInPeriods from './splitSecondsInPeriods'; +import splitSecondsInPeriods from './split-seconds-in-periods'; interface Props { value: number; diff --git a/ui/blockCountdown/BlockCountdownTimerItem.tsx b/client/slices/block/pages/countdown-details/BlockCountdownTimerItem.tsx similarity index 100% rename from ui/blockCountdown/BlockCountdownTimerItem.tsx rename to client/slices/block/pages/countdown-details/BlockCountdownTimerItem.tsx diff --git a/ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-desktop-1.png b/client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-desktop-1.png similarity index 100% rename from ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-desktop-1.png rename to client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-desktop-1.png diff --git a/ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-mobile-base-view-1.png b/client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-mobile-base-view-1.png similarity index 100% rename from ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-mobile-base-view-1.png rename to client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_long-period-until-the-block-mobile-base-view-1.png diff --git a/ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-desktop-1.png b/client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-desktop-1.png similarity index 100% rename from ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-desktop-1.png rename to client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-desktop-1.png diff --git a/ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-mobile-base-view-1.png b/client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-mobile-base-view-1.png similarity index 100% rename from ui/pages/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-mobile-base-view-1.png rename to client/slices/block/pages/countdown-details/__screenshots__/BlockCountdown.pw.tsx_default_short-period-until-the-block-mobile-base-view-1.png diff --git a/ui/blockCountdown/createGoogleCalendarLink.ts b/client/slices/block/pages/countdown-details/create-google-calendar-link.ts similarity index 100% rename from ui/blockCountdown/createGoogleCalendarLink.ts rename to client/slices/block/pages/countdown-details/create-google-calendar-link.ts diff --git a/ui/blockCountdown/createIcsFileBlob.ts b/client/slices/block/pages/countdown-details/create-ics-file-blob.ts similarity index 100% rename from ui/blockCountdown/createIcsFileBlob.ts rename to client/slices/block/pages/countdown-details/create-ics-file-blob.ts diff --git a/ui/blockCountdown/splitSecondsInPeriods.ts b/client/slices/block/pages/countdown-details/split-seconds-in-periods.ts similarity index 100% rename from ui/blockCountdown/splitSecondsInPeriods.ts rename to client/slices/block/pages/countdown-details/split-seconds-in-periods.ts diff --git a/ui/pages/BlockCountdownIndex.pw.tsx b/client/slices/block/pages/countdown-index/BlockCountdownIndex.pw.tsx similarity index 100% rename from ui/pages/BlockCountdownIndex.pw.tsx rename to client/slices/block/pages/countdown-index/BlockCountdownIndex.pw.tsx diff --git a/ui/pages/BlockCountdownIndex.tsx b/client/slices/block/pages/countdown-index/BlockCountdownIndex.tsx similarity index 100% rename from ui/pages/BlockCountdownIndex.tsx rename to client/slices/block/pages/countdown-index/BlockCountdownIndex.tsx diff --git a/ui/pages/__screenshots__/BlockCountdownIndex.pw.tsx_default_base-view-mobile-1.png b/client/slices/block/pages/countdown-index/__screenshots__/BlockCountdownIndex.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/BlockCountdownIndex.pw.tsx_default_base-view-mobile-1.png rename to client/slices/block/pages/countdown-index/__screenshots__/BlockCountdownIndex.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/pages/__screenshots__/BlockCountdownIndex.pw.tsx_mobile_base-view-mobile-1.png b/client/slices/block/pages/countdown-index/__screenshots__/BlockCountdownIndex.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/pages/__screenshots__/BlockCountdownIndex.pw.tsx_mobile_base-view-mobile-1.png rename to client/slices/block/pages/countdown-index/__screenshots__/BlockCountdownIndex.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/pages/Block.pw.tsx b/client/slices/block/pages/details/Block.pw.tsx similarity index 98% rename from ui/pages/Block.pw.tsx rename to client/slices/block/pages/details/Block.pw.tsx index 38a8ff5831..501a94aff4 100644 --- a/ui/pages/Block.pw.tsx +++ b/client/slices/block/pages/details/Block.pw.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { numberToHex } from 'viem'; +import * as blockMock from 'client/slices/block/mocks/block'; + import config from 'configs/app'; -import * as blockMock from 'mocks/blocks/block'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; diff --git a/ui/pages/Block.tsx b/client/slices/block/pages/details/Block.tsx similarity index 80% rename from ui/pages/Block.tsx rename to client/slices/block/pages/details/Block.tsx index bc90561649..2bd80ab66f 100644 --- a/ui/pages/Block.tsx +++ b/client/slices/block/pages/details/Block.tsx @@ -8,35 +8,38 @@ import type { PaginationParams } from 'ui/shared/pagination/types'; import { routeParams } from 'nextjs/routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockPendingUpdateAlert from 'client/slices/block/components/BlockPendingUpdateAlert'; +import * as BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import useBlockInternalTxsQuery from 'client/slices/block/hooks/useBlockInternalTxsQuery'; +import useBlockQuery from 'client/slices/block/hooks/useBlockQuery'; +import useBlockTxsQuery from 'client/slices/block/hooks/useBlockTxsQuery'; +import BlockDetails from 'client/slices/block/pages/details/BlockDetails'; +import BlockInternalTxs from 'client/slices/block/pages/details/BlockInternalTxs'; +import TxsWithFrontendSorting from 'client/slices/tx/pages/index/list/TxsWithFrontendSorting'; + +import BlockDeposits from 'client/features/chain-variants/beacon-chain/pages/block/BlockDeposits'; +import BlockWithdrawals from 'client/features/chain-variants/beacon-chain/pages/block/BlockWithdrawals'; +import useBlockDepositsQuery from 'client/features/chain-variants/beacon-chain/pages/block/useBlockDepositsQuery'; +import useBlockWithdrawalsQuery from 'client/features/chain-variants/beacon-chain/pages/block/useBlockWithdrawalsQuery'; +import BlockCeloEpochTag from 'client/features/chain-variants/celo/pages/block/BlockCeloEpochTag'; +import useBlockBlobTxsQuery from 'client/features/data-availability/hooks/useBlockBlobTxsQuery'; + +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import throwOnAbsentParamError from 'client/shared/errors/throw-on-absent-param-error'; +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import throwOnAbsentParamError from 'lib/errors/throwOnAbsentParamError'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { Skeleton } from 'toolkit/chakra/skeleton'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import BlockCeloEpochTag from 'ui/block/BlockCeloEpochTag'; -import BlockDeposits from 'ui/block/BlockDeposits'; -import BlockDetails from 'ui/block/BlockDetails'; -import BlockInternalTxs from 'ui/block/BlockInternalTxs'; -import BlockWithdrawals from 'ui/block/BlockWithdrawals'; -import useBlockBlobTxsQuery from 'ui/block/useBlockBlobTxsQuery'; -import useBlockDepositsQuery from 'ui/block/useBlockDepositsQuery'; -import useBlockInternalTxsQuery from 'ui/block/useBlockInternalTxsQuery'; -import useBlockQuery from 'ui/block/useBlockQuery'; -import useBlockTxsQuery from 'ui/block/useBlockTxsQuery'; -import useBlockWithdrawalsQuery from 'ui/block/useBlockWithdrawalsQuery'; import TextAd from 'ui/shared/ad/TextAd'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; -import BlockPendingUpdateAlert from 'ui/shared/block/BlockPendingUpdateAlert'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import * as BlockEntity from 'ui/shared/entities/block/BlockEntity'; import NetworkExplorers from 'ui/shared/NetworkExplorers'; import PageTitle from 'ui/shared/Page/PageTitle'; import Pagination from 'ui/shared/pagination/Pagination'; -import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting'; const TAB_LIST_PROPS = { marginBottom: 0, @@ -188,7 +191,7 @@ const BlockPageContent = () => { fontWeight={ 500 } > - { capitalize(getNetworkValidatorTitle()) } + { capitalize(getChainValidatorTitle()) } diff --git a/ui/block/BlockDetails.pw.tsx b/client/slices/block/pages/details/BlockDetails.pw.tsx similarity index 92% rename from ui/block/BlockDetails.pw.tsx rename to client/slices/block/pages/details/BlockDetails.pw.tsx index a10a06ef5a..6cd47c6717 100644 --- a/ui/block/BlockDetails.pw.tsx +++ b/client/slices/block/pages/details/BlockDetails.pw.tsx @@ -1,11 +1,12 @@ import React from 'react'; -import * as blockMock from 'mocks/blocks/block'; +import type { BlockQuery } from 'client/slices/block/hooks/useBlockQuery'; +import * as blockMock from 'client/slices/block/mocks/block'; + import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import BlockDetails from './BlockDetails'; -import type { BlockQuery } from './useBlockQuery'; const hooksConfig = { router: { diff --git a/ui/block/BlockDetails.tsx b/client/slices/block/pages/details/BlockDetails.tsx similarity index 95% rename from ui/block/BlockDetails.tsx rename to client/slices/block/pages/details/BlockDetails.tsx index 2d90281af5..f6a32874f2 100644 --- a/ui/block/BlockDetails.tsx +++ b/client/slices/block/pages/details/BlockDetails.tsx @@ -8,13 +8,25 @@ import { ZKSYNC_L2_TX_BATCH_STATUSES } from 'types/api/zkSyncL2'; import { route, routeParams } from 'nextjs/routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockGasUsed from 'client/slices/block/components/BlockGasUsed'; +import type { BlockQuery } from 'client/slices/block/hooks/useBlockQuery'; +import getBlockReward from 'client/slices/block/utils/get-block-reward'; + +import BlockDetailsBaseFeeCelo from 'client/features/chain-variants/celo/pages/block/BlockDetailsBaseFeeCelo'; +import BlockDetailsZilliqaQuorumCertificate from 'client/features/chain-variants/zilliqa/pages/block/BlockDetailsZilliqaQuorumCertificate'; +import BlockDetailsBlobInfo from 'client/features/data-availability/pages/block/BlockDetailsBlobInfo'; +import BatchEntityL2 from 'client/features/rollup/common/components/BatchEntityL2'; +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import getBlockReward from 'lib/block/getBlockReward'; import { useMultichainContext } from 'lib/contexts/multichain'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import * as arbitrum from 'lib/rollups/arbitrum'; import { formatZkSyncL2TxnBatchStatus, layerLabels } from 'lib/rollups/utils'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { CollapsibleDetails } from 'toolkit/chakra/collapsible'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; @@ -22,14 +34,9 @@ import { Tooltip } from 'toolkit/chakra/tooltip'; import { ZERO } from 'toolkit/utils/consts'; import { space } from 'toolkit/utils/htmlEntities'; import OptimisticL2TxnBatchDA from 'ui/shared/batch/OptimisticL2TxnBatchDA'; -import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; import PrevNext from 'ui/shared/PrevNext'; @@ -42,11 +49,6 @@ import { WEI } from 'ui/shared/value/utils'; import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps'; import ZkSyncL2TxnBatchHashesInfo from 'ui/txnBatches/zkSyncL2/ZkSyncL2TxnBatchHashesInfo'; -import BlockDetailsBaseFeeCelo from './details/BlockDetailsBaseFeeCelo'; -import BlockDetailsBlobInfo from './details/BlockDetailsBlobInfo'; -import BlockDetailsZilliqaQuorumCertificate from './details/BlockDetailsZilliqaQuorumCertificate'; -import type { BlockQuery } from './useBlockQuery'; - const zkSyncVerificationSteps = ZKSYNC_L2_TX_BATCH_STATUSES.map(formatZkSyncL2TxnBatchStatus); interface Props { @@ -79,7 +81,7 @@ const BlockDetails = ({ query }: Props) => { const { totalReward, staticReward, burntFees, txFees } = getBlockReward(data); - const validatorTitle = getNetworkValidatorTitle(); + const validatorTitle = getChainValidatorTitle(); const rewardBreakDown = (() => { if (rollupFeature.isEnabled || totalReward.isEqualTo(ZERO) || txFees.isEqualTo(ZERO) || burntFees.isEqualTo(ZERO)) { @@ -384,7 +386,7 @@ const BlockDetails = ({ query }: Props) => { <> { <> ; diff --git a/ui/blocks/BlocksListItem.tsx b/client/slices/block/pages/index/BlocksListItem.tsx similarity index 89% rename from ui/blocks/BlocksListItem.tsx rename to client/slices/block/pages/index/BlocksListItem.tsx index 6cdfc80a1f..4aff253c58 100644 --- a/ui/blocks/BlocksListItem.tsx +++ b/client/slices/block/pages/index/BlocksListItem.tsx @@ -3,21 +3,23 @@ import BigNumber from 'bignumber.js'; import { capitalize } from 'es-toolkit'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockGasUsed from 'client/slices/block/components/BlockGasUsed'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import getBlockTotalReward from 'client/slices/block/utils/get-block-total-reward'; + +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; -import getBlockTotalReward from 'lib/block/getBlockTotalReward'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; -import { currencyUnits } from 'lib/units'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; -import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import IconSvg from 'ui/shared/IconSvg'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; @@ -77,7 +79,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement, animation, chain ) } { !config.UI.views.block.hiddenFields?.miner && ( - { capitalize(getNetworkValidatorTitle()) } + { capitalize(getChainValidatorTitle()) } { }, }); - const networkUtilization = getNetworkUtilizationParams(statsQuery.data?.network_utilization_percentage ?? 0); + const networkUtilization = getChainUtilizationParams(statsQuery.data?.network_utilization_percentage ?? 0); return ( diff --git a/ui/blocks/BlocksTable.tsx b/client/slices/block/pages/index/BlocksTable.tsx similarity index 88% rename from ui/blocks/BlocksTable.tsx rename to client/slices/block/pages/index/BlocksTable.tsx index 3588513e8a..c38b916b19 100644 --- a/ui/blocks/BlocksTable.tsx +++ b/client/slices/block/pages/index/BlocksTable.tsx @@ -1,16 +1,18 @@ import { capitalize } from 'es-toolkit'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; import type { ClusterChainConfig } from 'types/multichain'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; +import BlocksTableItem from 'client/slices/block/pages/index/BlocksTableItem'; + +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import { currencyUnits } from 'client/shared/chain/units'; +import useInitialList from 'client/shared/lists/useInitialList'; + import config from 'configs/app'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import useInitialList from 'lib/hooks/useInitialList'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; -import { currencyUnits } from 'lib/units'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; -import BlocksTableItem from 'ui/blocks/BlocksTableItem'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; @@ -58,7 +60,7 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum Size, bytes { !config.UI.views.block.hiddenFields?.miner && ( - { capitalize(getNetworkValidatorTitle()) } + { capitalize(getChainValidatorTitle()) } ) } Txn diff --git a/ui/blocks/BlocksTableItem.tsx b/client/slices/block/pages/index/BlocksTableItem.tsx similarity index 91% rename from ui/blocks/BlocksTableItem.tsx rename to client/slices/block/pages/index/BlocksTableItem.tsx index c61ac5c884..36f650c81a 100644 --- a/ui/blocks/BlocksTableItem.tsx +++ b/client/slices/block/pages/index/BlocksTableItem.tsx @@ -2,21 +2,22 @@ import { Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockGasUsed from 'client/slices/block/components/BlockGasUsed'; +import BlockPendingUpdateHint from 'client/slices/block/components/BlockPendingUpdateHint'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import getBlockTotalReward from 'client/slices/block/utils/get-block-total-reward'; + import config from 'configs/app'; -import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tooltip } from 'toolkit/chakra/tooltip'; -import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; -import BlockPendingUpdateHint from 'ui/shared/block/BlockPendingUpdateHint'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; import IconSvg from 'ui/shared/IconSvg'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png b/client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/pages/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png rename to client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png b/client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png similarity index 100% rename from ui/pages/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png rename to client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_hidden-fields-1.png diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png b/client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png similarity index 100% rename from ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png rename to client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_mobile-base-view-1.png diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png b/client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png similarity index 100% rename from ui/pages/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png rename to client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_mobile-hidden-fields-1.png diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png b/client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png similarity index 100% rename from ui/pages/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png rename to client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_new-item-from-socket-1.png diff --git a/ui/pages/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png b/client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png similarity index 100% rename from ui/pages/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png rename to client/slices/block/pages/index/__screenshots__/Blocks.pw.tsx_default_socket-error-1.png diff --git a/stubs/block.ts b/client/slices/block/stubs/block.ts similarity index 87% rename from stubs/block.ts rename to client/slices/block/stubs/block.ts index 0861bd3625..7e7a2564e8 100644 --- a/stubs/block.ts +++ b/client/slices/block/stubs/block.ts @@ -1,6 +1,6 @@ -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; -import { ADDRESS_PARAMS } from './addressParams'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; export const BLOCK_HASH = '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70'; diff --git a/client/slices/block/types/api.ts b/client/slices/block/types/api.ts new file mode 100644 index 0000000000..ed8de02e83 --- /dev/null +++ b/client/slices/block/types/api.ts @@ -0,0 +1,89 @@ +import type { BlockCelo } from 'client/features/chain-variants/celo/types/api'; +import type { BlockRootstock } from 'client/features/chain-variants/rootstock/types/api'; +import type { BlockZilliqa } from 'client/features/chain-variants/zilliqa/types/api'; +import type { BlockDataAvailability } from 'client/features/data-availability/types/api'; +import type { BlockArbitrum } from 'client/features/rollup/arbitrum/types/api'; +import type { BlockOptimism } from 'client/features/rollup/optimistic/types/api'; +import type { BlockZkSync } from 'client/features/rollup/zk-sync/types/api'; +import type { AddressParam } from 'client/slices/address/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; +import type { InternalTransaction } from 'types/api/internalTransaction'; +import type { Reward } from 'types/api/reward'; + +export type BlockType = 'block' | 'reorg' | 'uncle'; + +export interface Block extends BlockArbitrum, BlockOptimism, BlockZkSync, BlockCelo, BlockZilliqa, BlockRootstock, BlockDataAvailability { + height: number; + timestamp: string; + transactions_count: number; + internal_transactions_count: number; + miner: AddressParam; + size?: number; + hash: string; + parent_hash: string; + difficulty?: string; + total_difficulty?: string | null; + gas_used: string | null; + gas_limit: string; + nonce: string; + base_fee_per_gas?: string | null; + burnt_fees: string | null; + priority_fee: string | null; + extra_data: string | null; + state_root: string | null; + rewards?: Array; + gas_target_percentage: number | null; + gas_used_percentage: number | null; + burnt_fees_percentage: number | null; + type: BlockType; + transaction_fees: string | null; + uncles_hashes: Array; + withdrawals_count?: number; + beacon_deposits_count?: number; + is_pending_update?: boolean; +} + +export interface BlocksResponse { + items: Array; + next_page_params: { + block_number: number; + items_count: number; + } | null; +} + +export interface BlockTransactionsResponse { + items: Array; + next_page_params: { + block_number: number; + items_count: number; + index: number; + } | null; +} + +export interface BlockInternalTransactionsResponse { + items: Array; + next_page_params: { + block_index: number; + items_count: number; + } | null; +} + +export interface NewBlockSocketResponse { + average_block_time: string; + block: Block; +} + +export interface BlockFilters { + type?: BlockType; +} + +export interface BlockCountdownResponse { + result: { + CountdownBlock: string; + CurrentBlock: string; + EstimateTimeInSec: string; + RemainingBlock: string; + } | null; +} + +export type { BlockWithdrawalsResponse, BlockWithdrawalsItem } from 'client/features/chain-variants/beacon-chain/types/api'; diff --git a/lib/web3/rpc/formatBlockData.ts b/client/slices/block/utils/format-rpc-data.ts similarity index 82% rename from lib/web3/rpc/formatBlockData.ts rename to client/slices/block/utils/format-rpc-data.ts index 05af1c0399..72593527b2 100644 --- a/lib/web3/rpc/formatBlockData.ts +++ b/client/slices/block/utils/format-rpc-data.ts @@ -1,11 +1,12 @@ import type { Chain, GetBlockReturnType } from 'viem'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; + +import { unknownAddress } from 'client/slices/address/utils/consts'; import dayjs from 'lib/date/dayjs'; -import { unknownAddress } from 'ui/shared/address/utils'; -export default function formatBlockData(block: GetBlockReturnType | null): Block | null { +export default function formatRpcData(block: GetBlockReturnType | null): Block | null { if (!block) { return null; } diff --git a/lib/block/getBlockReward.ts b/client/slices/block/utils/get-block-reward.ts similarity index 89% rename from lib/block/getBlockReward.ts rename to client/slices/block/utils/get-block-reward.ts index dfe2558232..8fe8c23725 100644 --- a/lib/block/getBlockReward.ts +++ b/client/slices/block/utils/get-block-reward.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; export default function getBlockReward(block: Block) { const txFees = BigNumber(block.transaction_fees || 0); diff --git a/lib/block/getBlockTotalReward.ts b/client/slices/block/utils/get-block-total-reward.ts similarity index 86% rename from lib/block/getBlockTotalReward.ts rename to client/slices/block/utils/get-block-total-reward.ts index 3edebaea61..4bb05d54fc 100644 --- a/lib/block/getBlockTotalReward.ts +++ b/client/slices/block/utils/get-block-total-reward.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; import { ZERO } from 'toolkit/utils/consts'; import { WEI } from 'ui/shared/value/utils'; diff --git a/ui/tx/TxInternals.pw.tsx b/client/slices/internal-tx/pages/tx/TxInternals.pw.tsx similarity index 85% rename from ui/tx/TxInternals.pw.tsx rename to client/slices/internal-tx/pages/tx/TxInternals.pw.tsx index 08db79959c..41ec6aeb4f 100644 --- a/ui/tx/TxInternals.pw.tsx +++ b/client/slices/internal-tx/pages/tx/TxInternals.pw.tsx @@ -1,11 +1,12 @@ import React from 'react'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import * as internalTxsMock from 'mocks/txs/internalTxs'; -import * as txMock from 'mocks/txs/tx'; import { test, expect } from 'playwright/lib'; import TxInternals from './TxInternals'; -import type { TxQuery } from './useTxQuery'; const TX_HASH = txMock.base.hash; const hooksConfig = { diff --git a/ui/tx/TxInternals.tsx b/client/slices/internal-tx/pages/tx/TxInternals.tsx similarity index 92% rename from ui/tx/TxInternals.tsx rename to client/slices/internal-tx/pages/tx/TxInternals.tsx index f830765856..273632ff4e 100644 --- a/ui/tx/TxInternals.tsx +++ b/client/slices/internal-tx/pages/tx/TxInternals.tsx @@ -3,6 +3,10 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import compareBns from 'lib/bigint/compareBns'; // import { apos } from 'toolkit/utils/htmlEntities'; import { INTERNAL_TX } from 'stubs/internalTx'; @@ -13,13 +17,10 @@ import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import { default as getNextSortValueShared } from 'ui/shared/sort/getNextSortValue'; -import TxInternalsList from 'ui/tx/internals/TxInternalsList'; -import TxInternalsTable from 'ui/tx/internals/TxInternalsTable'; -import type { Sort, SortField } from 'ui/tx/internals/utils'; -import TxPendingAlert from 'ui/tx/TxPendingAlert'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; -import type { TxQuery } from './useTxQuery'; +import type { Sort, SortField } from '../../utils/utils'; +import TxInternalsList from './TxInternalsList'; +import TxInternalsTable from './TxInternalsTable'; const SORT_SEQUENCE: Record> = { value: [ 'value-desc', 'value-asc', 'default' ], diff --git a/ui/tx/internals/TxInternalsList.tsx b/client/slices/internal-tx/pages/tx/TxInternalsList.tsx similarity index 87% rename from ui/tx/internals/TxInternalsList.tsx rename to client/slices/internal-tx/pages/tx/TxInternalsList.tsx index b057015525..a6aabb6458 100644 --- a/ui/tx/internals/TxInternalsList.tsx +++ b/client/slices/internal-tx/pages/tx/TxInternalsList.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; -import TxInternalsListItem from 'ui/tx/internals/TxInternalsListItem'; +import TxInternalsListItem from './TxInternalsListItem'; const TxInternalsList = ({ data, isLoading }: { data: Array; isLoading?: boolean }) => { return ( diff --git a/ui/tx/internals/TxInternalsListItem.tsx b/client/slices/internal-tx/pages/tx/TxInternalsListItem.tsx similarity index 87% rename from ui/tx/internals/TxInternalsListItem.tsx rename to client/slices/internal-tx/pages/tx/TxInternalsListItem.tsx index 3435a45054..14b187ec78 100644 --- a/ui/tx/internals/TxInternalsListItem.tsx +++ b/client/slices/internal-tx/pages/tx/TxInternalsListItem.tsx @@ -3,14 +3,17 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; -import { currencyUnits } from 'lib/units'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import TxStatus from 'client/slices/tx/components/TxStatus'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import TxStatus from 'ui/shared/statusTag/TxStatus'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; -import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; + +import { TX_INTERNALS_ITEMS } from '../../utils/utils'; type Props = InternalTransaction & { isLoading?: boolean }; diff --git a/ui/tx/internals/TxInternalsTable.tsx b/client/slices/internal-tx/pages/tx/TxInternalsTable.tsx similarity index 86% rename from ui/tx/internals/TxInternalsTable.tsx rename to client/slices/internal-tx/pages/tx/TxInternalsTable.tsx index 8c0c043cd7..b779d49d5f 100644 --- a/ui/tx/internals/TxInternalsTable.tsx +++ b/client/slices/internal-tx/pages/tx/TxInternalsTable.tsx @@ -2,11 +2,14 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import { currencyUnits } from 'lib/units'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; -import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem'; -import type { Sort, SortField } from 'ui/tx/internals/utils'; + +import type { Sort, SortField } from '../../utils/utils'; +import TxInternalsTableItem from './TxInternalsTableItem'; interface Props { data: Array; diff --git a/ui/tx/internals/TxInternalsTableItem.tsx b/client/slices/internal-tx/pages/tx/TxInternalsTableItem.tsx similarity index 89% rename from ui/tx/internals/TxInternalsTableItem.tsx rename to client/slices/internal-tx/pages/tx/TxInternalsTableItem.tsx index 725ee28311..c5f5b66ef0 100644 --- a/ui/tx/internals/TxInternalsTableItem.tsx +++ b/client/slices/internal-tx/pages/tx/TxInternalsTableItem.tsx @@ -3,12 +3,14 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import TxStatus from 'client/slices/tx/components/TxStatus'; + import { Badge } from 'toolkit/chakra/badge'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; -import TxStatus from 'ui/shared/statusTag/TxStatus'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; -import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; + +import { TX_INTERNALS_ITEMS } from '../../utils/utils'; type Props = InternalTransaction & { isLoading?: boolean; diff --git a/ui/tx/__screenshots__/TxInternals.pw.tsx_default_base-view-mobile-1.png b/client/slices/internal-tx/pages/tx/__screenshots__/TxInternals.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxInternals.pw.tsx_default_base-view-mobile-1.png rename to client/slices/internal-tx/pages/tx/__screenshots__/TxInternals.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/tx/__screenshots__/TxInternals.pw.tsx_mobile_base-view-mobile-1.png b/client/slices/internal-tx/pages/tx/__screenshots__/TxInternals.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxInternals.pw.tsx_mobile_base-view-mobile-1.png rename to client/slices/internal-tx/pages/tx/__screenshots__/TxInternals.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/tx/internals/utils.ts b/client/slices/internal-tx/utils/utils.ts similarity index 100% rename from ui/tx/internals/utils.ts rename to client/slices/internal-tx/utils/utils.ts diff --git a/mocks/txs/decodedInputData.ts b/client/slices/log/mocks/decoded-input.ts similarity index 94% rename from mocks/txs/decodedInputData.ts rename to client/slices/log/mocks/decoded-input.ts index bbb1274258..04504b8f90 100644 --- a/mocks/txs/decodedInputData.ts +++ b/client/slices/log/mocks/decoded-input.ts @@ -1,4 +1,4 @@ -import type { DecodedInput } from 'types/api/decodedInput'; +import type { DecodedInput } from '../types/api'; export const withoutIndexedFields: DecodedInput = { method_call: 'CreditSpended(uint256 _type, uint256 _quantity)', diff --git a/stubs/log.ts b/client/slices/log/stubs/log.ts similarity index 79% rename from stubs/log.ts rename to client/slices/log/stubs/log.ts index 8a011daf0c..9cd0c8e701 100644 --- a/stubs/log.ts +++ b/client/slices/log/stubs/log.ts @@ -1,9 +1,9 @@ -import type { Log } from 'types/api/log'; +import type { TransactionLog } from '../types/api'; -import { ADDRESS_PARAMS } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; -export const LOG: Log = { +export const LOG: TransactionLog = { address: ADDRESS_PARAMS, data: '0x000000000000000000000000000000000000000000000000000000d75e4be200', decoded: { diff --git a/types/api/log.ts b/client/slices/log/types/api.ts similarity index 53% rename from types/api/log.ts rename to client/slices/log/types/api.ts index cee555c03f..321c25a8d8 100644 --- a/types/api/log.ts +++ b/client/slices/log/types/api.ts @@ -1,7 +1,19 @@ -import type { AddressParam } from './addressParams'; -import type { DecodedInput } from './decodedInput'; +import type { AddressParam } from 'client/slices/address/types/api'; -export interface Log { +export interface DecodedInput { + method_call: string; + method_id: string; + parameters: Array; +} + +export interface DecodedInputParams { + name: string; + type: string; + value: string | Array | Record; + indexed?: boolean; +} + +export interface TransactionLog { address: AddressParam; topics: Array; data: string; @@ -12,7 +24,7 @@ export interface Log { } export interface LogsResponseTx { - items: Array; + items: Array; next_page_params: { index: number; items_count: number; @@ -21,7 +33,7 @@ export interface LogsResponseTx { } export interface LogsResponseAddress { - items: Array; + items: Array; next_page_params: { index: number; items_count: number; diff --git a/ui/address/tokens/AddressCollections.tsx b/client/slices/token/pages/address/AddressCollections.tsx similarity index 98% rename from ui/address/tokens/AddressCollections.tsx rename to client/slices/token/pages/address/AddressCollections.tsx index fdddf7cce8..3a73f0f1c1 100644 --- a/ui/address/tokens/AddressCollections.tsx +++ b/client/slices/token/pages/address/AddressCollections.tsx @@ -5,8 +5,9 @@ import type { NFTTokenType } from 'types/api/token'; import { route } from 'nextjs/routes'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import ActionBar from 'ui/shared/ActionBar'; diff --git a/ui/address/tokens/AddressNFTs.tsx b/client/slices/token/pages/address/AddressNFTs.tsx similarity index 97% rename from ui/address/tokens/AddressNFTs.tsx rename to client/slices/token/pages/address/AddressNFTs.tsx index 15667e9de0..8f3c110fdf 100644 --- a/ui/address/tokens/AddressNFTs.tsx +++ b/client/slices/token/pages/address/AddressNFTs.tsx @@ -3,8 +3,9 @@ import React from 'react'; import type { NFTTokenType } from 'types/api/token'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/ui/address/tokens/AddressNftDisplayTypeRadio.tsx b/client/slices/token/pages/address/AddressNftDisplayTypeRadio.tsx similarity index 100% rename from ui/address/tokens/AddressNftDisplayTypeRadio.tsx rename to client/slices/token/pages/address/AddressNftDisplayTypeRadio.tsx diff --git a/ui/address/tokens/AddressNftTypeFilter.tsx b/client/slices/token/pages/address/AddressNftTypeFilter.tsx similarity index 100% rename from ui/address/tokens/AddressNftTypeFilter.tsx rename to client/slices/token/pages/address/AddressNftTypeFilter.tsx diff --git a/ui/address/tokens/ERC20Tokens.tsx b/client/slices/token/pages/address/ERC20Tokens.tsx similarity index 93% rename from ui/address/tokens/ERC20Tokens.tsx rename to client/slices/token/pages/address/ERC20Tokens.tsx index 12545633db..d1f1ae8d6a 100644 --- a/ui/address/tokens/ERC20Tokens.tsx +++ b/client/slices/token/pages/address/ERC20Tokens.tsx @@ -1,11 +1,12 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { AddressTokenBalance } from 'types/api/address'; +import type { AddressTokenBalance } from 'client/slices/address/types/api'; import type { PaginationParams } from 'ui/shared/pagination/types'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/ui/address/tokens/ERC20TokensListItem.tsx b/client/slices/token/pages/address/ERC20TokensListItem.tsx similarity index 94% rename from ui/address/tokens/ERC20TokensListItem.tsx rename to client/slices/token/pages/address/ERC20TokensListItem.tsx index 6a97915339..9fd12328ac 100644 --- a/ui/address/tokens/ERC20TokensListItem.tsx +++ b/client/slices/token/pages/address/ERC20TokensListItem.tsx @@ -4,14 +4,16 @@ import React from 'react'; import type { AddressTokensErc20Item } from './types'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; + import config from 'configs/app'; import multichainConfig from 'configs/multichain'; import { getTokenTypeName, isConfidentialTokenType } from 'lib/token/tokenTypes'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tag } from 'toolkit/chakra/tag'; -import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; @@ -68,7 +70,7 @@ const ERC20TokensListItem = ({ truncation="constant" noIcon /> - + { token.exchange_rate !== undefined && token.exchange_rate !== null && ( diff --git a/ui/address/tokens/ERC20TokensTable.tsx b/client/slices/token/pages/address/ERC20TokensTable.tsx similarity index 100% rename from ui/address/tokens/ERC20TokensTable.tsx rename to client/slices/token/pages/address/ERC20TokensTable.tsx diff --git a/ui/address/tokens/ERC20TokensTableItem.tsx b/client/slices/token/pages/address/ERC20TokensTableItem.tsx similarity index 93% rename from ui/address/tokens/ERC20TokensTableItem.tsx rename to client/slices/token/pages/address/ERC20TokensTableItem.tsx index 386e5134d5..f28552ec61 100644 --- a/ui/address/tokens/ERC20TokensTableItem.tsx +++ b/client/slices/token/pages/address/ERC20TokensTableItem.tsx @@ -4,14 +4,16 @@ import React from 'react'; import type { AddressTokensErc20Item } from './types'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import TokenAddToWallet from 'client/features/web3-wallet/components/TokenAddToWallet'; + import config from 'configs/app'; import multichainConfig from 'configs/multichain'; import { getTokenTypeName, isConfidentialTokenType } from 'lib/token/tokenTypes'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tag } from 'toolkit/chakra/tag'; -import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import NativeTokenTag from 'ui/shared/celo/NativeTokenTag'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; import ConfidentialValue from 'ui/shared/value/ConfidentialValue'; @@ -73,7 +75,7 @@ const ERC20TokensTableItem = ({ truncation="constant" noIcon /> - + diff --git a/ui/address/tokens/NFTItem.tsx b/client/slices/token/pages/address/NFTItem.tsx similarity index 97% rename from ui/address/tokens/NFTItem.tsx rename to client/slices/token/pages/address/NFTItem.tsx index 10a86b3eff..8e4a26ae96 100644 --- a/ui/address/tokens/NFTItem.tsx +++ b/client/slices/token/pages/address/NFTItem.tsx @@ -1,7 +1,7 @@ import { Flex, Text } from '@chakra-ui/react'; import React from 'react'; -import type { AddressNFT } from 'types/api/address'; +import type { AddressNFT } from 'client/slices/address/types/api'; import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs/routes'; diff --git a/ui/address/tokens/NFTItemContainer.tsx b/client/slices/token/pages/address/NFTItemContainer.tsx similarity index 100% rename from ui/address/tokens/NFTItemContainer.tsx rename to client/slices/token/pages/address/NFTItemContainer.tsx diff --git a/ui/address/tokens/TokenBalances.tsx b/client/slices/token/pages/address/TokenBalances.tsx similarity index 92% rename from ui/address/tokens/TokenBalances.tsx rename to client/slices/token/pages/address/TokenBalances.tsx index d699c1de6a..1c36417993 100644 --- a/ui/address/tokens/TokenBalances.tsx +++ b/client/slices/token/pages/address/TokenBalances.tsx @@ -2,9 +2,11 @@ import { Flex } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; -import { currencyUnits } from 'lib/units'; import { ZERO } from 'toolkit/utils/consts'; import { thinsp } from 'toolkit/utils/htmlEntities'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; @@ -12,9 +14,9 @@ import IconSvg from 'ui/shared/IconSvg'; import NativeTokenIcon from 'ui/shared/NativeTokenIcon'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; -import { getTokensTotalInfo } from '../utils/tokenUtils'; -import useFetchTokens from '../utils/useFetchTokens'; import TokenBalancesItem from './TokenBalancesItem'; +import useFetchTokens from './useFetchTokens'; +import { getTokensTotalInfo } from './utils'; const TokenBalances = () => { const router = useRouter(); diff --git a/ui/address/tokens/TokenBalancesItem.tsx b/client/slices/token/pages/address/TokenBalancesItem.tsx similarity index 100% rename from ui/address/tokens/TokenBalancesItem.tsx rename to client/slices/token/pages/address/TokenBalancesItem.tsx diff --git a/ui/address/tokens/types.ts b/client/slices/token/pages/address/types.ts similarity index 63% rename from ui/address/tokens/types.ts rename to client/slices/token/pages/address/types.ts index 715a40b3da..63a3a349aa 100644 --- a/ui/address/tokens/types.ts +++ b/client/slices/token/pages/address/types.ts @@ -1,4 +1,4 @@ -import type { AddressTokenBalance } from 'types/api/address'; +import type { AddressTokenBalance } from 'client/slices/address/types/api'; export type AddressTokensErc20Item = Pick & { chain_values?: Record; diff --git a/ui/address/tokens/useAddressNftQuery.tsx b/client/slices/token/pages/address/useAddressNftQuery.ts similarity index 92% rename from ui/address/tokens/useAddressNftQuery.tsx rename to client/slices/token/pages/address/useAddressNftQuery.ts index 40f10286bf..e83f372298 100644 --- a/ui/address/tokens/useAddressNftQuery.tsx +++ b/client/slices/token/pages/address/useAddressNftQuery.ts @@ -3,11 +3,13 @@ import React from 'react'; import type { NFTTokenType } from 'types/api/token'; +import { ADDRESS_COLLECTION, ADDRESS_NFT_1155 } from 'client/slices/address/stubs/address'; + +import getFilterValuesFromQuery from 'client/shared/router/get-filter-values-from-query'; +import * as cookies from 'client/shared/storage/cookies'; + import { useAppContext } from 'lib/contexts/app'; -import * as cookies from 'lib/cookies'; -import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import { NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes'; -import { ADDRESS_COLLECTION, ADDRESS_NFT_1155 } from 'stubs/address'; import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/ui/address/utils/useFetchTokens.ts b/client/slices/token/pages/address/useFetchTokens.ts similarity index 94% rename from ui/address/utils/useFetchTokens.ts rename to client/slices/token/pages/address/useFetchTokens.ts index 67bdc92273..568c44c56f 100644 --- a/ui/address/utils/useFetchTokens.ts +++ b/client/slices/token/pages/address/useFetchTokens.ts @@ -1,19 +1,20 @@ import { useQueries, useQueryClient } from '@tanstack/react-query'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { AddressTokenBalance, AddressTokensBalancesSocketMessage, AddressTokensResponse } from 'types/api/address'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { AddressTokenBalance, AddressTokensBalancesSocketMessage, AddressTokensResponse } from 'client/slices/address/types/api'; import type { TokenType } from 'types/api/token'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + import config from 'configs/app'; -import useApiFetch from 'lib/api/useApiFetch'; -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import type { TokenEnhancedData } from './tokenUtils'; -import { calculateUsdValue } from './tokenUtils'; +import type { TokenEnhancedData } from './utils'; +import { calculateUsdValue } from './utils'; interface Props { hash?: string; diff --git a/ui/address/utils/tokenUtils.ts b/client/slices/token/pages/address/utils.ts similarity index 98% rename from ui/address/utils/tokenUtils.ts rename to client/slices/token/pages/address/utils.ts index 625f75f17f..33a96147d7 100644 --- a/ui/address/utils/tokenUtils.ts +++ b/client/slices/token/pages/address/utils.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; -import type { AddressTokenBalance } from 'types/api/address'; +import type { AddressTokenBalance } from 'client/slices/address/types/api'; import config from 'configs/app'; import sumBnReducer from 'lib/bigint/sumBnReducer'; diff --git a/ui/tx/TxTokenTransfer.tsx b/client/slices/tokens-transfer/pages/tx/TxTokenTransfer.tsx similarity index 86% rename from ui/tx/TxTokenTransfer.tsx rename to client/slices/tokens-transfer/pages/tx/TxTokenTransfer.tsx index 126f83f887..61a57d6b0b 100644 --- a/ui/tx/TxTokenTransfer.tsx +++ b/client/slices/tokens-transfer/pages/tx/TxTokenTransfer.tsx @@ -4,11 +4,19 @@ import React from 'react'; import type { TokenType } from 'types/api/token'; import type { TokenTransfer } from 'types/api/tokenTransfer'; +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + +import useTxCrossChainTransfersQuery from 'client/features/cross-chain-txs/hooks/useTxCrossChainTransfersQuery'; +import TxTokenTransferCrossChain from 'client/features/cross-chain-txs/pages/tx/TxTokenTransferCrossChain'; + +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { getTokenTransfersStub } from 'stubs/token'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; @@ -16,13 +24,8 @@ import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; import { getTokenFilterValue } from 'ui/tokens/utils'; -import TxPendingAlert from 'ui/tx/TxPendingAlert'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; -import TxTokenTransferCrossChain from './tokenTransfers/TxTokenTransferCrossChain'; -import TxTokenTransferLocal from './tokenTransfers/TxTokenTransferLocal'; -import useTxCrossChainTransfersQuery from './useTxCrossChainTransfersQuery'; -import type { TxQuery } from './useTxQuery'; +import TxTokenTransferLocal from './TxTokenTransferLocal'; interface Props { txQuery: TxQuery; @@ -77,6 +80,7 @@ const TxTokenTransfer = ({ txQuery, tokenTransferFilter, noCrossChain }: Props) tokenTransferFilter={ tokenTransferFilter } tokenTransferQuery={ localQuery } numActiveFilters={ typeFilter.length } + tableTop={ hasCrossChainTab ? ACTION_BAR_HEIGHT_DESKTOP : 0 } /> ), }, diff --git a/ui/tx/tokenTransfers/TxTokenTransferLocal.tsx b/client/slices/tokens-transfer/pages/tx/TxTokenTransferLocal.tsx similarity index 85% rename from ui/tx/tokenTransfers/TxTokenTransferLocal.tsx rename to client/slices/tokens-transfer/pages/tx/TxTokenTransferLocal.tsx index 229b3e2743..a3ab344746 100644 --- a/ui/tx/tokenTransfers/TxTokenTransferLocal.tsx +++ b/client/slices/tokens-transfer/pages/tx/TxTokenTransferLocal.tsx @@ -3,22 +3,23 @@ import React from 'react'; import type { TokenTransfer } from 'types/api/tokenTransfer'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList'; import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable'; -import type { TxQuery } from '../useTxQuery'; - interface Props { txQuery: TxQuery; tokenTransferQuery: QueryWithPagesResult<'general:tx_token_transfers'>; tokenTransferFilter?: (data: TokenTransfer) => boolean; numActiveFilters: number; + tableTop?: number; } -const TxTokenTransferLocal = ({ txQuery, tokenTransferQuery, tokenTransferFilter, numActiveFilters }: Props) => { +const TxTokenTransferLocal = ({ txQuery, tokenTransferQuery, tokenTransferFilter, numActiveFilters, tableTop }: Props) => { let items: Array = []; @@ -33,7 +34,7 @@ const TxTokenTransferLocal = ({ txQuery, tokenTransferQuery, tokenTransferFilter const content = tokenTransferQuery.data?.items ? ( <> - + diff --git a/ui/txs/TxAdditionalInfo.pw.tsx b/client/slices/tx/components/TxAdditionalInfo.pw.tsx similarity index 80% rename from ui/txs/TxAdditionalInfo.pw.tsx rename to client/slices/tx/components/TxAdditionalInfo.pw.tsx index 005cd3a81c..177136a03d 100644 --- a/ui/txs/TxAdditionalInfo.pw.tsx +++ b/client/slices/tx/components/TxAdditionalInfo.pw.tsx @@ -1,6 +1,9 @@ import React from 'react'; -import * as txMock from 'mocks/txs/tx'; +import * as txMock from 'client/slices/tx/mocks/tx'; + +import * as txMockBlob from 'client/features/data-availability/mocks/tx'; + import { test, expect } from 'playwright/lib'; import TxAdditionalInfo from './TxAdditionalInfo'; @@ -18,7 +21,7 @@ test('regular transaction +@mobile -@default', async({ render, page }) => { }); test('blob transaction', async({ render, page }) => { - const component = await render(); + const component = await render(); await component.getByLabel('Transaction info').click(); await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 400, height: 650 } }); }); diff --git a/ui/txs/TxAdditionalInfo.tsx b/client/slices/tx/components/TxAdditionalInfo.tsx similarity index 96% rename from ui/txs/TxAdditionalInfo.tsx rename to client/slices/tx/components/TxAdditionalInfo.tsx index 56507dee38..dbbe8d5b9a 100644 --- a/ui/txs/TxAdditionalInfo.tsx +++ b/client/slices/tx/components/TxAdditionalInfo.tsx @@ -1,7 +1,7 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import { DialogContent, DialogHeader, DialogRoot, DialogTrigger, DialogBody } from 'toolkit/chakra/dialog'; import { Heading } from 'toolkit/chakra/heading'; diff --git a/client/slices/tx/components/TxAdditionalInfoContainer.tsx b/client/slices/tx/components/TxAdditionalInfoContainer.tsx new file mode 100644 index 0000000000..6d72f99cc3 --- /dev/null +++ b/client/slices/tx/components/TxAdditionalInfoContainer.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { TX } from 'client/slices/tx/stubs/tx'; + +import DataFetchAlert from 'ui/shared/DataFetchAlert'; + +import TxAdditionalInfoContent from './TxAdditionalInfoContent'; + +interface Props { + hash: string; +} + +const TxAdditionalInfoContainer = ({ hash }: Props) => { + const { data, isError, isPlaceholderData } = useApiQuery('general:tx', { + pathParams: { hash }, + queryOptions: { + refetchOnMount: false, + placeholderData: TX, + }, + }); + + if (!data) { + return No data; + } + + if (isError) { + return ; + } + + return ; +}; + +export default React.memo(TxAdditionalInfoContainer); diff --git a/client/slices/tx/components/TxAdditionalInfoContent.tsx b/client/slices/tx/components/TxAdditionalInfoContent.tsx new file mode 100644 index 0000000000..428a3d4ba8 --- /dev/null +++ b/client/slices/tx/components/TxAdditionalInfoContent.tsx @@ -0,0 +1,192 @@ +import { Box, Flex, VStack, Separator, HStack, chakra } from '@chakra-ui/react'; +import BigNumber from 'bignumber.js'; +import React from 'react'; + +import type { Transaction } from 'client/slices/tx/types/api'; + +import { route } from 'nextjs/routes'; + +import TxFee from 'client/slices/tx/components/TxFee'; +import TxStatus from 'client/slices/tx/components/TxStatus'; + +import { currencyUnits } from 'client/shared/chain/units'; + +import config from 'configs/app'; +import { useMultichainContext } from 'lib/contexts/multichain'; +import { Link } from 'toolkit/chakra/link'; +import { Skeleton } from 'toolkit/chakra/skeleton'; +import BlobEntity from 'ui/shared/entities/blob/BlobEntity'; +import TextSeparator from 'ui/shared/TextSeparator'; +import Utilization from 'ui/shared/Utilization/Utilization'; +import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; + +const TxAdditionalInfoContent = ({ tx, isLoading }: { tx: Transaction; isLoading?: boolean }) => { + const multichainContext = useMultichainContext(); + + const sectionTitleProps = { + color: 'text.secondary', + fontWeight: 600, + marginBottom: 1, + }; + + return ( + <> + + } + gap={ 3 } + > + { tx.blob_versioned_hashes && tx.blob_versioned_hashes.length > 0 && ( + + + + Blobs: { tx.blob_versioned_hashes.length } + { tx.blob_versioned_hashes.length > 3 && ( + + view all + + ) } + + + { tx.blob_versioned_hashes.slice(0, 3).map((hash, index) => ( + + { index + 1 } + + + )) } + + + + ) } + + + + Value + + + + + { !config.UI.views.tx.hiddenFields?.tx_fee && (tx.stability_fee !== undefined || tx.fee.value !== null) && ( + + + Transaction fee + + + + ) } + + { tx.gas_used !== null && ( + + + Gas limit & usage by transaction + + + { BigNumber(tx.gas_used).toFormat() } + + { BigNumber(tx.gas_limit).toFormat() } + + + + ) } + + { !config.UI.views.tx.hiddenFields?.gas_fees && + (tx.base_fee_per_gas !== null || tx.max_fee_per_gas !== null || tx.max_priority_fee_per_gas !== null) && ( + + + Gas fees ({ currencyUnits.gwei }) + + + { tx.base_fee_per_gas !== null && ( + + Base: + + + ) } + { tx.max_fee_per_gas !== null && ( + + Max: + + + ) } + { tx.max_priority_fee_per_gas !== null && ( + + Max priority: + + + ) } + + + ) } + { !(tx.blob_versioned_hashes && tx.blob_versioned_hashes.length > 0) && ( + + + Others + + + + Txn type: + { tx.type } + { tx.type === 2 && (EIP-1559) } + + + Nonce: + { tx.nonce } + + + Position: + { tx.position } + + + + ) } + + More details + + + + ); +}; + +export default React.memo(TxAdditionalInfoContent); diff --git a/ui/shared/tx/TxFee.pw.tsx b/client/slices/tx/components/TxFee.pw.tsx similarity index 54% rename from ui/shared/tx/TxFee.pw.tsx rename to client/slices/tx/components/TxFee.pw.tsx index 1f2008506f..f64ddd600f 100644 --- a/ui/shared/tx/TxFee.pw.tsx +++ b/client/slices/tx/components/TxFee.pw.tsx @@ -1,6 +1,10 @@ import React from 'react'; -import * as txMock from 'mocks/txs/tx'; +import * as txMock from 'client/slices/tx/mocks/tx'; + +import * as txMockCelo from 'client/features/chain-variants/celo/mocks/tx'; +import * as txMockStability from 'client/features/chain-variants/stability/mocks/tx'; + import { test, expect } from 'playwright/lib'; import TxFee from './TxFee'; @@ -18,13 +22,13 @@ test('no usd value', async({ render }) => { }); test('celo gas token', async({ render, mockAssetResponse }) => { - await mockAssetResponse(txMock.celoTxn.celo?.gas_token?.icon_url as string, './playwright/mocks/image_svg.svg'); - const component = await render(); + await mockAssetResponse(txMockCelo.celoTxn.celo?.gas_token?.icon_url as string, './playwright/mocks/image_svg.svg'); + const component = await render(); await expect(component).toHaveScreenshot(); }); test('stability token', async({ render, mockAssetResponse }) => { - await mockAssetResponse(txMock.stabilityTx.stability_fee?.token.icon_url as string, './playwright/mocks/image_svg.svg'); - const component = await render(); + await mockAssetResponse(txMockStability.stabilityTx.stability_fee?.token.icon_url as string, './playwright/mocks/image_svg.svg'); + const component = await render(); await expect(component).toHaveScreenshot(); }); diff --git a/ui/shared/tx/TxFee.tsx b/client/slices/tx/components/TxFee.tsx similarity index 92% rename from ui/shared/tx/TxFee.tsx rename to client/slices/tx/components/TxFee.tsx index 8ea6c0337c..ca1ff6f9bb 100644 --- a/ui/shared/tx/TxFee.tsx +++ b/client/slices/tx/components/TxFee.tsx @@ -2,7 +2,8 @@ import type { BoxProps } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react'; import React from 'react'; -import type { Transaction, WrappedTransactionFields } from 'types/api/transaction'; +import type { WrappedTransactionFields } from 'client/features/chain-variants/suave/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; import config from 'configs/app'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/tx/TxPendingAlert.tsx b/client/slices/tx/components/TxPendingAlert.tsx similarity index 100% rename from ui/tx/TxPendingAlert.tsx rename to client/slices/tx/components/TxPendingAlert.tsx diff --git a/ui/tx/TxSocketAlert.tsx b/client/slices/tx/components/TxSocketAlert.tsx similarity index 100% rename from ui/tx/TxSocketAlert.tsx rename to client/slices/tx/components/TxSocketAlert.tsx diff --git a/ui/shared/statusTag/TxStatus.tsx b/client/slices/tx/components/TxStatus.tsx similarity index 80% rename from ui/shared/statusTag/TxStatus.tsx rename to client/slices/tx/components/TxStatus.tsx index a2571d2934..b6a80df43c 100644 --- a/ui/shared/statusTag/TxStatus.tsx +++ b/client/slices/tx/components/TxStatus.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import type { BadgeProps } from 'toolkit/chakra/badge'; - -import type { StatusTagType } from './StatusTag'; -import StatusTag from './StatusTag'; +import type { StatusTagType } from 'ui/shared/statusTag/StatusTag'; +import StatusTag from 'ui/shared/statusTag/StatusTag'; export interface Props extends BadgeProps { status: Transaction['status']; diff --git a/ui/txs/TxType.tsx b/client/slices/tx/components/TxType.tsx similarity index 96% rename from ui/txs/TxType.tsx rename to client/slices/tx/components/TxType.tsx index 03ed8fec2c..1070f71900 100644 --- a/ui/txs/TxType.tsx +++ b/client/slices/tx/components/TxType.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { TransactionType } from 'types/api/transaction'; +import type { TransactionType } from 'client/slices/tx/types/api'; import type { BadgeProps } from 'toolkit/chakra/badge'; import { Badge } from 'toolkit/chakra/badge'; diff --git a/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_dark-color-mode_regular-transaction-dark-mode-1.png b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_dark-color-mode_regular-transaction-dark-mode-1.png new file mode 100644 index 0000000000..312d04ac5e Binary files /dev/null and b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_dark-color-mode_regular-transaction-dark-mode-1.png differ diff --git a/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_default_blob-transaction-1.png b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_default_blob-transaction-1.png new file mode 100644 index 0000000000..7fec3ff91f Binary files /dev/null and b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_default_blob-transaction-1.png differ diff --git a/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_default_regular-transaction-dark-mode-1.png b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_default_regular-transaction-dark-mode-1.png new file mode 100644 index 0000000000..cf0df3ddb2 Binary files /dev/null and b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_default_regular-transaction-dark-mode-1.png differ diff --git a/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_mobile_regular-transaction-mobile---default-1.png b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_mobile_regular-transaction-mobile---default-1.png new file mode 100644 index 0000000000..ef62ceff77 Binary files /dev/null and b/client/slices/tx/components/__screenshots__/TxAdditionalInfo.pw.tsx_mobile_regular-transaction-mobile---default-1.png differ diff --git a/ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_base-view-1.png b/client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_base-view-1.png similarity index 100% rename from ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_base-view-1.png rename to client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_base-view-1.png diff --git a/ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_celo-gas-token-1.png b/client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_celo-gas-token-1.png similarity index 100% rename from ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_celo-gas-token-1.png rename to client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_celo-gas-token-1.png diff --git a/ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_no-usd-value-1.png b/client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_no-usd-value-1.png similarity index 100% rename from ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_no-usd-value-1.png rename to client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_no-usd-value-1.png diff --git a/ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_stability-token-1.png b/client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_stability-token-1.png similarity index 100% rename from ui/shared/tx/__screenshots__/TxFee.pw.tsx_default_stability-token-1.png rename to client/slices/tx/components/__screenshots__/TxFee.pw.tsx_default_stability-token-1.png diff --git a/ui/shared/entities/tx/TxEntity.pw.tsx b/client/slices/tx/components/entity/TxEntity.pw.tsx similarity index 100% rename from ui/shared/entities/tx/TxEntity.pw.tsx rename to client/slices/tx/components/entity/TxEntity.pw.tsx diff --git a/ui/shared/entities/tx/TxEntity.tsx b/client/slices/tx/components/entity/TxEntity.tsx similarity index 97% rename from ui/shared/entities/tx/TxEntity.tsx rename to client/slices/tx/components/entity/TxEntity.tsx index fc02376f08..2193650f93 100644 --- a/ui/shared/entities/tx/TxEntity.tsx +++ b/client/slices/tx/components/entity/TxEntity.tsx @@ -6,10 +6,9 @@ import { route } from 'nextjs/routes'; import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; import * as EntityBase from 'ui/shared/entities/base/components'; +import { distributeEntityProps } from 'ui/shared/entities/base/utils'; import getChainTooltipText from 'ui/shared/externalChains/getChainTooltipText'; -import { distributeEntityProps } from '../base/utils'; - type LinkProps = EntityBase.LinkBaseProps & Pick; const Link = chakra((props: LinkProps) => { diff --git a/ui/shared/entities/tx/TxEntityExternal.tsx b/client/slices/tx/components/entity/TxEntityExternal.tsx similarity index 100% rename from ui/shared/entities/tx/TxEntityExternal.tsx rename to client/slices/tx/components/entity/TxEntityExternal.tsx diff --git a/ui/shared/entities/tx/TxEntityInterchain.tsx b/client/slices/tx/components/entity/TxEntityInterchain.tsx similarity index 100% rename from ui/shared/entities/tx/TxEntityInterchain.tsx rename to client/slices/tx/components/entity/TxEntityInterchain.tsx diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_dark-color-mode_with-no-copy-dark-mode-1.png b/client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_dark-color-mode_with-no-copy-dark-mode-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_dark-color-mode_with-no-copy-dark-mode-1.png rename to client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_dark-color-mode_with-no-copy-dark-mode-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_customization-1.png b/client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_customization-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_customization-1.png rename to client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_customization-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png b/client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png rename to client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_external-link-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_loading-1.png b/client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_loading-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_loading-1.png rename to client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_loading-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_variant-content-1.png b/client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_variant-content-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_variant-content-1.png rename to client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_variant-content-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_variant-subheading-1.png b/client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_variant-subheading-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_variant-subheading-1.png rename to client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_variant-subheading-1.png diff --git a/ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_with-no-copy-dark-mode-1.png b/client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_with-no-copy-dark-mode-1.png similarity index 100% rename from ui/shared/entities/tx/__screenshots__/TxEntity.pw.tsx_default_with-no-copy-dark-mode-1.png rename to client/slices/tx/components/entity/__screenshots__/TxEntity.pw.tsx_default_with-no-copy-dark-mode-1.png diff --git a/ui/tx/useTxQuery.tsx b/client/slices/tx/hooks/useTxQuery.ts similarity index 76% rename from ui/tx/useTxQuery.tsx rename to client/slices/tx/hooks/useTxQuery.ts index e3dcbf45dc..5f937ea0ad 100644 --- a/ui/tx/useTxQuery.tsx +++ b/client/slices/tx/hooks/useTxQuery.ts @@ -3,21 +3,21 @@ import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { Transaction } from 'types/api/transaction'; - -import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -import { retry } from 'lib/api/useQueryClientConfig'; -import delay from 'lib/delay'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import { TX, TX_ZKEVM_L2 } from 'stubs/tx'; -import { SECOND } from 'toolkit/utils/consts'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; +import { retry } from 'client/api/hooks/useQueryClientConfig'; +import type { ResourceError } from 'client/api/resources'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import { TX } from 'client/slices/tx/stubs/tx'; -const rollupFeature = config.features.rollup; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import delay from 'client/shared/utils/delay'; + +import { SECOND } from 'toolkit/utils/consts'; export type TxQuery = UseQueryResult> & { socketStatus: 'close' | 'error' | undefined; @@ -43,7 +43,7 @@ export default function useTxQuery(params?: Params): TxQuery { queryOptions: { enabled: Boolean(hash) && params?.isEnabled !== false, refetchOnMount: false, - placeholderData: rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' ? TX_ZKEVM_L2 : TX, + placeholderData: TX, retry: (failureCount, error) => { if (isRefetchEnabled) { return false; diff --git a/ui/txs/socket/useTxsSocketTypeAddress.tsx b/client/slices/tx/hooks/useTxsSocketTypeAddress.ts similarity index 86% rename from ui/txs/socket/useTxsSocketTypeAddress.tsx rename to client/slices/tx/hooks/useTxsSocketTypeAddress.ts index 7bdbd201a3..40dc9923e8 100644 --- a/ui/txs/socket/useTxsSocketTypeAddress.tsx +++ b/client/slices/tx/hooks/useTxsSocketTypeAddress.ts @@ -2,20 +2,22 @@ import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { AddressFromToFilter, AddressTransactionsResponse } from 'types/api/address'; -import type { Transaction, TransactionsSortingValue } from 'types/api/transaction'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { AddressFromToFilter, AddressTransactionsResponse } from 'client/slices/address/types/api'; +import type { Transaction, TransactionsSortingValue } from 'client/slices/tx/types/api'; + +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; import config from 'configs/app'; -import { getResourceKey } from 'lib/api/useApiQuery'; import { useMultichainContext } from 'lib/contexts/multichain'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; -import { sortTxsFromSocket } from '../sortTxs'; -import { SORT_OPTIONS } from '../useTxsSort'; +import { sortTxsFromSocket } from '../utils/sort-txs'; +import { SORT_OPTIONS } from './useTxsSort'; const matchFilter = (filterValue: AddressFromToFilter, transaction: Transaction, address?: string) => { if (!filterValue) { diff --git a/ui/txs/socket/useTxsSocketTypeAll.tsx b/client/slices/tx/hooks/useTxsSocketTypeAll.ts similarity index 87% rename from ui/txs/socket/useTxsSocketTypeAll.tsx rename to client/slices/tx/hooks/useTxsSocketTypeAll.ts index 18d25fb5fd..5b4fb02491 100644 --- a/ui/txs/socket/useTxsSocketTypeAll.tsx +++ b/client/slices/tx/hooks/useTxsSocketTypeAll.ts @@ -1,12 +1,13 @@ import { useRouter } from 'next/router'; import React from 'react'; -import type { TxsSocketType } from './types'; +import type { TxsSocketType } from '../types/socket'; -import useGradualIncrement from 'lib/hooks/useGradualIncrement'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import useGradualIncrement from 'client/shared/hooks/useGradualIncrement'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; function getSocketParams(type: TxsSocketType, page: string) { diff --git a/ui/txs/useTxsSort.tsx b/client/slices/tx/hooks/useTxsSort.ts similarity index 87% rename from ui/txs/useTxsSort.tsx rename to client/slices/tx/hooks/useTxsSort.ts index 5f05f8ffc4..a0d20ec93c 100644 --- a/ui/txs/useTxsSort.tsx +++ b/client/slices/tx/hooks/useTxsSort.ts @@ -1,13 +1,15 @@ import type { UseQueryResult } from '@tanstack/react-query'; import React from 'react'; -import type { TransactionsSortingValue, TxsResponse } from 'types/api/transaction'; +import type { TransactionsSortingValue, TxsResponse } from 'client/slices/tx/types/api'; + +import type { ResourceError } from 'client/api/resources'; + +import * as cookies from 'client/shared/storage/cookies'; -import type { ResourceError } from 'lib/api/resources'; -import * as cookies from 'lib/cookies'; import type { SelectOption } from 'toolkit/chakra/select'; -import sortTxs from './sortTxs'; +import sortTxs from '../utils/sort-txs'; export const SORT_OPTIONS: Array> = [ { label: 'Default', value: 'default' }, diff --git a/mocks/txs/state.ts b/client/slices/tx/mocks/state-changes.ts similarity index 98% rename from mocks/txs/state.ts rename to client/slices/tx/mocks/state-changes.ts index dfc1c96588..8d77ef69a5 100644 --- a/mocks/txs/state.ts +++ b/client/slices/tx/mocks/state-changes.ts @@ -1,4 +1,4 @@ -import type { TxStateChange, TxStateChanges } from 'types/api/txStateChanges'; +import type { TxStateChange, TxStateChanges } from 'client/slices/tx/types/api'; export const mintToken: TxStateChange = { address: { diff --git a/mocks/txs/stats.ts b/client/slices/tx/mocks/stats.ts similarity index 75% rename from mocks/txs/stats.ts rename to client/slices/tx/mocks/stats.ts index 7b05dc975a..4dfa3c5ea0 100644 --- a/mocks/txs/stats.ts +++ b/client/slices/tx/mocks/stats.ts @@ -1,4 +1,4 @@ -import type { TransactionsStats } from 'types/api/transaction'; +import type { TransactionsStats } from 'client/slices/tx/types/api'; export const base: TransactionsStats = { pending_transactions_count: '4200', diff --git a/mocks/txs/tx.ts b/client/slices/tx/mocks/tx.ts similarity index 56% rename from mocks/txs/tx.ts rename to client/slices/tx/mocks/tx.ts index 923dbe2b40..922012cd4a 100644 --- a/mocks/txs/tx.ts +++ b/client/slices/tx/mocks/tx.ts @@ -1,15 +1,15 @@ /* eslint-disable max-len */ import type { RpcTransactionReceipt } from 'viem'; -import type { AddressParam } from 'types/api/addressParams'; -import type { Transaction } from 'types/api/transaction'; +import type { AddressParam } from 'client/slices/address/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import * as decodedInputDataMock from 'client/slices/log/mocks/decoded-input'; import * as addressMock from 'mocks/address/address'; import { publicTag, privateTag, watchlistName } from 'mocks/address/tag'; -import * as interopMock from 'mocks/interop/interop'; import { protocolTag } from 'mocks/metadata/address'; import * as tokenTransferMock from 'mocks/tokens/tokenTransfer'; -import * as decodedInputDataMock from 'mocks/txs/decodedInputData'; export const base: Transaction = { base_fee_per_gas: '10000000000', @@ -229,174 +229,6 @@ export const pending: Transaction = { value: '0', }; -export const withActionsUniswap: Transaction = { - ...base, - actions: [ - { - data: { - address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', - address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', - amount0: '7143.488560357232097378', - amount1: '10', - symbol0: 'Ring ding ding daa baa Baa aramba baa bom baa barooumba Wh-wha-what's going on-on? Ding, ding This is the Crazy Frog Ding, ding Bem', - symbol1: 'Ether', - }, - protocol: 'uniswap_v3', - type: 'mint', - }, - { - data: { - address: '0xC36442b4a4522E871399CD717aBDD847Ab11FE88', - ids: [ - '53699', - '53700123456', - '42', - ], - name: 'Uniswap V3: Positions NFT', - symbol: 'UNI-V3-POS', - to: '0x6d872Fb5F5B2B1f71fA9AadE159bc3976c1946B7', - }, - protocol: 'uniswap_v3', - type: 'mint_nft', - }, - { - data: { - address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', - address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', - amount0: '42876.488560357232', - amount1: '345.908098203434', - symbol0: 'SHAVUHA', - symbol1: 'BOB', - }, - protocol: 'uniswap_v3', - type: 'swap', - }, - { - data: { - address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', - address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', - amount0: '42', - amount1: '0.523523223232', - symbol0: 'VIC', - symbol1: 'USDT', - }, - protocol: 'uniswap_v3', - type: 'burn', - }, - { - data: { - address0: '0x6f16598F00eDabEA92B4Cef4b6aa0d45c898A9AE', - address1: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', - amount0: '42', - amount1: '0.523523223232', - symbol0: 'BOB', - symbol1: 'UNI', - }, - protocol: 'uniswap_v3', - type: 'collect', - }, - ], -}; - -export const l2tx: Transaction = { - ...base, - l1_gas_price: '82702201886', - l1_fee_scalar: '1.0', - l1_gas_used: '17060', - l1_fee: '1584574188135760', - operator_fee: '2769347953', -}; - -export const stabilityTx: Transaction = { - ...base, - stability_fee: { - dapp_address: { - hash: '0xDc2B93f3291030F3F7a6D9363ac37757f7AD5C43', - implementations: null, - is_contract: false, - is_verified: null, - name: null, - private_tags: [], - public_tags: [], - watchlist_names: [], - ens_domain_name: null, - }, - dapp_fee: '34381250000000', - token: { - address_hash: '0xDc2B93f3291030F3F7a6D9363ac37757f7AD5C43', - circulating_market_cap: null, - decimals: '18', - exchange_rate: '123.567', - holders_count: '92', - icon_url: 'https://example.com/icon.png', - name: 'Stability Gas', - symbol: 'GAS', - total_supply: '10000000000000000000000000', - type: 'ERC-20', - reputation: 'ok', - }, - total_fee: '68762500000000', - validator_address: { - hash: '0x1432997a4058acbBe562F3c1E79738c142039044', - implementations: null, - is_contract: false, - is_verified: null, - name: null, - private_tags: [], - public_tags: [], - watchlist_names: [], - ens_domain_name: null, - }, - validator_fee: '34381250000000', - }, -}; - -export const celoTxn: Transaction = { - ...base, - celo: { - gas_token: { - address_hash: '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1', - circulating_market_cap: null, - decimals: '18', - exchange_rate: '0.42', - holders_count: '205738', - icon_url: 'https://example.com/icon.png', - name: 'Celo Dollar', - symbol: 'cUSD', - total_supply: '7145754483836626799435133', - type: 'ERC-20', - reputation: 'ok', - }, - }, -}; - -export const arbitrumTxn: Transaction = { - ...base, - arbitrum: { - batch_number: 743991, - commitment_transaction: { - hash: '0x71a25e01dde129a308704de217d200ea42e0f5b8c221c8ba8b2b680ff347f708', - status: 'unfinalized', - timestamp: '2024-11-19T14:26:23.000000Z', - }, - confirmation_transaction: { - hash: null, - status: null, - timestamp: null, - }, - contains_message: null, - gas_used_for_l1: '129773', - gas_used_for_l2: '128313', - message_related_info: { - associated_l1_transaction_hash: null, - message_status: 'Relayed', - }, - network_fee: '1283130000000', - poster_fee: '1297730000000', - status: 'Sent to base', - }, -}; - export const base2 = { ...base, hash: '0x02d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193', @@ -420,20 +252,6 @@ export const base4 = { hash: '0x22d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193', }; -export const withBlob = { - ...base, - blob_gas_price: '21518435987', - blob_gas_used: '131072', - blob_versioned_hashes: [ - '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09', - '0x0197fdb17195c176b23160f335daabd4b6a231aaaadd73ec567877c66a3affd1', - ], - burnt_blob_fee: '2820464441688064', - max_fee_per_blob_gas: '60000000000', - transaction_types: [ 'blob_transaction' as const ], - type: 3, -}; - export const withRecipientName = { ...base, to: addressMock.withName, @@ -454,32 +272,6 @@ export const withRecipientContract = { to: addressMock.contract, }; -export const withInteropInMessage: Transaction = { - ...base, - op_interop_messages: [ { - init_chain: interopMock.chain, - nonce: 1, - payload: '0x', - init_transaction_hash: '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09', - sender_address_hash: addressMock.hash, - status: 'Sent', - target_address_hash: addressMock.hash, - } ], -}; - -export const withInteropOutMessage: Transaction = { - ...base, - op_interop_messages: [ { - relay_chain: interopMock.chain, - nonce: 1, - payload: '0xfa4b78b90000000000000000000000000000000000000000000000000000000005001bcfe835d1028984e9e6e7d016b77164eacbcc6cc061e9333c0b37982b504f7ea791000000000000000000000000a79b29ad7e0196c95b87f4663ded82fbf2e3add8', - relay_transaction_hash: '0x01a8c328b0370068aaaef49c107f70901cd79adcda81e3599a88855532122e09', - sender_address_hash: addressMock.hash, - status: 'Sent', - target_address_hash: addressMock.hash, - } ], -}; - export const rpcTxReceipt: RpcTransactionReceipt = { blockHash: '0xa737203aac9f38b5355c716f46b84ff1031335d1a99b2366900378c9e4c837a5', blockNumber: '0x171f82b', diff --git a/ui/pages/Transaction.tsx b/client/slices/tx/pages/details/Transaction.tsx similarity index 76% rename from ui/pages/Transaction.tsx rename to client/slices/tx/pages/details/Transaction.tsx index 2c4c2fa292..361661c815 100644 --- a/ui/pages/Transaction.tsx +++ b/client/slices/tx/pages/details/Transaction.tsx @@ -4,32 +4,36 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; import type { EntityTag as TEntityTag } from 'ui/shared/EntityTags/types'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; +import TxInternals from 'client/slices/internal-tx/pages/tx/TxInternals'; +import TxTokenTransfer from 'client/slices/tokens-transfer/pages/tx/TxTokenTransfer'; +import useTxQuery from 'client/slices/tx/hooks/useTxQuery'; + +import TxDetailsWrapped from 'client/features/chain-variants/suave/pages/tx/TxDetailsWrapped'; +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; +import TxBlobs from 'client/features/data-availability/pages/tx/TxBlobs'; +import TxFheOperations from 'client/features/fhe-operations/pages/tx/TxFheOperations'; +import TxAuthorizations from 'client/features/tx-authorization/pages/tx/TxAuthorizations'; +import TxAssetFlows from 'client/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows'; +import TxUserOps from 'client/features/user-ops/pages/tx/TxUserOps'; + +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import useEtherscanRedirects from 'client/shared/router/useEtherscanRedirects'; + import config from 'configs/app'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useEtherscanRedirects from 'lib/router/useEtherscanRedirects'; -import { publicClient } from 'lib/web3/client'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import TextAd from 'ui/shared/ad/TextAd'; import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; import EntityTags from 'ui/shared/EntityTags/EntityTags'; import PageTitle from 'ui/shared/Page/PageTitle'; -import TxAssetFlows from 'ui/tx/TxAssetFlows'; -import TxAuthorizations from 'ui/tx/TxAuthorizations'; -import TxBlobs from 'ui/tx/TxBlobs'; -import TxDetailsApi from 'ui/tx/TxDetailsApi'; -import TxDetailsRpc from 'ui/tx/TxDetailsRpc'; -import TxDetailsWrapped from 'ui/tx/TxDetailsWrapped'; -import TxFHEOperations from 'ui/tx/TxFHEOperations'; -import TxInternals from 'ui/tx/TxInternals'; -import TxLogs from 'ui/tx/TxLogs'; -import TxRawTrace from 'ui/tx/TxRawTrace'; -import TxState from 'ui/tx/TxState'; -import TxSubHeading from 'ui/tx/TxSubHeading'; -import TxTokenTransfer from 'ui/tx/TxTokenTransfer'; -import TxUserOps from 'ui/tx/TxUserOps'; -import useTxQuery from 'ui/tx/useTxQuery'; + +import TxDetailsApi from './info/TxDetailsApi'; +import TxDetailsRpc from './info/TxDetailsRpc'; +import TxLogs from './logs/TxLogs'; +import TxRawTrace from './raw-trace/TxRawTrace'; +import TxState from './state/TxState'; +import TxSubHeading from './TxSubHeading'; const txInterpretation = config.features.txInterpretation; const rollupFeature = config.features.rollup; @@ -81,7 +85,7 @@ const TransactionPageContent = () => { { id: 'state', title: 'State', component: }, { id: 'raw_trace', title: 'Raw trace', component: }, txQuery.data?.fhe_operations_count && txQuery.data.fhe_operations_count > 0 ? - { id: 'fhe_operations', title: 'FHE operations', component: } : + { id: 'fhe_operations', title: 'FHE operations', component: } : undefined, txQuery.data?.authorization_list?.length ? { id: 'authorizations', title: 'Authorizations', component: } : diff --git a/ui/tx/TxSubHeading.pw.tsx b/client/slices/tx/pages/details/TxSubHeading.pw.tsx similarity index 97% rename from ui/tx/TxSubHeading.pw.tsx rename to client/slices/tx/pages/details/TxSubHeading.pw.tsx index cd63350d89..1ca3ce491f 100644 --- a/ui/tx/TxSubHeading.pw.tsx +++ b/client/slices/tx/pages/details/TxSubHeading.pw.tsx @@ -1,18 +1,19 @@ import React from 'react'; -import type { AddressMetadataInfo, AddressMetadataTagApi } from 'types/api/addressMetadata'; +import type { AddressMetadataInfo, AddressMetadataTagApi } from 'client/features/address-metadata/types/api'; + +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import * as txMock from 'client/slices/tx/mocks/tx'; import config from 'configs/app'; import * as addressMock from 'mocks/address/address'; import { protocolTagWithMeta } from 'mocks/metadata/address'; import { transaction as novesTransaction } from 'mocks/noves/transaction'; -import * as txMock from 'mocks/txs/tx'; import { txInterpretation } from 'mocks/txs/txInterpretation'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import TxSubHeading from './TxSubHeading'; -import type { TxQuery } from './useTxQuery'; const hash = '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193'; diff --git a/ui/tx/TxSubHeading.tsx b/client/slices/tx/pages/details/TxSubHeading.tsx similarity index 92% rename from ui/tx/TxSubHeading.tsx rename to client/slices/tx/pages/details/TxSubHeading.tsx index 447bb8b8ec..755406409c 100644 --- a/ui/tx/TxSubHeading.tsx +++ b/client/slices/tx/pages/details/TxSubHeading.tsx @@ -1,10 +1,17 @@ import { Box, Flex } from '@chakra-ui/react'; import React from 'react'; -import type { AddressParam } from 'types/api/addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + +import TxInterpretation from 'client/features/tx-interpretation/common/components/TxInterpretation'; +import { createNovesSummaryObject } from 'client/features/tx-interpretation/noves/utils/createNovesSummaryObject'; import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { useMultichainContext } from 'lib/contexts/multichain'; import { NOVES_TRANSLATE } from 'stubs/noves/NovesTranslate'; import { TX_INTERPRETATION } from 'stubs/txInterpretation'; @@ -13,12 +20,7 @@ import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu' import AppActionButton from 'ui/shared/AppActionButton/AppActionButton'; import useAppActionData from 'ui/shared/AppActionButton/useAppActionData'; import { TX_ACTIONS_BLOCK_ID } from 'ui/shared/DetailedInfo/DetailedInfoActionsWrapper'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import NetworkExplorers from 'ui/shared/NetworkExplorers'; -import TxInterpretation from 'ui/shared/tx/interpretation/TxInterpretation'; - -import { createNovesSummaryObject } from './assetFlows/utils/createNovesSummaryObject'; -import type { TxQuery } from './useTxQuery'; type Props = { hash: string; diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_blockscout-provider-with-interpretation-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_noves-provider-with-interpretation-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_noves-provider-with-interpretation-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_noves-provider-with-interpretation-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_dark-color-mode_noves-provider-with-interpretation-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-has-method-called-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-has-method-called-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-has-method-called-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-has-method-called-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-pending-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-pending-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-pending-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-pending-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-with-action-button-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-with-action-button-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-with-action-button-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-no-interpretation-with-action-button-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_blockscout-provider-with-interpretation-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_no-interpretation-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_no-interpretation-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_no-interpretation-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_no-interpretation-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_noves-provider-with-interpretation-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_noves-provider-with-interpretation-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_default_noves-provider-with-interpretation-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_default_noves-provider-with-interpretation-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-action-button-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-ENS-domain-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-recipient-name-tag-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-vi-ee08a-k-and-action-button-external-link-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-and-view-all-link-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_blockscout-provider-with-interpretation-mobile-dark-mode-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_no-interpretation-mobile-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_no-interpretation-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_no-interpretation-mobile-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_no-interpretation-mobile-1.png diff --git a/ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_noves-provider-with-interpretation-mobile-dark-mode-1.png b/client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_noves-provider-with-interpretation-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/__screenshots__/TxSubHeading.pw.tsx_mobile_noves-provider-with-interpretation-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/__screenshots__/TxSubHeading.pw.tsx_mobile_noves-provider-with-interpretation-mobile-dark-mode-1.png diff --git a/ui/tx/details/TxDetails.pw.tsx b/client/slices/tx/pages/details/info/TxDetails.pw.tsx similarity index 83% rename from ui/tx/details/TxDetails.pw.tsx rename to client/slices/tx/pages/details/info/TxDetails.pw.tsx index a7445a8ace..c508c1a978 100644 --- a/ui/tx/details/TxDetails.pw.tsx +++ b/client/slices/tx/pages/details/info/TxDetails.pw.tsx @@ -1,7 +1,15 @@ import React from 'react'; +import * as txMock from 'client/slices/tx/mocks/tx'; + +import { stabilityTx } from 'client/features/chain-variants/stability/mocks/tx'; +import { withBlob } from 'client/features/data-availability/mocks/tx'; +import { withInteropInMessage, withInteropOutMessage } from 'client/features/op-interop/mocks/tx'; +import { arbitrumTxn } from 'client/features/rollup/arbitrum/mocks/tx'; +import { l2tx } from 'client/features/rollup/common/mocks/tx'; +import { withActionsUniswap } from 'client/features/tx-actions/mocks/tx'; + import * as tokenInstanceMock from 'mocks/tokens/tokenInstance'; -import * as txMock from 'mocks/txs/tx'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; @@ -70,7 +78,7 @@ test('pending', async({ render, page }) => { // NOTE: On the screenshot from the test for the mobile device, the scroll overlay is not quite right. // I checked it manually in the real device, there was not any issue with it test('with actions uniswap +@mobile +@dark-mode', async({ render, page }) => { - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot({ mask: [ page.locator(pwConfig.adsBannerSelector) ], @@ -79,7 +87,7 @@ test('with actions uniswap +@mobile +@dark-mode', async({ render, page }) => { }); test('with blob', async({ render, page }) => { - const component = await render(); + const component = await render(); await page.getByText('View details').click(); @@ -91,7 +99,7 @@ test('with blob', async({ render, page }) => { test('l2', async({ render, page, mockEnvs }) => { await mockEnvs(ENVS_MAP.optimisticRollup); - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot({ mask: [ page.locator(pwConfig.adsBannerSelector) ], maskColor: pwConfig.maskColor, @@ -102,7 +110,7 @@ test('without testnet warning', async({ render, page, mockEnvs }) => { await mockEnvs([ [ 'NEXT_PUBLIC_IS_TESTNET', 'false' ], ]); - const component = await render(); + const component = await render(); await expect(component).toHaveScreenshot({ mask: [ page.locator(pwConfig.adsBannerSelector) ], @@ -112,8 +120,8 @@ test('without testnet warning', async({ render, page, mockEnvs }) => { test('stability customization', async({ render, page, mockEnvs, mockAssetResponse }) => { await mockEnvs(ENVS_MAP.stabilityEnvs); - await mockAssetResponse(txMock.stabilityTx.stability_fee?.token.icon_url as string, './playwright/mocks/image_s.jpg'); - const component = await render(); + await mockAssetResponse(stabilityTx.stability_fee?.token.icon_url as string, './playwright/mocks/image_s.jpg'); + const component = await render(); await expect(component).toHaveScreenshot({ mask: [ page.locator(pwConfig.adsBannerSelector) ], @@ -140,7 +148,7 @@ test('with grouped fees', async({ render, page, mockEnvs }) => { test('arbitrum L1 status', async({ render, mockEnvs }) => { await mockEnvs(ENVS_MAP.arbitrumRollup); - const component = await render(); + const component = await render(); const statusElement = component.locator('div').filter({ hasText: 'Processed on rollup' }).nth(2); @@ -162,7 +170,7 @@ test('with external txs +@mobile', async({ page, render, mockEnvs, mockApiRespon test('with interop message in +@mobile', async({ render, page, mockEnvs, mockAssetResponse }) => { await mockEnvs(ENVS_MAP.interop); await mockAssetResponse('https://example.com/logo.png', './playwright/mocks/image_s.jpg'); - const component = await render(); + const component = await render(); await page.getByText('View details').first().click(); await expect(page.getByText('Interop status')).toBeVisible(); @@ -175,7 +183,7 @@ test('with interop message in +@mobile', async({ render, page, mockEnvs, mockAss test('with interop message out +@mobile', async({ page, render, mockEnvs, mockAssetResponse }) => { await mockEnvs(ENVS_MAP.interop); await mockAssetResponse('https://example.com/logo.png', './playwright/mocks/image_s.jpg'); - const component = await render(); + const component = await render(); await component.getByText('View details').first().click(); await expect(component.getByText('Interop status')).toBeVisible(); diff --git a/ui/tx/details/TxDetails.tsx b/client/slices/tx/pages/details/info/TxDetails.tsx similarity index 86% rename from ui/tx/details/TxDetails.tsx rename to client/slices/tx/pages/details/info/TxDetails.tsx index c03521a61e..7271b90a62 100644 --- a/ui/tx/details/TxDetails.tsx +++ b/client/slices/tx/pages/details/info/TxDetails.tsx @@ -9,67 +9,67 @@ import { import BigNumber from 'bignumber.js'; import React from 'react'; +import type { Transaction } from 'client/slices/tx/types/api'; import { SCROLL_L2_BLOCK_STATUSES } from 'types/api/scrollL2'; -import type { Transaction } from 'types/api/transaction'; -import { ZKEVM_L2_TX_STATUSES } from 'types/api/transaction'; import { ZKSYNC_L2_TX_BATCH_STATUSES } from 'types/api/zkSyncL2'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import TxStatus from 'client/slices/tx/components/TxStatus'; +import getConfirmationDuration from 'client/slices/tx/utils/get-confirmation-duration'; + +import TxAllowedPeekers from 'client/features/chain-variants/suave/pages/tx/TxAllowedPeekers'; +import TxDetailsTacOperation from 'client/features/chain-variants/tac/pages/tx/TxDetailsTacOperation'; +import TxDetailsCrossChainMessages from 'client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainMessages'; +import TxDetailsCrossChainTransfers from 'client/features/cross-chain-txs/pages/tx/TxDetailsCrossChainTransfers'; +import AddressEntityInterop from 'client/features/op-interop/components/AddressEntityInterop'; +import TxDetailsInterop from 'client/features/op-interop/pages/tx/TxDetailsInterop'; +import TxDetailsWithdrawalStatusArbitrum from 'client/features/rollup/arbitrum/pages/tx/TxDetailsWithdrawalStatusArbitrum'; +import BatchEntityL2 from 'client/features/rollup/common/components/BatchEntityL2'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; +import TxDetailsWithdrawalStatusOptimistic from 'client/features/rollup/optimistic/pages/tx/TxDetailsWithdrawalStatusOptimistic'; +import TxInfoScrollFees from 'client/features/rollup/scroll/pages/tx/TxInfoScrollFees'; +import TxDetailsActions from 'client/features/tx-actions/pages/tx/TxDetailsActions'; + +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import * as arbitrum from 'lib/rollups/arbitrum'; -import { formatZkEvmTxStatus, formatZkSyncL2TxnBatchStatus, layerLabels } from 'lib/rollups/utils'; -import getConfirmationDuration from 'lib/tx/getConfirmationDuration'; -import { currencyUnits } from 'lib/units'; +import { formatZkSyncL2TxnBatchStatus, layerLabels } from 'lib/rollups/utils'; import { Badge } from 'toolkit/chakra/badge'; import { CollapsibleDetails } from 'toolkit/chakra/collapsible'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; -import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoNativeCoinValue from 'ui/shared/DetailedInfo/DetailedInfoNativeCoinValue'; import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import AddressEntityInterop from 'ui/shared/entities/address/AddressEntityInterop'; -import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; -import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData'; import RawInputData from 'ui/shared/RawInputData'; import StatusTag from 'ui/shared/statusTag/StatusTag'; -import TxStatus from 'ui/shared/statusTag/TxStatus'; import TextSeparator from 'ui/shared/TextSeparator'; import Utilization from 'ui/shared/Utilization/Utilization'; import GasPriceValue from 'ui/shared/value/GasPriceValue'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps'; -import TxDetailsActions from 'ui/tx/details/txDetailsActions/TxDetailsActions'; -import TxDetailsBurntFees from 'ui/tx/details/TxDetailsBurntFees'; -import TxDetailsFeePerGas from 'ui/tx/details/TxDetailsFeePerGas'; -import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice'; -import TxDetailsOther from 'ui/tx/details/TxDetailsOther'; -import TxDetailsTokenTransfers from 'ui/tx/details/TxDetailsTokenTransfers'; -import TxDetailsWithdrawalStatusOptimistic from 'ui/tx/details/TxDetailsWithdrawalStatusOptimistic'; -import TxRevertReason from 'ui/tx/details/TxRevertReason'; -import TxAllowedPeekers from 'ui/tx/TxAllowedPeekers'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; import ZkSyncL2TxnBatchHashesInfo from 'ui/txnBatches/zkSyncL2/ZkSyncL2TxnBatchHashesInfo'; -import TxDetailsCrossChainMessages from './TxDetailsCrossChainMessages'; -import TxDetailsCrossChainTransfers from './TxDetailsCrossChainTransfers'; -import TxDetailsGasUsage from './TxDetailsGasUsage'; -import TxDetailsInterop from './TxDetailsInterop'; -import TxDetailsSetMaxGasLimit from './TxDetailsSetMaxGasLimit'; -import TxDetailsTacOperation from './TxDetailsTacOperation'; -import TxDetailsTxFee from './TxDetailsTxFee'; -import TxDetailsWithdrawalStatusArbitrum from './TxDetailsWithdrawalStatusArbitrum'; -import TxHash from './TxHash'; -import TxInfoScrollFees from './TxInfoScrollFees'; +import TxDetailsBurntFees from './parts/TxDetailsBurntFees'; +import TxDetailsFeePerGas from './parts/TxDetailsFeePerGas'; +import TxDetailsGasPrice from './parts/TxDetailsGasPrice'; +import TxDetailsGasUsage from './parts/TxDetailsGasUsage'; +import TxDetailsOther from './parts/TxDetailsOther'; +import TxDetailsSetMaxGasLimit from './parts/TxDetailsSetMaxGasLimit'; +import TxDetailsTokenTransfers from './parts/TxDetailsTokenTransfers'; +import TxDetailsTxFee from './parts/TxDetailsTxFee'; +import TxHash from './parts/TxHash'; +import TxRevertReason from './parts/TxRevertReason'; interface Props { data: Transaction | undefined; @@ -80,6 +80,7 @@ interface Props { const rollupFeature = config.features.rollup; +// REFACTOR: Put feature related parts under the feature folder const TxDetails = ({ data, isLoading, socketStatus, noTxActions }: Props) => { const [ isExpanded, setIsExpanded ] = React.useState(false); @@ -159,7 +160,7 @@ const TxDetails = ({ data, isLoading, socketStatus, noTxActions }: Props) => { > { rollupFeature.isEnabled && - (rollupFeature.type === 'zkEvm' || rollupFeature.type === 'zkSync' || rollupFeature.type === 'arbitrum' || rollupFeature.type === 'scroll') ? + (rollupFeature.type === 'zkSync' || rollupFeature.type === 'arbitrum' || rollupFeature.type === 'scroll') ? `${ layerLabels.current } status and method` : 'Status and method' } @@ -204,24 +205,6 @@ const TxDetails = ({ data, isLoading, socketStatus, noTxActions }: Props) => { ) } - { data.zkevm_status && !config.UI.views.tx.hiddenFields?.L1_status && ( - <> - - Confirmation status - - - - - - ) } - { data.arbitrum?.status && !config.UI.views.tx.hiddenFields?.L1_status && ( <> { ) } - { data.zkevm_batch_number && !config.UI.views.tx.hiddenFields?.batch && ( - <> - - Txn batch - - - - - - ) } - { data.zksync && !config.UI.views.tx.hiddenFields?.batch && ( <> { ) } - { data.zkevm_sequence_hash && ( - <> - - Sequence tx hash - - - - - - - - - - ) } - - { data.zkevm_verify_hash && ( - <> - - Verify tx hash - - - - - - - - - ) } - - { (data.zkevm_batch_number || data.zkevm_verify_hash) && } - { !config.UI.views.tx.hiddenFields?.value && ( <> { hint={ ` Base Fee refers to the network Base Fee at the time of the block, while Max Fee & Max Priority Fee refer to the max amount a user is willing to pay - for their tx & to give to the ${ getNetworkValidatorTitle() } respectively + for their tx & to give to the ${ getChainValidatorTitle() } respectively ` } isLoading={ isLoading } > diff --git a/ui/tx/TxDetailsApi.tsx b/client/slices/tx/pages/details/info/TxDetailsApi.tsx similarity index 80% rename from ui/tx/TxDetailsApi.tsx rename to client/slices/tx/pages/details/info/TxDetailsApi.tsx index 35b9b7ba8c..176c25d776 100644 --- a/ui/tx/TxDetailsApi.tsx +++ b/client/slices/tx/pages/details/info/TxDetailsApi.tsx @@ -1,12 +1,13 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; +import BlockPendingUpdateAlert from 'client/slices/block/components/BlockPendingUpdateAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; + import TestnetWarning from 'ui/shared/alerts/TestnetWarning'; -import BlockPendingUpdateAlert from 'ui/shared/block/BlockPendingUpdateAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; -import TxDetails from './details/TxDetails'; -import type { TxQuery } from './useTxQuery'; +import TxDetails from './TxDetails'; interface Props { txQuery: TxQuery; diff --git a/ui/tx/TxDetailsRpc.tsx b/client/slices/tx/pages/details/info/TxDetailsRpc.tsx similarity index 87% rename from ui/tx/TxDetailsRpc.tsx rename to client/slices/tx/pages/details/info/TxDetailsRpc.tsx index 98880c9a79..9e3920413a 100644 --- a/ui/tx/TxDetailsRpc.tsx +++ b/client/slices/tx/pages/details/info/TxDetailsRpc.tsx @@ -3,11 +3,15 @@ import { useQuery } from '@tanstack/react-query'; import React from 'react'; import type { Chain, GetBlockReturnType, GetTransactionReturnType, TransactionReceipt } from 'viem'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import formatRpcData from 'client/slices/tx/utils/format-rpc-data'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import { publicClient } from 'lib/web3/client'; -import formatTxData from 'lib/web3/rpc/formatTxData'; import { GET_BLOCK, GET_TRANSACTION, GET_TRANSACTION_RECEIPT, GET_TRANSACTION_CONFIRMATIONS } from 'stubs/RPC'; import { SECOND } from 'toolkit/utils/consts'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; @@ -15,8 +19,7 @@ import TestnetWarning from 'ui/shared/alerts/TestnetWarning'; import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; -import TxDetails from './details/TxDetails'; -import type { TxQuery } from './useTxQuery'; +import TxDetails from './TxDetails'; type RpcResponseType = [ GetTransactionReturnType, @@ -62,7 +65,7 @@ const TxDetailsRpc = ({ hash, txQuery }: Props) => { select: (response) => { const [ tx, txReceipt, txConfirmations, block ] = response; - return formatTxData(tx, txReceipt, txConfirmations, block); + return formatRpcData(tx, txReceipt, txConfirmations, block); }, placeholderData: [ GET_TRANSACTION, diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_dark-color-mode_between-addresses-mobile-dark-mode-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_dark-color-mode_between-addresses-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_dark-color-mode_between-addresses-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_dark-color-mode_between-addresses-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_dark-color-mode_with-actions-uniswap-mobile-dark-mode-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_dark-color-mode_with-actions-uniswap-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_dark-color-mode_with-actions-uniswap-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_dark-color-mode_with-actions-uniswap-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_arbitrum-L1-status-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_arbitrum-L1-status-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_arbitrum-L1-status-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_arbitrum-L1-status-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_between-addresses-mobile-dark-mode-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_between-addresses-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_between-addresses-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_between-addresses-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_creating-contact-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_creating-contact-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_creating-contact-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_creating-contact-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_l2-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_l2-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_l2-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_l2-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_pending-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_pending-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_pending-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_pending-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_stability-customization-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_stability-customization-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_stability-customization-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_stability-customization-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-actions-uniswap-mobile-dark-mode-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-actions-uniswap-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-actions-uniswap-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-actions-uniswap-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-blob-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-blob-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-blob-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-blob-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-decoded-raw-reason-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-decoded-raw-reason-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-decoded-raw-reason-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-decoded-raw-reason-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-decoded-revert-reason-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-decoded-revert-reason-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-decoded-revert-reason-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-decoded-revert-reason-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-external-txs-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-external-txs-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-external-txs-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-external-txs-mobile-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-grouped-fees-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-grouped-fees-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-grouped-fees-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-grouped-fees-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-in-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-in-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-in-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-in-mobile-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-out-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-out-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-out-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-interop-message-out-mobile-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-token-transfer-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-token-transfer-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_with-token-transfer-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_with-token-transfer-mobile-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_without-testnet-warning-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_without-testnet-warning-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_default_without-testnet-warning-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_default_without-testnet-warning-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_between-addresses-mobile-dark-mode-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_between-addresses-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_between-addresses-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_between-addresses-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-actions-uniswap-mobile-dark-mode-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-actions-uniswap-mobile-dark-mode-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-actions-uniswap-mobile-dark-mode-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-actions-uniswap-mobile-dark-mode-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-external-txs-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-external-txs-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-external-txs-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-external-txs-mobile-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-in-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-in-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-in-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-in-mobile-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-out-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-out-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-out-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-interop-message-out-mobile-1.png diff --git a/ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-token-transfer-mobile-1.png b/client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-token-transfer-mobile-1.png similarity index 100% rename from ui/tx/details/__screenshots__/TxDetails.pw.tsx_mobile_with-token-transfer-mobile-1.png rename to client/slices/tx/pages/details/info/__screenshots__/TxDetails.pw.tsx_mobile_with-token-transfer-mobile-1.png diff --git a/ui/tx/details/TxDetailsBurntFees.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsBurntFees.tsx similarity index 93% rename from ui/tx/details/TxDetailsBurntFees.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsBurntFees.tsx index 1172af1230..a81160f267 100644 --- a/ui/tx/details/TxDetailsBurntFees.tsx +++ b/client/slices/tx/pages/details/info/parts/TxDetailsBurntFees.tsx @@ -1,10 +1,11 @@ import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import { currencyUnits } from 'client/shared/chain/units'; import config from 'configs/app'; -import { currencyUnits } from 'lib/units'; import { ZERO } from 'toolkit/utils/consts'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoNativeCoinValue from 'ui/shared/DetailedInfo/DetailedInfoNativeCoinValue'; diff --git a/ui/tx/details/TxDetailsFeePerGas.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsFeePerGas.tsx similarity index 100% rename from ui/tx/details/TxDetailsFeePerGas.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsFeePerGas.tsx diff --git a/ui/tx/details/TxDetailsGasPrice.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsGasPrice.tsx similarity index 100% rename from ui/tx/details/TxDetailsGasPrice.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsGasPrice.tsx diff --git a/ui/tx/details/TxDetailsGasUsage.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsGasUsage.tsx similarity index 94% rename from ui/tx/details/TxDetailsGasUsage.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsGasUsage.tsx index acf8f2e2db..6b80e70696 100644 --- a/ui/tx/details/TxDetailsGasUsage.tsx +++ b/client/slices/tx/pages/details/info/parts/TxDetailsGasUsage.tsx @@ -1,7 +1,7 @@ import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import { Skeleton } from 'toolkit/chakra/skeleton'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; diff --git a/ui/tx/details/TxDetailsOther.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsOther.tsx similarity index 96% rename from ui/tx/details/TxDetailsOther.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsOther.tsx index 2072d221d0..0fd31edafb 100644 --- a/ui/tx/details/TxDetailsOther.tsx +++ b/client/slices/tx/pages/details/info/parts/TxDetailsOther.tsx @@ -1,7 +1,7 @@ import { Box, Text } from '@chakra-ui/react'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/ui/tx/details/TxDetailsSetMaxGasLimit.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsSetMaxGasLimit.tsx similarity index 95% rename from ui/tx/details/TxDetailsSetMaxGasLimit.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsSetMaxGasLimit.tsx index 9f904bd256..ced8e53708 100644 --- a/ui/tx/details/TxDetailsSetMaxGasLimit.tsx +++ b/client/slices/tx/pages/details/info/parts/TxDetailsSetMaxGasLimit.tsx @@ -1,7 +1,7 @@ import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import config from 'configs/app'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; diff --git a/ui/tx/details/TxDetailsTokenTransfers.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsTokenTransfers.tsx similarity index 88% rename from ui/tx/details/TxDetailsTokenTransfers.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsTokenTransfers.tsx index 238d6e80d0..55375b127e 100644 --- a/ui/tx/details/TxDetailsTokenTransfers.tsx +++ b/client/slices/tx/pages/details/info/parts/TxDetailsTokenTransfers.tsx @@ -3,12 +3,14 @@ import React from 'react'; import type { TokenTransfer } from 'types/api/tokenTransfer'; -import { route } from 'nextjs-routes'; +import { route } from 'nextjs/routes'; +import { useMultichainContext } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import IconSvg from 'ui/shared/IconSvg'; import TokenTransferSnippet from 'ui/shared/TokenTransferSnippet/TokenTransferSnippet'; + interface Props { data: Array; txHash: string; @@ -23,7 +25,11 @@ const TOKEN_TRANSFERS_TYPES = [ ]; const TxDetailsTokenTransfers = ({ data, txHash, isOverflow }: Props) => { - const viewAllUrl = route({ pathname: '/tx/[hash]', query: { hash: txHash, tab: 'token_transfers' } }); + const multichainContext = useMultichainContext(); + const viewAllUrl = route({ + pathname: '/tx/[hash]', + query: { hash: txHash, tab: 'token_transfers' }, + }, { chain: multichainContext?.chain }); const transferGroups = TOKEN_TRANSFERS_TYPES.map((group) => ({ ...group, diff --git a/ui/tx/details/TxDetailsTxFee.tsx b/client/slices/tx/pages/details/info/parts/TxDetailsTxFee.tsx similarity index 96% rename from ui/tx/details/TxDetailsTxFee.tsx rename to client/slices/tx/pages/details/info/parts/TxDetailsTxFee.tsx index bc03c79126..9d3ed9d1e9 100644 --- a/ui/tx/details/TxDetailsTxFee.tsx +++ b/client/slices/tx/pages/details/info/parts/TxDetailsTxFee.tsx @@ -1,12 +1,13 @@ import BigNumber from 'bignumber.js'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import TxFee from 'client/slices/tx/components/TxFee'; import config from 'configs/app'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import * as DetailedInfoItemBreakdown from 'ui/shared/DetailedInfo/DetailedInfoItemBreakdown'; -import TxFee from 'ui/shared/tx/TxFee'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; interface Props { diff --git a/ui/tx/details/TxHash.tsx b/client/slices/tx/pages/details/info/parts/TxHash.tsx similarity index 90% rename from ui/tx/details/TxHash.tsx rename to client/slices/tx/pages/details/info/parts/TxHash.tsx index 17cce0a07a..643aa9cd02 100644 --- a/ui/tx/details/TxHash.tsx +++ b/client/slices/tx/pages/details/info/parts/TxHash.tsx @@ -1,19 +1,21 @@ import { Box, Flex, Spinner } from '@chakra-ui/react'; import React from 'react'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import TxExternalTxs from 'client/features/external-txs/components/TxExternalTxs'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { Skeleton } from 'toolkit/chakra/skeleton'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import TextSeparator from 'ui/shared/TextSeparator'; -import TxExternalTxs from '../TxExternalTxs'; - const externalTxFeature = config.features.externalTxs; interface Props { diff --git a/ui/tx/details/TxRevertReason.tsx b/client/slices/tx/pages/details/info/parts/TxRevertReason.tsx similarity index 89% rename from ui/tx/details/TxRevertReason.tsx rename to client/slices/tx/pages/details/info/parts/TxRevertReason.tsx index d472e0b569..f4c69d239d 100644 --- a/ui/tx/details/TxRevertReason.tsx +++ b/client/slices/tx/pages/details/info/parts/TxRevertReason.tsx @@ -1,9 +1,10 @@ import { Grid, GridItem, Text } from '@chakra-ui/react'; import React from 'react'; -import type { TransactionRevertReason } from 'types/api/transaction'; +import type { TransactionRevertReason } from 'client/slices/tx/types/api'; + +import hexToUtf8 from 'client/shared/transformers/hex-to-utf8'; -import hexToUtf8 from 'lib/hexToUtf8'; import { HEX_REGEXP } from 'toolkit/utils/regexp'; import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData'; diff --git a/ui/tx/TxLogs.tsx b/client/slices/tx/pages/details/logs/TxLogs.tsx similarity index 82% rename from ui/tx/TxLogs.tsx rename to client/slices/tx/pages/details/logs/TxLogs.tsx index a57dcbf1c2..f0b3bf561d 100644 --- a/ui/tx/TxLogs.tsx +++ b/client/slices/tx/pages/details/logs/TxLogs.tsx @@ -1,23 +1,23 @@ import { Box, Text } from '@chakra-ui/react'; import React from 'react'; -import type { Log } from 'types/api/log'; +import type { TransactionLog } from 'client/slices/log/types/api'; + +import { LOG } from 'client/slices/log/stubs/log'; +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; -import { LOG } from 'stubs/log'; import { generateListStub } from 'stubs/utils'; import ActionBar from 'ui/shared/ActionBar'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import LogItem from 'ui/shared/logs/LogItem'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import TxPendingAlert from 'ui/tx/TxPendingAlert'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; - -import type { TxQuery } from './useTxQuery'; interface Props { txQuery: TxQuery; - logsFilter?: (log: Log) => boolean; + logsFilter?: (log: TransactionLog) => boolean; } const TxLogs = ({ txQuery, logsFilter }: Props) => { @@ -38,7 +38,7 @@ const TxLogs = ({ txQuery, logsFilter }: Props) => { return ; } - let items: Array = []; + let items: Array = []; if (data?.items) { if (isPlaceholderData) { diff --git a/ui/tx/TxRawTrace.tsx b/client/slices/tx/pages/details/raw-trace/TxRawTrace.tsx similarity index 71% rename from ui/tx/TxRawTrace.tsx rename to client/slices/tx/pages/details/raw-trace/TxRawTrace.tsx index 6a64c037de..b6e57f531f 100644 --- a/ui/tx/TxRawTrace.tsx +++ b/client/slices/tx/pages/details/raw-trace/TxRawTrace.tsx @@ -1,20 +1,22 @@ import { useRouter } from 'next/router'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { RawTracesResponse } from 'types/api/rawTrace'; - -import useApiQuery from 'lib/api/useApiQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import { TX_RAW_TRACE } from 'stubs/tx'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { TxRawTracesResponse } from 'client/slices/tx/types/api'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import { TX_RAW_TRACE } from 'client/slices/tx/stubs/tx'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import DataFetchAlert from 'ui/shared/DataFetchAlert'; import RawDataSnippet from 'ui/shared/RawDataSnippet'; -import TxPendingAlert from 'ui/tx/TxPendingAlert'; -import TxSocketAlert from 'ui/tx/TxSocketAlert'; - -import type { TxQuery } from './useTxQuery'; interface Props { txQuery: TxQuery; @@ -22,7 +24,7 @@ interface Props { const TxRawTrace = ({ txQuery }: Props) => { const [ isQueryEnabled, setIsQueryEnabled ] = React.useState(false); - const [ rawTraces, setRawTraces ] = React.useState(); + const [ rawTraces, setRawTraces ] = React.useState(); const router = useRouter(); const hash = getQueryParamString(router.query.hash); diff --git a/ui/tx/TxState.pw.tsx b/client/slices/tx/pages/details/state/TxState.pw.tsx similarity index 60% rename from ui/tx/TxState.pw.tsx rename to client/slices/tx/pages/details/state/TxState.pw.tsx index 4136ea257f..1d3ac8c9a6 100644 --- a/ui/tx/TxState.pw.tsx +++ b/client/slices/tx/pages/details/state/TxState.pw.tsx @@ -1,11 +1,12 @@ import React from 'react'; -import * as txStateMock from 'mocks/txs/state'; -import * as txMock from 'mocks/txs/tx'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import * as txStateChangesMock from 'client/slices/tx/mocks/state-changes'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import { test, expect } from 'playwright/lib'; import TxState from './TxState'; -import type { TxQuery } from './useTxQuery'; const hooksConfig = { router: { @@ -14,7 +15,7 @@ const hooksConfig = { }; test('base view +@mobile', async({ render, mockApiResponse }) => { - await mockApiResponse('general:tx_state_changes', txStateMock.baseResponse, { pathParams: { hash: txMock.base.hash } }); + await mockApiResponse('general:tx_state_changes', txStateChangesMock.baseResponse, { pathParams: { hash: txMock.base.hash } }); const txQuery = { data: txMock.base, isPlaceholderData: false, diff --git a/ui/tx/TxState.tsx b/client/slices/tx/pages/details/state/TxState.tsx similarity index 85% rename from ui/tx/TxState.tsx rename to client/slices/tx/pages/details/state/TxState.tsx index 6c51d84ec8..1a42756cf5 100644 --- a/ui/tx/TxState.tsx +++ b/client/slices/tx/pages/details/state/TxState.tsx @@ -1,17 +1,18 @@ import { Box, Text } from '@chakra-ui/react'; import React from 'react'; -import { TX_STATE_CHANGES } from 'stubs/txStateChanges'; +import TxPendingAlert from 'client/slices/tx/components/TxPendingAlert'; +import TxSocketAlert from 'client/slices/tx/components/TxSocketAlert'; +import type { TxQuery } from 'client/slices/tx/hooks/useTxQuery'; +import { TX_STATE_CHANGES } from 'client/slices/tx/stubs/state-changes'; + import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; -import TxStateList from 'ui/tx/state/TxStateList'; -import TxStateTable from 'ui/tx/state/TxStateTable'; -import TxPendingAlert from './TxPendingAlert'; -import TxSocketAlert from './TxSocketAlert'; -import type { TxQuery } from './useTxQuery'; +import TxStateList from './TxStateList'; +import TxStateTable from './TxStateTable'; interface Props { txQuery: TxQuery; diff --git a/ui/tx/state/TxStateList.tsx b/client/slices/tx/pages/details/state/TxStateList.tsx similarity index 75% rename from ui/tx/state/TxStateList.tsx rename to client/slices/tx/pages/details/state/TxStateList.tsx index 9c3cabfec8..d6bc667ab0 100644 --- a/ui/tx/state/TxStateList.tsx +++ b/client/slices/tx/pages/details/state/TxStateList.tsx @@ -1,9 +1,9 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { TxStateChange } from 'types/api/txStateChanges'; +import type { TxStateChange } from 'client/slices/tx/types/api'; -import TxStateListItem from 'ui/tx/state/TxStateListItem'; +import TxStateListItem from './TxStateListItem'; interface Props { data: Array; diff --git a/ui/tx/state/TxStateListItem.tsx b/client/slices/tx/pages/details/state/TxStateListItem.tsx similarity index 92% rename from ui/tx/state/TxStateListItem.tsx rename to client/slices/tx/pages/details/state/TxStateListItem.tsx index 2dbc3c5e23..b97c4bf70d 100644 --- a/ui/tx/state/TxStateListItem.tsx +++ b/client/slices/tx/pages/details/state/TxStateListItem.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import type { TxStateChange } from 'types/api/txStateChanges'; +import type { TxStateChange } from 'client/slices/tx/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import { getStateElements } from './utils'; diff --git a/ui/tx/state/TxStateTable.tsx b/client/slices/tx/pages/details/state/TxStateTable.tsx similarity index 85% rename from ui/tx/state/TxStateTable.tsx rename to client/slices/tx/pages/details/state/TxStateTable.tsx index 6b3d5cf93b..3e61cc453c 100644 --- a/ui/tx/state/TxStateTable.tsx +++ b/client/slices/tx/pages/details/state/TxStateTable.tsx @@ -1,10 +1,12 @@ import React from 'react'; -import type { TxStateChange } from 'types/api/txStateChanges'; +import type { TxStateChange } from 'client/slices/tx/types/api'; + +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; -import TxStateTableItem from 'ui/tx/state/TxStateTableItem'; + +import TxStateTableItem from './TxStateTableItem'; interface Props { data: Array; diff --git a/ui/tx/state/TxStateTableItem.tsx b/client/slices/tx/pages/details/state/TxStateTableItem.tsx similarity index 87% rename from ui/tx/state/TxStateTableItem.tsx rename to client/slices/tx/pages/details/state/TxStateTableItem.tsx index a686601940..d2219d0b10 100644 --- a/ui/tx/state/TxStateTableItem.tsx +++ b/client/slices/tx/pages/details/state/TxStateTableItem.tsx @@ -1,10 +1,11 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { TxStateChange } from 'types/api/txStateChanges'; +import type { TxStateChange } from 'client/slices/tx/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import { getStateElements } from './utils'; diff --git a/ui/tx/state/TxStateTokenIdList.tsx b/client/slices/tx/pages/details/state/TxStateTokenIdList.tsx similarity index 100% rename from ui/tx/state/TxStateTokenIdList.tsx rename to client/slices/tx/pages/details/state/TxStateTokenIdList.tsx diff --git a/ui/tx/__screenshots__/TxState.pw.tsx_default_base-view-mobile-1.png b/client/slices/tx/pages/details/state/__screenshots__/TxState.pw.tsx_default_base-view-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxState.pw.tsx_default_base-view-mobile-1.png rename to client/slices/tx/pages/details/state/__screenshots__/TxState.pw.tsx_default_base-view-mobile-1.png diff --git a/ui/tx/__screenshots__/TxState.pw.tsx_mobile_base-view-mobile-1.png b/client/slices/tx/pages/details/state/__screenshots__/TxState.pw.tsx_mobile_base-view-mobile-1.png similarity index 100% rename from ui/tx/__screenshots__/TxState.pw.tsx_mobile_base-view-mobile-1.png rename to client/slices/tx/pages/details/state/__screenshots__/TxState.pw.tsx_mobile_base-view-mobile-1.png diff --git a/ui/tx/state/utils.tsx b/client/slices/tx/pages/details/state/utils.tsx similarity index 95% rename from ui/tx/state/utils.tsx rename to client/slices/tx/pages/details/state/utils.tsx index 987441fb00..7b9c7ee064 100644 --- a/ui/tx/state/utils.tsx +++ b/client/slices/tx/pages/details/state/utils.tsx @@ -2,11 +2,12 @@ import { Flex } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; -import type { TxStateChange } from 'types/api/txStateChanges'; +import type { TxStateChange } from 'client/slices/tx/types/api'; + +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import { currencyUnits } from 'client/shared/chain/units'; import config from 'configs/app'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; -import { currencyUnits } from 'lib/units'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; @@ -23,7 +24,7 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) { return ( - { getNetworkValidatorTitle() } + { getChainValidatorTitle() } ); diff --git a/ui/pages/Transactions.tsx b/client/slices/tx/pages/index/TxIndex.tsx similarity index 82% rename from ui/pages/Transactions.tsx rename to client/slices/tx/pages/index/TxIndex.tsx index 54d8edf0e7..1e190bfe3b 100644 --- a/ui/pages/Transactions.tsx +++ b/client/slices/tx/pages/index/TxIndex.tsx @@ -1,10 +1,12 @@ import React from 'react'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; import PageTitle from 'ui/shared/Page/PageTitle'; -import TxsStats from 'ui/txs/TxsStats'; -import TxsTabs from 'ui/txs/TxsTabs'; + +import TxsTabs from './list/TxsTabs'; +import TxsStats from './stats/TxsStats'; const TAB_LIST_PROPS = { marginBottom: 0, diff --git a/ui/txs/TxsContent.tsx b/client/slices/tx/pages/index/list/TxsContent.tsx similarity index 83% rename from ui/txs/TxsContent.tsx rename to client/slices/tx/pages/index/list/TxsContent.tsx index d8c843de5d..ae5baaa17c 100644 --- a/ui/txs/TxsContent.tsx +++ b/client/slices/tx/pages/index/list/TxsContent.tsx @@ -1,20 +1,22 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { TxsSocketType } from './socket/types'; -import type { AddressFromToFilter } from 'types/api/address'; -import type { Transaction, TransactionsSortingField, TransactionsSortingValue } from 'types/api/transaction'; +import type { AddressFromToFilter } from 'client/slices/address/types/api'; +import type { Transaction, TransactionsSortingField, TransactionsSortingValue } from 'client/slices/tx/types/api'; +import type { TxsSocketType } from 'client/slices/tx/types/socket'; import type { PaginationParams } from 'ui/shared/pagination/types'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useTableViewValue from 'lib/hooks/useTableViewValue'; -import AddressCsvExportLink from 'ui/address/AddressCsvExportLink'; +import CsvExport from 'client/features/csv-export/components/CsvExport'; +import useDescribeTxs from 'client/features/tx-interpretation/noves/hooks/useDescribeTxs'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useTableViewValue from 'client/shared/hooks/useTableViewValue'; + import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import getNextSortValue from 'ui/shared/sort/getNextSortValue'; import TableViewToggleButton from 'ui/shared/TableViewToggleButton'; -import useDescribeTxs from './noves/useDescribeTxs'; import TxsHeaderMobile from './TxsHeaderMobile'; import TxsList from './TxsList'; import TxsTable from './TxsTable'; @@ -127,10 +129,15 @@ const TxsContent = ({ showPagination={ pagination.isVisible } filterComponent={ filter } linkSlot={ currentAddress ? ( - ) : null } tableViewButton={ tableViewButton } diff --git a/ui/txs/TxsHeaderMobile.tsx b/client/slices/tx/pages/index/list/TxsHeaderMobile.tsx similarity index 93% rename from ui/txs/TxsHeaderMobile.tsx rename to client/slices/tx/pages/index/list/TxsHeaderMobile.tsx index 376a66e778..425fbc1f7c 100644 --- a/ui/txs/TxsHeaderMobile.tsx +++ b/client/slices/tx/pages/index/list/TxsHeaderMobile.tsx @@ -1,17 +1,17 @@ import { HStack, chakra, createListCollection } from '@chakra-ui/react'; import React from 'react'; -import type { TransactionsSortingValue } from 'types/api/transaction'; +import type { TransactionsSortingValue } from 'client/slices/tx/types/api'; import type { PaginationParams } from 'ui/shared/pagination/types'; // import { FilterInput } from 'toolkit/components/filters/FilterInput'; +import { SORT_OPTIONS } from 'client/slices/tx/hooks/useTxsSort'; + import ActionBar from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; import Sort from 'ui/shared/sort/Sort'; -import { SORT_OPTIONS } from './useTxsSort'; - type Props = { sorting: TransactionsSortingValue; setSorting?: (val: TransactionsSortingValue) => void; diff --git a/ui/txs/TxsList.tsx b/client/slices/tx/pages/index/list/TxsList.tsx similarity index 82% rename from ui/txs/TxsList.tsx rename to client/slices/tx/pages/index/list/TxsList.tsx index acde688fe8..925ad5fb73 100644 --- a/ui/txs/TxsList.tsx +++ b/client/slices/tx/pages/index/list/TxsList.tsx @@ -1,14 +1,16 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { TxsSocketType } from './socket/types'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; +import type { TxsSocketType } from 'client/slices/tx/types/socket'; + +import type { TxsTranslationQuery } from 'client/features/tx-interpretation/noves/hooks/useDescribeTxs'; + +import useInitialList from 'client/shared/lists/useInitialList'; +import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useInitialList from 'lib/hooks/useInitialList'; -import useLazyRenderedList from 'lib/hooks/useLazyRenderedList'; -import type { TxsTranslationQuery } from './noves/useDescribeTxs'; import TxsSocketNotice from './socket/TxsSocketNotice'; import TxsListItem from './TxsListItem'; diff --git a/ui/txs/TxsListItem.pw.tsx b/client/slices/tx/pages/index/list/TxsListItem.pw.tsx similarity index 93% rename from ui/txs/TxsListItem.pw.tsx rename to client/slices/tx/pages/index/list/TxsListItem.pw.tsx index 49596bf540..a14059b8db 100644 --- a/ui/txs/TxsListItem.pw.tsx +++ b/client/slices/tx/pages/index/list/TxsListItem.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as txMock from 'mocks/txs/tx'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import { test, expect, devices } from 'playwright/lib'; import TxsListItem from './TxsListItem'; diff --git a/ui/txs/TxsListItem.tsx b/client/slices/tx/pages/index/list/TxsListItem.tsx similarity index 86% rename from ui/txs/TxsListItem.tsx rename to client/slices/tx/pages/index/list/TxsListItem.tsx index 4d8b643356..7d11513a2f 100644 --- a/ui/txs/TxsListItem.tsx +++ b/client/slices/tx/pages/index/list/TxsListItem.tsx @@ -4,28 +4,29 @@ import { } from '@chakra-ui/react'; import React from 'react'; +import type { Transaction } from 'client/slices/tx/types/api'; import type { NovesDescribeTxsResponse } from 'types/api/noves'; -import type { Transaction } from 'types/api/transaction'; import type { ClusterChainConfig } from 'types/multichain'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import TxAdditionalInfo from 'client/slices/tx/components/TxAdditionalInfo'; +import TxFee from 'client/slices/tx/components/TxFee'; +import TxStatus from 'client/slices/tx/components/TxStatus'; +import TxType from 'client/slices/tx/components/TxType'; + +import TxWatchListTags from 'client/features/account/components/TxWatchListTags'; +import TxTranslationType from 'client/features/tx-interpretation/noves/components/TxTranslationType'; + import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import EntityTag from 'ui/shared/EntityTags/EntityTag'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import TxStatus from 'ui/shared/statusTag/TxStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; -import TxFee from 'ui/shared/tx/TxFee'; -import TxWatchListTags from 'ui/shared/tx/TxWatchListTags'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; -import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; -import TxType from 'ui/txs/TxType'; - -import TxTranslationType from './TxTranslationType'; -type Props = { +interface Props { tx: Transaction; showBlockInfo: boolean; currentAddress?: string; diff --git a/ui/txs/TxsTable.pw.tsx b/client/slices/tx/pages/index/list/TxsTable.pw.tsx similarity index 95% rename from ui/txs/TxsTable.pw.tsx rename to client/slices/tx/pages/index/list/TxsTable.pw.tsx index 2feb7fb8d1..b02c345acf 100644 --- a/ui/txs/TxsTable.pw.tsx +++ b/client/slices/tx/pages/index/list/TxsTable.pw.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import * as txMock from 'mocks/txs/tx'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/txs/TxsTable.tsx b/client/slices/tx/pages/index/list/TxsTable.tsx similarity index 90% rename from ui/txs/TxsTable.tsx rename to client/slices/tx/pages/index/list/TxsTable.tsx index a42bdb53e3..a32c1fd80b 100644 --- a/ui/txs/TxsTable.tsx +++ b/client/slices/tx/pages/index/list/TxsTable.tsx @@ -1,19 +1,22 @@ import React from 'react'; -import type { TxsSocketType } from './socket/types'; -import type { Transaction, TransactionsSortingField, TransactionsSortingValue } from 'types/api/transaction'; +import type { Transaction, TransactionsSortingField, TransactionsSortingValue } from 'client/slices/tx/types/api'; +import type { TxsSocketType } from 'client/slices/tx/types/socket'; + +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + +import type { TxsTranslationQuery } from 'client/features/tx-interpretation/noves/hooks/useDescribeTxs'; + +import { currencyUnits } from 'client/shared/chain/units'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useInitialList from 'client/shared/lists/useInitialList'; +import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; import config from 'configs/app'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useInitialList from 'lib/hooks/useInitialList'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useLazyRenderedList from 'lib/hooks/useLazyRenderedList'; -import { currencyUnits } from 'lib/units'; import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; -import type { TxsTranslationQuery } from './noves/useDescribeTxs'; import TxsSocketNotice from './socket/TxsSocketNotice'; import TxsTableItem from './TxsTableItem'; diff --git a/ui/txs/TxsTableItem.tsx b/client/slices/tx/pages/index/list/TxsTableItem.tsx similarity index 85% rename from ui/txs/TxsTableItem.tsx rename to client/slices/tx/pages/index/list/TxsTableItem.tsx index c8b129d0c4..d920299e48 100644 --- a/ui/txs/TxsTableItem.tsx +++ b/client/slices/tx/pages/index/list/TxsTableItem.tsx @@ -1,30 +1,31 @@ import { Flex, VStack } from '@chakra-ui/react'; import React from 'react'; +import type { Transaction } from 'client/slices/tx/types/api'; import type { NovesDescribeTxsResponse } from 'types/api/noves'; -import type { Transaction } from 'types/api/transaction'; import type { ClusterChainConfig } from 'types/multichain'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import BlockPendingUpdateHint from 'client/slices/block/components/BlockPendingUpdateHint'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import TxAdditionalInfo from 'client/slices/tx/components/TxAdditionalInfo'; +import TxFee from 'client/slices/tx/components/TxFee'; +import TxStatus from 'client/slices/tx/components/TxStatus'; +import TxType from 'client/slices/tx/components/TxType'; + +import TxWatchListTags from 'client/features/account/components/TxWatchListTags'; +import TxTranslationType from 'client/features/tx-interpretation/noves/components/TxTranslationType'; + import config from 'configs/app'; import { Badge } from 'toolkit/chakra/badge'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; -import BlockPendingUpdateHint from 'ui/shared/block/BlockPendingUpdateHint'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import EntityTag from 'ui/shared/EntityTags/EntityTag'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; -import TxStatus from 'ui/shared/statusTag/TxStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; -import TxFee from 'ui/shared/tx/TxFee'; -import TxWatchListTags from 'ui/shared/tx/TxWatchListTags'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; -import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; - -import TxTranslationType from './TxTranslationType'; -import TxType from './TxType'; -type Props = { +interface Props { tx: Transaction; showBlockInfo: boolean; currentAddress?: string; diff --git a/ui/txs/TxsTabs.tsx b/client/slices/tx/pages/index/list/TxsTabs.tsx similarity index 90% rename from ui/txs/TxsTabs.tsx rename to client/slices/tx/pages/index/list/TxsTabs.tsx index bec1c26bcd..baaa375646 100644 --- a/ui/txs/TxsTabs.tsx +++ b/client/slices/tx/pages/index/list/TxsTabs.tsx @@ -5,13 +5,17 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import { TX } from 'client/slices/tx/stubs/tx'; + +import TxsWatchlist from 'client/features/account/pages/tx-index-watchlist/TxsWatchlist'; + +import getChainValidationActionText from 'client/shared/chain/get-chain-validation-action-text'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { TX } from 'stubs/tx'; import { generateListStub } from 'stubs/utils'; import type { RoutedTabsProps } from 'toolkit/components/RoutedTabs/RoutedTabs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; @@ -19,9 +23,8 @@ import AdvancedFilterLink from 'ui/shared/links/AdvancedFilterLink'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useIsAuth from 'ui/snippets/auth/useIsAuth'; -import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting'; -import TxsWatchlist from './TxsWatchlist'; +import TxsWithFrontendSorting from './TxsWithFrontendSorting'; type TabId = 'validated' | 'pending' | 'blob_txs' | 'watchlist'; export const getTabId = (id: TabId, prefix?: string) => prefix ? `${ prefix }_${ id }` : id; @@ -96,7 +99,7 @@ const TxsTabs = ({ parentTab, tabsHeight, ...rest }: Props) => { }, }); - const verifiedTitle = capitalize(getNetworkValidationActionText(chainConfig)); + const verifiedTitle = capitalize(getChainValidationActionText(chainConfig)); const tabs: Array = [ { diff --git a/ui/txs/TxsWithAPISorting.tsx b/client/slices/tx/pages/index/list/TxsWithApiSorting.tsx similarity index 83% rename from ui/txs/TxsWithAPISorting.tsx rename to client/slices/tx/pages/index/list/TxsWithApiSorting.tsx index 7bdb4958f8..dacd38f675 100644 --- a/ui/txs/TxsWithAPISorting.tsx +++ b/client/slices/tx/pages/index/list/TxsWithApiSorting.tsx @@ -1,15 +1,15 @@ import React from 'react'; -import type { TxsSocketType } from './socket/types'; -import type { AddressFromToFilter } from 'types/api/address'; -import type { TransactionsSortingValue } from 'types/api/transaction'; +import type { AddressFromToFilter } from 'client/slices/address/types/api'; +import type { TransactionsSortingValue } from 'client/slices/tx/types/api'; +import type { TxsSocketType } from 'client/slices/tx/types/socket'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; import TxsContent from './TxsContent'; -type Props = { +interface Props { query: QueryWithPagesResult<'general:address_txs'>; showBlockInfo?: boolean; @@ -24,7 +24,7 @@ type Props = { showTableViewButton?: boolean; }; -const TxsWithAPISorting = ({ +const TxsWithApiSorting = ({ filter, filterValue, query, @@ -63,4 +63,4 @@ const TxsWithAPISorting = ({ ); }; -export default TxsWithAPISorting; +export default TxsWithApiSorting; diff --git a/ui/txs/TxsWithFrontendSorting.tsx b/client/slices/tx/pages/index/list/TxsWithFrontendSorting.tsx similarity index 83% rename from ui/txs/TxsWithFrontendSorting.tsx rename to client/slices/tx/pages/index/list/TxsWithFrontendSorting.tsx index 90ae9d7419..980a4460f3 100644 --- a/ui/txs/TxsWithFrontendSorting.tsx +++ b/client/slices/tx/pages/index/list/TxsWithFrontendSorting.tsx @@ -1,18 +1,18 @@ import React from 'react'; -import type { TxsSocketType } from './socket/types'; -import type { AddressFromToFilter } from 'types/api/address'; +import type { AddressFromToFilter } from 'client/slices/address/types/api'; +import type { TxsSocketType } from 'client/slices/tx/types/socket'; + +import useTxsSort from 'client/slices/tx/hooks/useTxsSort'; import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages'; import TxsContent from './TxsContent'; -import useTxsSort from './useTxsSort'; type Props = { query: QueryWithPagesResult<'general:txs_validated' | 'general:txs_pending'> | QueryWithPagesResult<'general:txs_watchlist'> | - QueryWithPagesResult<'general:block_txs'> | - QueryWithPagesResult<'general:zkevm_l2_txn_batch_txs'>; + QueryWithPagesResult<'general:block_txs'>; showBlockInfo?: boolean; socketType?: TxsSocketType; currentAddress?: string; diff --git a/ui/txs/__screenshots__/TxsListItem.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/txs/__screenshots__/TxsListItem.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/txs/__screenshots__/TxsListItem.pw.tsx_default_base-view-dark-mode-1.png b/client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/txs/__screenshots__/TxsListItem.pw.tsx_default_base-view-dark-mode-1.png rename to client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/txs/__screenshots__/TxsListItem.pw.tsx_default_with-base-address-1.png b/client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_default_with-base-address-1.png similarity index 100% rename from ui/txs/__screenshots__/TxsListItem.pw.tsx_default_with-base-address-1.png rename to client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_default_with-base-address-1.png diff --git a/ui/txs/__screenshots__/TxsListItem.pw.tsx_default_with-pending-update-1.png b/client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_default_with-pending-update-1.png similarity index 100% rename from ui/txs/__screenshots__/TxsListItem.pw.tsx_default_with-pending-update-1.png rename to client/slices/tx/pages/index/list/__screenshots__/TxsListItem.pw.tsx_default_with-pending-update-1.png diff --git a/ui/txs/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/client/slices/tx/pages/index/list/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png similarity index 100% rename from ui/txs/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png rename to client/slices/tx/pages/index/list/__screenshots__/TxsTable.pw.tsx_dark-color-mode_base-view-dark-mode-1.png diff --git a/ui/txs/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png b/client/slices/tx/pages/index/list/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png similarity index 100% rename from ui/txs/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png rename to client/slices/tx/pages/index/list/__screenshots__/TxsTable.pw.tsx_default_base-view-dark-mode-1.png diff --git a/ui/txs/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png b/client/slices/tx/pages/index/list/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png similarity index 100% rename from ui/txs/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png rename to client/slices/tx/pages/index/list/__screenshots__/TxsTable.pw.tsx_default_screen-xl-base-view-1.png diff --git a/ui/txs/socket/TxsSocketNotice.tsx b/client/slices/tx/pages/index/list/socket/TxsSocketNotice.tsx similarity index 85% rename from ui/txs/socket/TxsSocketNotice.tsx rename to client/slices/tx/pages/index/list/socket/TxsSocketNotice.tsx index ca0ae3ded7..0009fda59c 100644 --- a/ui/txs/socket/TxsSocketNotice.tsx +++ b/client/slices/tx/pages/index/list/socket/TxsSocketNotice.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import type { TxsSocketNoticePlace, TxsSocketType } from './types'; +import type { TxsSocketNoticePlace, TxsSocketType } from 'client/slices/tx/types/socket'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; import TxsSocketNoticeTypeAddress from './TxsSocketNoticeTypeAddress'; import TxsSocketNoticeTypeAll from './TxsSocketNoticeTypeAll'; diff --git a/ui/txs/socket/TxsSocketNoticeTypeAddress.tsx b/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAddress.tsx similarity index 83% rename from ui/txs/socket/TxsSocketNoticeTypeAddress.tsx rename to client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAddress.tsx index 806ea3463f..0e8653b56f 100644 --- a/ui/txs/socket/TxsSocketNoticeTypeAddress.tsx +++ b/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAddress.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import type { TxsSocketNoticePlace } from './types'; +import type { TxsSocketNoticePlace } from 'client/slices/tx/types/socket'; -import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; +import useTxsSocketTypeAddress from 'client/slices/tx/hooks/useTxsSocketTypeAddress'; -import useTxsSocketTypeAddress from './useTxsSocketTypeAddress'; +import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; interface Props { place: TxsSocketNoticePlace; diff --git a/ui/txs/socket/TxsSocketNoticeTypeAll.tsx b/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAll.tsx similarity index 87% rename from ui/txs/socket/TxsSocketNoticeTypeAll.tsx rename to client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAll.tsx index 6f9201b602..6282286b4e 100644 --- a/ui/txs/socket/TxsSocketNoticeTypeAll.tsx +++ b/client/slices/tx/pages/index/list/socket/TxsSocketNoticeTypeAll.tsx @@ -1,14 +1,14 @@ import React from 'react'; -import type { TxsSocketNoticePlace, TxsSocketType } from './types'; +import type { TxsSocketNoticePlace, TxsSocketType } from 'client/slices/tx/types/socket'; import { route } from 'nextjs/routes'; +import useNewTxsSocketTypeAll from 'client/slices/tx/hooks/useTxsSocketTypeAll'; + import { useMultichainContext } from 'lib/contexts/multichain'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; -import useNewTxsSocketTypeAll from './useTxsSocketTypeAll'; - interface Props { type: TxsSocketType; place: TxsSocketNoticePlace; diff --git a/ui/txs/TxsStats.pw.tsx b/client/slices/tx/pages/index/stats/TxsStats.pw.tsx similarity index 89% rename from ui/txs/TxsStats.pw.tsx rename to client/slices/tx/pages/index/stats/TxsStats.pw.tsx index 293a5da8bd..45d45dd5f2 100644 --- a/ui/txs/TxsStats.pw.tsx +++ b/client/slices/tx/pages/index/stats/TxsStats.pw.tsx @@ -1,7 +1,8 @@ import React from 'react'; +import * as txsStatsMock from 'client/slices/tx/mocks/stats'; + import * as statsMock from 'mocks/stats'; -import * as txsStatsMock from 'mocks/txs/stats'; import { test, expect } from 'playwright/lib'; import TxsStats from './TxsStats'; diff --git a/ui/txs/TxsStats.tsx b/client/slices/tx/pages/index/stats/TxsStats.tsx similarity index 97% rename from ui/txs/TxsStats.tsx rename to client/slices/tx/pages/index/stats/TxsStats.tsx index 1e06d54c6a..6ed7bffa03 100644 --- a/ui/txs/TxsStats.tsx +++ b/client/slices/tx/pages/index/stats/TxsStats.tsx @@ -2,12 +2,14 @@ import type { BoxProps } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { TXS_STATS, TXS_STATS_MICROSERVICE } from 'client/slices/tx/stubs/tx'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { useMultichainContext } from 'lib/contexts/multichain'; import getStatsLabelFromTitle from 'lib/stats/getStatsLabelFromTitle'; import { HOMEPAGE_STATS } from 'stubs/stats'; -import { TXS_STATS, TXS_STATS_MICROSERVICE } from 'stubs/tx'; import { thinsp } from 'toolkit/utils/htmlEntities'; import StatsWidget from 'ui/shared/stats/StatsWidget'; import calculateUsdValue from 'ui/shared/value/calculateUsdValue'; @@ -132,7 +134,7 @@ const TxsStats = (props: Props) => { isLoading={ isLoading } /> ) } - { txFeeSum24h != null && ( + { !Object.is(txFeeSum24h, NaN) && ( ; + type: string; +} + +export type TxRawTracesResponse = Array; + +export type TransactionType = 'rootstock_remasc' | +'rootstock_bridge' | +'token_transfer' | +'contract_creation' | +'contract_call' | +'token_creation' | +'coin_transfer' | +'blob_transaction'; + +export interface Transaction extends + TransactionRollup, + TransactionSuave, + TransactionStability, + TransactionCelo, + TransactionZilliqa, + TransactionOptimistic, + TransactionZkSync, + TransactionArbitrum, + TransactionScroll, + TransactionOpInterop, + TransactionFheOperations, + TransactionAuthorization, + TransactionActions, + TransactionDataAvailability +{ + to: AddressParam | null; + created_contract: AddressParam | null; + hash: string; + result: string; + confirmations: number; + status: 'ok' | 'error' | null | undefined; + block_number: number | null; + timestamp: string | null; + confirmation_duration: Array | null; + from: AddressParam; + value: string; + fee: TransactionFee; + gas_price: string | null; + type: number | null; + gas_used: string | null; + gas_limit: string; + max_fee_per_gas: string | null; + max_priority_fee_per_gas: string | null; + priority_fee: string | null; + base_fee_per_gas: string | null; + transaction_burnt_fee: string | null; + nonce: number; + position: number | null; + revert_reason: TransactionRevertReason | null; + raw_input: string; + decoded_input: DecodedInput | null; + token_transfers: Array | null; + token_transfers_overflow: boolean; + exchange_rate: string | null; + historic_exchange_rate: string | null; + method: string | null; + transaction_types: Array; + transaction_tag: string | null; + has_error_in_internal_transactions: boolean | null; + is_pending_update?: boolean; +}; + +export interface TransactionsStats { + pending_transactions_count: string; + transaction_fees_avg_24h: string; + transaction_fees_sum_24h: string; + transactions_count_24h: string; +} + +// INDEX + +export type TransactionsResponse = TransactionsResponseValidated | TransactionsResponsePending; + +export interface TransactionsResponseValidated { + items: Array; + next_page_params: { + block_number: number; + index: number; + items_count: number; + filter: 'validated'; + } | null; +} + +export interface TransactionsResponsePending { + items: Array; + next_page_params: { + inserted_at: string; + hash: string; + filter: 'pending'; + } | null; +} + +export type TxsResponse = TransactionsResponseValidated | TransactionsResponsePending | BlockTransactionsResponse; + +export interface TransactionsSorting { + sort: 'value' | 'fee' | 'block_number'; + order: 'asc' | 'desc'; +} + +export type TransactionsSortingField = TransactionsSorting['sort']; + +export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }` | 'default'; + +export type TxsTypeFilter = 'token_transfer' | 'contract_creation' | 'contract_call' | 'coin_transfer' | 'token_creation' | 'blob_transaction'; + +export type TxsMethodFilter = 'approve' | 'transfer' | 'multicall' | 'mint' | 'commit'; + +export type TxsFilters = { + filter: 'pending' | 'validated'; + type?: Array; + method?: Array; +}; + +// STATE CHANGES +export type TxStateChange = (TxStateChangeCoin | TxStateChangeToken) & { + address: AddressParam; + is_miner: boolean; + balance_before: string | null; + balance_after: string | null; +}; + +export interface TxStateChangeCoin { + type: 'coin'; + change: string; + token: null; +} + +export type TxStateChangeToken = TxStateChangeTokenErc20 | TxStateChangeTokenErc721 | TxStateChangeTokenErc1155; + +type TxStateChangeDirection = 'from' | 'to'; + +export interface TxStateChangeTokenErc20 { + type: 'token'; + token: TokenInfo; + change: string; +} + +export interface TxStateChangeTokenErc721 { + type: 'token'; + token: TokenInfo; + change: Array<{ + direction: TxStateChangeDirection; + total: Erc721TotalPayload; + }>; +} + +export interface TxStateChangeTokenErc1155 { + type: 'token'; + token: TokenInfo; + change: string; + token_id: string; +} + +export interface TxStateChangeTokenErc404 { + type: 'token'; + token: TokenInfo; + change: string; + token_id: string; +} + +export type TxStateChanges = { + items: Array; + next_page_params: { + items_count: number; + state_changes: null; + }; +}; diff --git a/ui/txs/socket/types.ts b/client/slices/tx/types/socket.ts similarity index 100% rename from ui/txs/socket/types.ts rename to client/slices/tx/types/socket.ts diff --git a/lib/web3/rpc/formatTxData.ts b/client/slices/tx/utils/format-rpc-data.ts similarity index 89% rename from lib/web3/rpc/formatTxData.ts rename to client/slices/tx/utils/format-rpc-data.ts index 1f7f3fb507..08bf6c18cd 100644 --- a/lib/web3/rpc/formatTxData.ts +++ b/client/slices/tx/utils/format-rpc-data.ts @@ -1,12 +1,14 @@ import type { Chain, GetTransactionReturnType, TransactionReceipt } from 'viem'; -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import { unknownAddress } from 'client/slices/address/utils/consts'; + +import hexToDecimal from 'client/shared/transformers/hex-to-decimal'; import dayjs from 'lib/date/dayjs'; -import hexToDecimal from 'lib/hexToDecimal'; -import { unknownAddress } from 'ui/shared/address/utils'; -export default function formatTxData( +export default function formatRpcData( tx: GetTransactionReturnType, receipt: TransactionReceipt | null, confirmations: bigint | null, diff --git a/lib/tx/getConfirmationDuration.ts b/client/slices/tx/utils/get-confirmation-duration.ts similarity index 83% rename from lib/tx/getConfirmationDuration.ts rename to client/slices/tx/utils/get-confirmation-duration.ts index 36d9a9afa9..9463dd2018 100644 --- a/lib/tx/getConfirmationDuration.ts +++ b/client/slices/tx/utils/get-confirmation-duration.ts @@ -1,4 +1,4 @@ -export default function getConfirmationString(durations: Array) { +export default function getConfirmationDuration(durations: Array) { if (durations.length === 0) { return ''; } diff --git a/ui/txs/sortTxs.spec.ts b/client/slices/tx/utils/sort-txs.spec.ts similarity index 94% rename from ui/txs/sortTxs.spec.ts rename to client/slices/tx/utils/sort-txs.spec.ts index 2e1b55a4f1..65ee47ad93 100644 --- a/ui/txs/sortTxs.spec.ts +++ b/client/slices/tx/utils/sort-txs.spec.ts @@ -1,8 +1,8 @@ -import type { Transaction } from 'types/api/transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; import { describe, it, expect } from 'vitest'; -import sortTxs, { sortTxsFromSocket } from './sortTxs'; +import sortTxs, { sortTxsFromSocket } from './sort-txs'; describe('sortTxs', () => { it('should sort transactions by value in descending order', () => { diff --git a/ui/txs/sortTxs.ts b/client/slices/tx/utils/sort-txs.ts similarity index 92% rename from ui/txs/sortTxs.ts rename to client/slices/tx/utils/sort-txs.ts index 891f3b2428..2832136db8 100644 --- a/ui/txs/sortTxs.ts +++ b/client/slices/tx/utils/sort-txs.ts @@ -1,4 +1,4 @@ -import type { Transaction, TransactionsSortingValue } from 'types/api/transaction'; +import type { Transaction, TransactionsSortingValue } from 'client/slices/tx/types/api'; import compareBns from 'lib/bigint/compareBns'; diff --git a/configs/app/apis.ts b/configs/app/apis.ts index b9c3e4c86b..e4668f1a64 100644 --- a/configs/app/apis.ts +++ b/configs/app/apis.ts @@ -1,4 +1,4 @@ -import type { ApiName } from 'lib/api/types'; +import type { ApiName } from 'client/api/types'; import { stripTrailingSlash } from 'toolkit/utils/url'; @@ -8,6 +8,7 @@ export interface ApiPropsBase { endpoint: string; basePath?: string; socketEndpoint?: string; + instanceId?: string; } export interface ApiPropsFull extends ApiPropsBase { @@ -58,6 +59,7 @@ const adminApi = (() => { return Object.freeze({ endpoint: apiHost, + instanceId: getEnvValue('NEXT_PUBLIC_ADMIN_RS_INSTANCE_ID') || getEnvValue('NEXT_PUBLIC_NETWORK_ID'), }); })(); @@ -80,6 +82,7 @@ const contractInfoApi = (() => { return Object.freeze({ endpoint: apiHost, + instanceId: getEnvValue('NEXT_PUBLIC_CONTRACT_INFO_INSTANCE_ID') || getEnvValue('NEXT_PUBLIC_NETWORK_ID'), }); })(); diff --git a/configs/app/app.ts b/configs/app/app.ts index 21d31f90fc..0fee3bac0d 100644 --- a/configs/app/app.ts +++ b/configs/app/app.ts @@ -1,4 +1,4 @@ -import * as cookies from 'lib/cookies'; +import { isPrivateMode } from 'client/shared/storage/cookies'; import { getEnvValue } from './utils'; @@ -15,7 +15,6 @@ const isDev = getEnvValue('NEXT_PUBLIC_APP_ENV') === 'development'; const isReview = getEnvValue('NEXT_PUBLIC_APP_ENV') === 'review'; const isPw = getEnvValue('NEXT_PUBLIC_APP_INSTANCE') === 'pw'; const spriteHash = getEnvValue('NEXT_PUBLIC_ICON_SPRITE_HASH'); -const isPrivateMode = cookies.get(cookies.NAMES.APP_PROFILE) === 'private'; const app = Object.freeze({ isDev, @@ -27,7 +26,7 @@ const app = Object.freeze({ baseUrl, useProxy: getEnvValue('NEXT_PUBLIC_USE_NEXT_JS_PROXY') === 'true', spriteHash, - isPrivateMode, + isPrivateMode: isPrivateMode(), }); export default app; diff --git a/configs/app/chain.ts b/configs/app/chain.ts index dcb642d12f..3d528bef21 100644 --- a/configs/app/chain.ts +++ b/configs/app/chain.ts @@ -14,9 +14,6 @@ const verificationType: NetworkVerificationType = (() => { if (rollupType === 'arbitrum') { return 'posting'; } - if (rollupType === 'zkEvm') { - return 'sequencing'; - } return getEnvValue('NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE') as NetworkVerificationTypeEnvs || 'mining'; })(); diff --git a/configs/app/features/address3rdPartyWidgets.ts b/configs/app/features/address-3rd-party-widgets.ts similarity index 100% rename from configs/app/features/address3rdPartyWidgets.ts rename to configs/app/features/address-3rd-party-widgets.ts diff --git a/configs/app/features/addressMetadata.ts b/configs/app/features/address-metadata.ts similarity index 100% rename from configs/app/features/addressMetadata.ts rename to configs/app/features/address-metadata.ts diff --git a/configs/app/features/addressProfileAPI.ts b/configs/app/features/address-profile-api.ts similarity index 87% rename from configs/app/features/addressProfileAPI.ts rename to configs/app/features/address-profile-api.ts index 88b475e0f2..c76cbdceba 100644 --- a/configs/app/features/addressProfileAPI.ts +++ b/configs/app/features/address-profile-api.ts @@ -1,9 +1,16 @@ import type { Feature } from './types'; -import type { AddressProfileAPIConfig } from 'types/client/addressProfileAPIConfig'; import app from '../app'; import { getEnvValue, parseEnvJson } from '../utils'; +type AddressProfileAPIConfig = { + api_url_template: string; + tag_link_template?: string; + tag_icon?: string; + tag_bg_color?: string; + tag_text_color?: string; +}; + const value = parseEnvJson(getEnvValue('NEXT_PUBLIC_ADDRESS_USERNAME_TAG')); function checkApiUrlTemplate(apiUrlTemplate: string): boolean { diff --git a/configs/app/features/addressVerification.ts b/configs/app/features/address-verification.ts similarity index 91% rename from configs/app/features/addressVerification.ts rename to configs/app/features/address-verification.ts index 6c8df5c7a5..8a2d0d2ef7 100644 --- a/configs/app/features/addressVerification.ts +++ b/configs/app/features/address-verification.ts @@ -3,7 +3,7 @@ import type { Feature } from './types'; import apis from '../apis'; import app from '../app'; import account from './account'; -import verifiedTokens from './verifiedTokens'; +import verifiedTokens from './verified-tokens'; const title = 'Address verification in "My account"'; diff --git a/configs/app/features/ads-banner.ts b/configs/app/features/ads-banner.ts new file mode 100644 index 0000000000..9659bffd1a --- /dev/null +++ b/configs/app/features/ads-banner.ts @@ -0,0 +1,140 @@ +import type { Feature } from './types'; +import type { AdButlerConfig, AdButlerDeviceConfig } from 'types/client/adButlerConfig'; +import { SUPPORTED_AD_BANNER_PROVIDERS } from 'types/client/adProviders'; +import type { AdBannerProviders, AdBannerAdditionalProviders } from 'types/client/adProviders'; + +import app from '../app'; +import { getEnvValue, parseEnvJson } from '../utils'; + +const SEVIO_ZONE_MOBILE = '52909312-7ebb-4bd5-9006-5e4f7041ed63'; +const SEVIO_ZONE_DESKTOP = '07cabd45-77f1-4203-8081-868bae776981'; +const SEVIO_INVENTORY_ID = '65597ae2-67b8-404b-ac28-43d5029389da'; +const SEVIO_ACCOUNT_ID = 'e08c2e1e-2213-49d8-a397-c2e32094fba4'; +const SEVIO_AD_TYPE = 'banner'; + +interface SevioConfig { + readonly zoneMobile: string; + readonly zoneDesktop: string; + readonly inventoryId: string; + readonly accountId: string; + readonly adType: string; +} + +const provider: AdBannerProviders = (() => { + const envValue = getEnvValue('NEXT_PUBLIC_AD_BANNER_PROVIDER') as AdBannerProviders; + return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'slise'; +})(); + +const additionalProvider = getEnvValue('NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER') as AdBannerAdditionalProviders; +const isSpecifyEnabled = getEnvValue('NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY') === 'true'; + +const sevioZones = parseEnvJson>(getEnvValue('NEXT_PUBLIC_AD_BANNER_SEVIO_ZONES')); +const sevioConfig: SevioConfig = { + zoneMobile: sevioZones?.[0] || SEVIO_ZONE_MOBILE, + zoneDesktop: sevioZones?.[1] || SEVIO_ZONE_DESKTOP, + inventoryId: SEVIO_INVENTORY_ID, + accountId: SEVIO_ACCOUNT_ID, + adType: SEVIO_AD_TYPE, +}; + +const adButlerDesktopConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP')); +const adButlerMobileConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE')); +const adButlerConfig: AdButlerConfig | null = adButlerDesktopConfig && adButlerMobileConfig ? { + config: { + desktop: adButlerDesktopConfig, + mobile: adButlerMobileConfig, + }, +} : null; + +const title = 'Banner ads'; + +type AdsBannerFeatureProviderPayload = { + provider: 'slise'; +} | { + provider: 'sevio'; + sevio: SevioConfig; +} | { + provider: 'adbutler'; + adButler: AdButlerConfig; +} | { + provider: 'slise'; + additionalProvider: 'adbutler'; + adButler: AdButlerConfig; +} | { + provider: 'sevio'; + additionalProvider: 'adbutler'; + sevio: SevioConfig; + adButler: AdButlerConfig; +}; + +type AdsBannerFeaturePayload = AdsBannerFeatureProviderPayload & { + isSpecifyEnabled: boolean; +}; + +const config: Feature = (() => { + if (app.isPrivateMode) { + return Object.freeze({ + title, + isEnabled: false, + }); + } + + if (provider === 'adbutler' && adButlerConfig) { + return Object.freeze({ + title, + isEnabled: true, + provider, + adButler: adButlerConfig, + isSpecifyEnabled, + }); + } + + if (provider !== 'none' && additionalProvider === 'adbutler' && adButlerConfig) { + if (provider === 'sevio') { + return Object.freeze({ + title, + isEnabled: true, + provider, + additionalProvider, + sevio: sevioConfig, + adButler: adButlerConfig, + isSpecifyEnabled, + }); + } + + return Object.freeze({ + title, + isEnabled: true, + provider, + additionalProvider, + adButler: adButlerConfig, + isSpecifyEnabled, + }); + } + + if (provider === 'sevio') { + return Object.freeze({ + title, + isEnabled: true, + provider, + sevio: sevioConfig, + isSpecifyEnabled, + }); + } + + if (provider === 'slise') { + return Object.freeze({ + title, + isEnabled: true, + provider, + isSpecifyEnabled, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/adsText.ts b/configs/app/features/ads-text.ts similarity index 97% rename from configs/app/features/adsText.ts rename to configs/app/features/ads-text.ts index aa62652120..53b93596e4 100644 --- a/configs/app/features/adsText.ts +++ b/configs/app/features/ads-text.ts @@ -19,7 +19,7 @@ interface SevioConfig { const provider: AdTextProviders = (() => { const envValue = getEnvValue('NEXT_PUBLIC_AD_TEXT_PROVIDER') as AdTextProviders; - return envValue && SUPPORTED_AD_TEXT_PROVIDERS.includes(envValue) ? envValue : 'coinzilla'; + return envValue && SUPPORTED_AD_TEXT_PROVIDERS.includes(envValue) ? envValue : 'sevio'; })(); const title = 'Text ads'; diff --git a/configs/app/features/adsBanner.ts b/configs/app/features/adsBanner.ts deleted file mode 100644 index 764c176223..0000000000 --- a/configs/app/features/adsBanner.ts +++ /dev/null @@ -1,105 +0,0 @@ -import type { Feature } from './types'; -import type { AdButlerConfig } from 'types/client/adButlerConfig'; -import { SUPPORTED_AD_BANNER_PROVIDERS } from 'types/client/adProviders'; -import type { AdBannerProviders, AdBannerAdditionalProviders } from 'types/client/adProviders'; - -import app from '../app'; -import { getEnvValue, parseEnvJson } from '../utils'; - -const provider: AdBannerProviders = (() => { - const envValue = getEnvValue('NEXT_PUBLIC_AD_BANNER_PROVIDER') as AdBannerProviders; - - return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'slise'; -})(); - -const additionalProvider = getEnvValue('NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER') as AdBannerAdditionalProviders; -const isSpecifyEnabled = getEnvValue('NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY') === 'true'; - -const title = 'Banner ads'; - -type AdsBannerFeatureProviderPayload = { - provider: Exclude; -} | { - provider: 'adbutler'; - adButler: { - config: { - desktop: AdButlerConfig; - mobile: AdButlerConfig; - }; - }; -} | { - provider: Exclude; - additionalProvider: 'adbutler'; - adButler: { - config: { - desktop: AdButlerConfig; - mobile: AdButlerConfig; - }; - }; -}; - -type AdsBannerFeaturePayload = AdsBannerFeatureProviderPayload & { - isSpecifyEnabled: boolean; -}; - -const config: Feature = (() => { - if (app.isPrivateMode) { - return Object.freeze({ - title, - isEnabled: false, - }); - } - - if (provider === 'adbutler') { - const desktopConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP')); - const mobileConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE')); - - if (desktopConfig && mobileConfig) { - return Object.freeze({ - title, - isEnabled: true, - provider, - adButler: { - config: { - desktop: desktopConfig, - mobile: mobileConfig, - }, - }, - isSpecifyEnabled, - }); - } - } else if (provider !== 'none') { - - if (additionalProvider === 'adbutler') { - const desktopConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP')); - const mobileConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE')); - - return Object.freeze({ - title, - isEnabled: true, - provider, - additionalProvider, - adButler: { - config: { - desktop: desktopConfig, - mobile: mobileConfig, - }, - }, - isSpecifyEnabled, - }); - } - return Object.freeze({ - title, - isEnabled: true, - provider, - isSpecifyEnabled, - }); - } - - return Object.freeze({ - title, - isEnabled: false, - }); -})(); - -export default config; diff --git a/configs/app/features/advancedFilter.ts b/configs/app/features/advanced-filter.ts similarity index 100% rename from configs/app/features/advancedFilter.ts rename to configs/app/features/advanced-filter.ts diff --git a/configs/app/features/apiDocs.ts b/configs/app/features/api-docs.ts similarity index 100% rename from configs/app/features/apiDocs.ts rename to configs/app/features/api-docs.ts diff --git a/configs/app/features/beaconChain.ts b/configs/app/features/beacon-chain.ts similarity index 100% rename from configs/app/features/beaconChain.ts rename to configs/app/features/beacon-chain.ts diff --git a/configs/app/features/blockchainInteraction.ts b/configs/app/features/blockchain-interaction.ts similarity index 100% rename from configs/app/features/blockchainInteraction.ts rename to configs/app/features/blockchain-interaction.ts diff --git a/configs/app/features/bridgedTokens.ts b/configs/app/features/bridged-tokens.ts similarity index 100% rename from configs/app/features/bridgedTokens.ts rename to configs/app/features/bridged-tokens.ts diff --git a/configs/app/features/crossChainTxs.ts b/configs/app/features/cross-chain-txs.ts similarity index 100% rename from configs/app/features/crossChainTxs.ts rename to configs/app/features/cross-chain-txs.ts diff --git a/configs/app/features/csvExport.ts b/configs/app/features/csv-export.ts similarity index 100% rename from configs/app/features/csvExport.ts rename to configs/app/features/csv-export.ts diff --git a/configs/app/features/dataAvailability.ts b/configs/app/features/data-availability.ts similarity index 100% rename from configs/app/features/dataAvailability.ts rename to configs/app/features/data-availability.ts diff --git a/configs/app/features/deFiDropdown.ts b/configs/app/features/defi-dropdown.ts similarity index 100% rename from configs/app/features/deFiDropdown.ts rename to configs/app/features/defi-dropdown.ts diff --git a/configs/app/features/easterEggBadge.ts b/configs/app/features/easter-egg-badge.ts similarity index 100% rename from configs/app/features/easterEggBadge.ts rename to configs/app/features/easter-egg-badge.ts diff --git a/configs/app/features/easterEggPuzzleBadge.ts b/configs/app/features/easter-egg-puzzle-badge.ts similarity index 100% rename from configs/app/features/easterEggPuzzleBadge.ts rename to configs/app/features/easter-egg-puzzle-badge.ts diff --git a/configs/app/features/externalTxs.ts b/configs/app/features/external-txs.ts similarity index 100% rename from configs/app/features/externalTxs.ts rename to configs/app/features/external-txs.ts diff --git a/configs/app/features/faultProofSystem.ts b/configs/app/features/fault-proof-system.ts similarity index 100% rename from configs/app/features/faultProofSystem.ts rename to configs/app/features/fault-proof-system.ts diff --git a/configs/app/features/flashblocks.ts b/configs/app/features/flashblocks.ts index 28a247bdc1..24d1a1d0e3 100644 --- a/configs/app/features/flashblocks.ts +++ b/configs/app/features/flashblocks.ts @@ -1,7 +1,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -import megaEthFeature from './megaEth'; +import megaEthFeature from './mega-eth'; const title = 'Flashblocks'; diff --git a/configs/app/features/gasTracker.ts b/configs/app/features/gas-tracker.ts similarity index 100% rename from configs/app/features/gasTracker.ts rename to configs/app/features/gas-tracker.ts diff --git a/configs/app/features/getGasButton.ts b/configs/app/features/get-gas-button.ts similarity index 100% rename from configs/app/features/getGasButton.ts rename to configs/app/features/get-gas-button.ts diff --git a/configs/app/features/googleAnalytics.ts b/configs/app/features/google-analytics.ts similarity index 100% rename from configs/app/features/googleAnalytics.ts rename to configs/app/features/google-analytics.ts diff --git a/configs/app/features/growthBook.ts b/configs/app/features/growthbook.ts similarity index 100% rename from configs/app/features/growthBook.ts rename to configs/app/features/growthbook.ts diff --git a/configs/app/features/hotContracts.ts b/configs/app/features/hot-contracts.ts similarity index 100% rename from configs/app/features/hotContracts.ts rename to configs/app/features/hot-contracts.ts diff --git a/configs/app/features/index.ts b/configs/app/features/index.ts index f343062abb..ebbc836e20 100644 --- a/configs/app/features/index.ts +++ b/configs/app/features/index.ts @@ -1,39 +1,39 @@ -export { default as advancedFilter } from './advancedFilter'; +export { default as advancedFilter } from './advanced-filter'; export { default as account } from './account'; -export { default as addressVerification } from './addressVerification'; -export { default as addressMetadata } from './addressMetadata'; -export { default as address3rdPartyWidgets } from './address3rdPartyWidgets'; -export { default as adsBanner } from './adsBanner'; -export { default as adsText } from './adsText'; -export { default as apiDocs } from './apiDocs'; -export { default as beaconChain } from './beaconChain'; -export { default as bridgedTokens } from './bridgedTokens'; -export { default as blockchainInteraction } from './blockchainInteraction'; +export { default as addressVerification } from './address-verification'; +export { default as addressMetadata } from './address-metadata'; +export { default as address3rdPartyWidgets } from './address-3rd-party-widgets'; +export { default as adsBanner } from './ads-banner'; +export { default as adsText } from './ads-text'; +export { default as apiDocs } from './api-docs'; +export { default as beaconChain } from './beacon-chain'; +export { default as bridgedTokens } from './bridged-tokens'; +export { default as blockchainInteraction } from './blockchain-interaction'; export { default as celo } from './celo'; -export { default as crossChainTxs } from './crossChainTxs'; -export { default as csvExport } from './csvExport'; -export { default as dataAvailability } from './dataAvailability'; -export { default as deFiDropdown } from './deFiDropdown'; -export { default as easterEggBadge } from './easterEggBadge'; -export { default as easterEggPuzzleBadge } from './easterEggPuzzleBadge'; -export { default as externalTxs } from './externalTxs'; -export { default as faultProofSystem } from './faultProofSystem'; +export { default as crossChainTxs } from './cross-chain-txs'; +export { default as csvExport } from './csv-export'; +export { default as dataAvailability } from './data-availability'; +export { default as deFiDropdown } from './defi-dropdown'; +export { default as easterEggBadge } from './easter-egg-badge'; +export { default as easterEggPuzzleBadge } from './easter-egg-puzzle-badge'; +export { default as externalTxs } from './external-txs'; +export { default as faultProofSystem } from './fault-proof-system'; export { default as flashblocks } from './flashblocks'; -export { default as gasTracker } from './gasTracker'; -export { default as getGasButton } from './getGasButton'; -export { default as googleAnalytics } from './googleAnalytics'; -export { default as growthBook } from './growthBook'; -export { default as hotContracts } from './hotContracts'; +export { default as gasTracker } from './gas-tracker'; +export { default as getGasButton } from './get-gas-button'; +export { default as googleAnalytics } from './google-analytics'; +export { default as growthBook } from './growthbook'; +export { default as hotContracts } from './hot-contracts'; export { default as marketplace } from './marketplace'; -export { default as megaEth } from './megaEth'; +export { default as megaEth } from './mega-eth'; export { default as metasuites } from './metasuites'; export { default as mixpanel } from './mixpanel'; -export { default as mudFramework } from './mudFramework'; +export { default as mudFramework } from './mud-framework'; export { default as multichain } from './multichain'; -export { default as multichainButton } from './multichainButton'; -export { default as nameServices } from './nameServices'; +export { default as multichainButton } from './multichain-button'; +export { default as nameServices } from './name-services'; export { default as pools } from './pools'; -export { default as publicTagsSubmission } from './publicTagsSubmission'; +export { default as publicTagsSubmission } from './public-tags-submission'; export { default as rewards } from './rewards'; export { default as rollbar } from './rollbar'; export { default as rollup } from './rollup'; @@ -42,11 +42,11 @@ export { default as sol2uml } from './sol2uml'; export { default as stats } from './stats'; export { default as suave } from './suave'; export { default as tac } from './tac'; -export { default as txInterpretation } from './txInterpretation'; -export { default as userOps } from './userOps'; -export { default as addressProfileAPI } from './addressProfileAPI'; +export { default as txInterpretation } from './tx-interpretation'; +export { default as userOps } from './user-ops'; +export { default as addressProfileAPI } from './address-profile-api'; export { default as validators } from './validators'; -export { default as verifiedTokens } from './verifiedTokens'; -export { default as web3Wallet } from './web3Wallet'; -export { default as xStarScore } from './xStarScore'; +export { default as verifiedTokens } from './verified-tokens'; +export { default as web3Wallet } from './web3-wallet'; +export { default as xStarScore } from './x-star-score'; export { default as zetachain } from './zetachain'; diff --git a/configs/app/features/marketplace.ts b/configs/app/features/marketplace.ts index 28eec0ea61..0efd00c99a 100644 --- a/configs/app/features/marketplace.ts +++ b/configs/app/features/marketplace.ts @@ -5,7 +5,7 @@ import apis from '../apis'; import app from '../app'; import chain from '../chain'; import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from '../utils'; -import blockchainInteraction from './blockchainInteraction'; +import blockchainInteraction from './blockchain-interaction'; const defaultTitles: MarketplaceTitles = { entity_name: 'Dapp', diff --git a/configs/app/features/megaEth.ts b/configs/app/features/mega-eth.ts similarity index 100% rename from configs/app/features/megaEth.ts rename to configs/app/features/mega-eth.ts diff --git a/configs/app/features/mudFramework.ts b/configs/app/features/mud-framework.ts similarity index 100% rename from configs/app/features/mudFramework.ts rename to configs/app/features/mud-framework.ts diff --git a/configs/app/features/multichainButton.ts b/configs/app/features/multichain-button.ts similarity index 100% rename from configs/app/features/multichainButton.ts rename to configs/app/features/multichain-button.ts diff --git a/configs/app/features/nameServices.ts b/configs/app/features/name-services.ts similarity index 100% rename from configs/app/features/nameServices.ts rename to configs/app/features/name-services.ts diff --git a/configs/app/features/publicTagsSubmission.ts b/configs/app/features/public-tags-submission.ts similarity index 90% rename from configs/app/features/publicTagsSubmission.ts rename to configs/app/features/public-tags-submission.ts index 2f15b424f9..dadb5e3c53 100644 --- a/configs/app/features/publicTagsSubmission.ts +++ b/configs/app/features/public-tags-submission.ts @@ -3,7 +3,7 @@ import type { Feature } from './types'; import apis from '../apis'; import app from '../app'; import services from '../services'; -import addressMetadata from './addressMetadata'; +import addressMetadata from './address-metadata'; const title = 'Public tag submission'; diff --git a/configs/app/features/rewards.ts b/configs/app/features/rewards.ts index c61c521fc0..1f7d0565e6 100644 --- a/configs/app/features/rewards.ts +++ b/configs/app/features/rewards.ts @@ -3,7 +3,7 @@ import type { Feature } from './types'; import apis from '../apis'; import app from '../app'; import account from './account'; -import blockchainInteraction from './blockchainInteraction'; +import blockchainInteraction from './blockchain-interaction'; const title = 'Rewards service integration'; diff --git a/configs/app/features/txInterpretation.ts b/configs/app/features/tx-interpretation.ts similarity index 100% rename from configs/app/features/txInterpretation.ts rename to configs/app/features/tx-interpretation.ts diff --git a/configs/app/features/userOps.ts b/configs/app/features/user-ops.ts similarity index 100% rename from configs/app/features/userOps.ts rename to configs/app/features/user-ops.ts diff --git a/configs/app/features/verifiedTokens.ts b/configs/app/features/verified-tokens.ts similarity index 100% rename from configs/app/features/verifiedTokens.ts rename to configs/app/features/verified-tokens.ts diff --git a/configs/app/features/web3Wallet.ts b/configs/app/features/web3-wallet.ts similarity index 100% rename from configs/app/features/web3Wallet.ts rename to configs/app/features/web3-wallet.ts diff --git a/configs/app/features/xStarScore.ts b/configs/app/features/x-star-score.ts similarity index 100% rename from configs/app/features/xStarScore.ts rename to configs/app/features/x-star-score.ts diff --git a/configs/app/ui.ts b/configs/app/ui.ts index 745f6d9980..6b255d0ce9 100644 --- a/configs/app/ui.ts +++ b/configs/app/ui.ts @@ -17,7 +17,7 @@ const homePageStats: Array = (() => { if (!Array.isArray(parsedValue)) { const rollupFeature = features.rollup; - if (rollupFeature.isEnabled && [ 'zkEvm', 'zkSync', 'arbitrum' ].includes(rollupFeature.type)) { + if (rollupFeature.isEnabled && [ 'zkSync', 'arbitrum' ].includes(rollupFeature.type)) { return [ 'latest_batch', 'average_block_time', 'total_txs', 'wallet_addresses', 'gas_tracker' ]; } diff --git a/configs/app/ui/views/address.ts b/configs/app/ui/views/address.ts index 4616891f89..aba2bf9621 100644 --- a/configs/app/ui/views/address.ts +++ b/configs/app/ui/views/address.ts @@ -1,7 +1,7 @@ +import type { AddressFormat, AddressViewId, IdenticonType } from 'client/slices/address/types/view'; +import { ADDRESS_FORMATS, ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'client/slices/address/types/view'; import type { SmartContractVerificationMethodExtra } from 'types/client/contract'; import { SMART_CONTRACT_EXTRA_VERIFICATION_METHODS } from 'types/client/contract'; -import type { AddressFormat, AddressViewId, IdenticonType } from 'types/views/address'; -import { ADDRESS_FORMATS, ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'types/views/address'; import { getEnvValue, parseEnvJson } from 'configs/app/utils'; diff --git a/configs/envs/.env.eth b/configs/envs/.env.eth index ad1f44b25c..1afccee42c 100644 --- a/configs/envs/.env.eth +++ b/configs/envs/.env.eth @@ -76,4 +76,4 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address NEXT_PUBLIC_API_KEYS_ALERT_MESSAGE='Chain-specific API keys are being deprecated. Please migrate to Blockscout's PRO API for new multichain access. Existing API keys will become invalid on 1st of Jan 2027' NEXT_PUBLIC_API_DOCS_ALERT_MESSAGE='Blockscout Pro API is now available. Check out the Blockscout Pro API documentation for new multichain access.' -NEXT_PUBLIC_ACCOUNT_API_KEYS_BUTTON=https://dev.blockscout.com \ No newline at end of file +NEXT_PUBLIC_ACCOUNT_API_KEYS_BUTTON=https://dev.blockscout.com diff --git a/configs/envs/.env.main b/configs/envs/.env.main index 91d953ae27..16a7ce110d 100644 --- a/configs/envs/.env.main +++ b/configs/envs/.env.main @@ -12,6 +12,7 @@ NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_HOMEPAGE_HIGHLIGHTS_CONFIG=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/homepage-highlights/test.json # NEXT_PUBLIC_ACCOUNT_AUTH_PROVIDER=dynamic NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=['

Launch your own fully functioning blockchain explorer in minutes. Deploy now

', '

The super duck!

'] +NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES=[{'id':'ERC-7984','name':'ERC-7984'}] # Instance ENVs NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY=true diff --git a/configs/envs/.env.multichain_dev b/configs/envs/.env.multichain_dev index 78d7b2d565..3296c95707 100644 --- a/configs/envs/.env.multichain_dev +++ b/configs/envs/.env.multichain_dev @@ -1,5 +1,5 @@ -# Set of ENVs for OP Mainnet network explorer -# https://xxx.blockscout.com +# Set of ENVs for Superchain network explorer +# https://superchain.k8s-dev.blockscout.com # This is an auto-generated file. To update all values, run "pnpm dev:preset:sync --name=multichain_dev" # Local ENVs @@ -7,24 +7,27 @@ NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_ENV=development +NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws -NEXT_PUBLIC_MULTICHAIN_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_AGGREGATOR_API_HOST=https://multichain-aggregator.k8s-dev.blockscout.com -NEXT_PUBLIC_MULTICHAIN_CLUSTER=interop -NEXT_PUBLIC_MULTICHAIN_STATS_API_HOST=http://multichain-search-stats.k8s-dev.blockscout.com - -SKIP_ENVS_VALIDATION=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}] - -NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] -NEXT_PUBLIC_HOMEPAGE_STATS=['total_txs','wallet_addresses'] +# Instance ENVs +NEXT_PUBLIC_ADVANCED_FILTER_ENABLED=false NEXT_PUBLIC_API_DOCS_TABS=[] -NEXT_PUBLIC_NETWORK_NAME=Blockscout NEXT_PUBLIC_GAS_TRACKER_ENABLED=false +NEXT_PUBLIC_HAS_USER_OPS=true NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=true NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=true +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap'] +NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(90deg, rgb(232, 52, 53) 0%, rgb(139, 28, 232) 100%)'],'text_color':['rgb(255, 255, 255)']} +NEXT_PUBLIC_HOMEPAGE_STATS=['total_txs','wallet_addresses'] NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=false -NEXT_PUBLIC_IS_TESTNET=false -NEXT_PUBLIC_USE_NEXT_JS_PROXY=true -NEXT_PUBLIC_HAS_USER_OPS=true -NEXT_PUBLIC_ADVANCED_FILTER_ENABLED=false \ No newline at end of file +NEXT_PUBLIC_IS_TESTNET=true +NEXT_PUBLIC_MULTICHAIN_AGGREGATOR_API_HOST=https://multichain-aggregator.k8s-dev.blockscout.com +NEXT_PUBLIC_MULTICHAIN_CLUSTER=interop +NEXT_PUBLIC_MULTICHAIN_ENABLED=true +NEXT_PUBLIC_MULTICHAIN_STATS_API_HOST=https://multichain-stats.k8s-dev.blockscout.com +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism-superchain.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism-superchain.svg +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism-superchain.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism-superchain-dark.svg +NEXT_PUBLIC_NETWORK_NAME=Superchain +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/superchain-explorer.png \ No newline at end of file diff --git a/configs/envs/.env.multichain_prod b/configs/envs/.env.multichain_prod index fc23fdf955..fa2cf32829 100644 --- a/configs/envs/.env.multichain_prod +++ b/configs/envs/.env.multichain_prod @@ -1,5 +1,5 @@ -# Set of ENVs for OP Mainnet network explorer -# https://xxx.blockscout.com +# Set of ENVs for Multichain network explorer +# https://explorer.blockscout.com # This is an auto-generated file. To update all values, run "pnpm dev:preset:sync --name=multichain_prod" # Local ENVs @@ -7,34 +7,29 @@ NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_ENV=development +NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws -NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/multichain-light.svg -NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/multichain-dark.svg -NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/multichain-light.svg -NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/multichain-dark.svg -NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/multichain.png -FAVICON_MASTER_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/favicons/multichain.png -NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(89deg,rgba(83,83,211) 50.49%,rgba(155,155,255) 99.38%)'],'text_color':['rgba(225,225,255,1)'],'button':{'_default':{'background':['rgba(16,17,18,1)']},'_hover':{'background':['rgba(74,71,127,1)']}}} -NEXT_PUBLIC_COLOR_THEME_OVERRIDES={'bg':{'primary':{'_light':{'value':'rgba(255,255,255,1)'},'_dark':{'value':'rgba(35,43,55,1)'}}},'text':{'primary':{'_light':{'value':'rgba(16,17,18,0.8)'},'_dark':{'value':'rgba(255,255,255,0.8)'}},'secondary':{'_light':{'value':'rgba(113,128,150,1)'},'_dark':{'value':'rgba(160,174,192,1)'}}},'hover':{'_light':{'value':'rgba(155,155,255,1)'},'_dark':{'value':'rgba(155,155,255,1)'}},'selected':{'control':{'text':{'_light':{'value':'rgba(16,17,18,1)'},'_dark':{'value':'rgba(247,250,252,1)'}},'bg':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(247,250,252,0.06)'}}},'option':{'bg':{'_light':{'value':'rgba(83,83,211,1)'},'_dark':{'value':'rgba(114,114,233,1)'}}}},'icon':{'primary':{'_light':{'value':'rgba(113,128,150,1)'},'_dark':{'value':'rgba(160,174,192,1)'}},'secondary':{'_light':{'value':'rgba(160,174,192,1)'},'_dark':{'value':'rgba(113,128,150,1)'}}},'button':{'primary':{'_light':{'value':'rgba(83,83,211,1)'},'_dark':{'value':'rgba(114,114,233,1)'},'text':{'_light':{'value':'rgba(255,255,255,0.92)'},'_dark':{'value':'rgba(255,255,255,0.92)'}}}},'link':{'primary':{'_light':{'value':'rgba(83,83,211,1)'},'_dark':{'value':'rgba(114,114,233,1)'}}},'graph':{'line':{'_light':{'value':'rgba(94,87,112,1)'},'_dark':{'value':'rgba(194,190,208,1)'}},'gradient':{'start':{'_light':{'value':'rgba(94,87,112,0.3)'},'_dark':{'value':'rgba(194,190,208,0.3)'}},'stop':{'_light':{'value':'rgba(94,87,112,0)'},'_dark':{'value':'rgba(194,190,208,0)'}}}},'stats':{'bg':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(255,255,255,0.06)'}}},'topbar':{'bg':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(255,255,255,0.06)'}}},'navigation':{'text':{'selected':{'_light':{'value':'rgba(16,17,18,1)'},'_dark':{'value':'rgba(247,250,252,1)'}}},'bg':{'selected':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(255,255,255,0.06)'}}}},'tabs':{'text':{'primary':{'_light':{'value':'rgba(113,128,150,1)'},'_dark':{'value':'rgba(160,174,192,1)'}}}}} - - -NEXT_PUBLIC_MULTICHAIN_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_AGGREGATOR_API_HOST=https://multichain-aggregator.services.blockscout.com -NEXT_PUBLIC_MULTICHAIN_CLUSTER=multichain -NEXT_PUBLIC_MULTICHAIN_STATS_API_HOST=https://multichain-stats.k8s-prod-3.blockscout.com - -SKIP_ENVS_VALIDATION=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}] - -NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] -NEXT_PUBLIC_HOMEPAGE_STATS=['total_txs','wallet_addresses'] +# Instance ENVs +NEXT_PUBLIC_ADVANCED_FILTER_ENABLED=false NEXT_PUBLIC_API_DOCS_TABS=[] -NEXT_PUBLIC_NETWORK_NAME=Blockscout +NEXT_PUBLIC_COLOR_THEME_OVERRIDES={'bg':{'primary':{'_light':{'value':'rgba(255,255,255,1)'},'_dark':{'value':'rgba(35,43,55,1)'}}},'text':{'primary':{'_light':{'value':'rgba(16,17,18,0.8)'},'_dark':{'value':'rgba(255,255,255,0.8)'}},'secondary':{'_light':{'value':'rgba(113,128,150,1)'},'_dark':{'value':'rgba(160,174,192,1)'}}},'hover':{'_light':{'value':'rgba(155,155,255,1)'},'_dark':{'value':'rgba(155,155,255,1)'}},'selected':{'control':{'text':{'_light':{'value':'rgba(16,17,18,1)'},'_dark':{'value':'rgba(247,250,252,1)'}},'bg':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(255,255,255,0.06)'}}},'option':{'bg':{'_light':{'value':'rgba(83,83,211,1)'},'_dark':{'value':'rgba(114,114,233,1)'}}}},'icon':{'primary':{'_light':{'value':'rgba(113,128,150,1)'},'_dark':{'value':'rgba(160,174,192,1)'}},'secondary':{'_light':{'value':'rgba(160,174,192,1)'},'_dark':{'value':'rgba(113,128,150,1)'}}},'button':{'primary':{'_light':{'value':'rgba(83,83,211,1)'},'_dark':{'value':'rgba(114,114,233,1)'},'text':{'_light':{'value':'rgba(255,255,255,0.92)'},'_dark':{'value':'rgba(255,255,255,0.92)'}}}},'link':{'primary':{'_light':{'value':'rgba(83,83,211,1)'},'_dark':{'value':'rgba(114,114,233,1)'}}},'graph':{'line':{'_light':{'value':'rgba(109,109,215,1)'},'_dark':{'value':'rgba(124,124,239,1)'}},'gradient':{'start':{'_light':{'value':'rgba(109,109,215,0.3)'},'_dark':{'value':'rgba(124,124,239,0.3)'}},'stop':{'_light':{'value':'rgba(109,109,215,0)'},'_dark':{'value':'rgba(124,124,239,0)'}}}},'stats':{'bg':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(255,255,255,0.06)'}}},'topbar':{'bg':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(255,255,255,0.06)'}}},'navigation':{'text':{'selected':{'_light':{'value':'rgba(16,17,18,1)'},'_dark':{'value':'rgba(247,250,252,1)'}}},'bg':{'selected':{'_light':{'value':'rgba(16,17,18,0.04)'},'_dark':{'value':'rgba(255,255,255,0.06)'}}}},'tabs':{'text':{'primary':{'_light':{'value':'rgba(16,17,18,0.8)'},'_dark':{'value':'rgba(255,255,255,0.8)'}}}}} NEXT_PUBLIC_GAS_TRACKER_ENABLED=false +NEXT_PUBLIC_HAS_USER_OPS=true NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=true NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=true +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] +NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(89deg,rgba(83,83,211) 50.49%,rgba(155,155,255) 99.38%)'],'text_color':['rgba(225,225,255,1)'],'button':{'_default':{'background':['rgba(16,17,18,1)']},'_hover':{'background':['rgba(74,71,127,1)']}}} +NEXT_PUBLIC_HOMEPAGE_STATS=['total_txs','wallet_addresses'] NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=false -NEXT_PUBLIC_IS_TESTNET=false -NEXT_PUBLIC_USE_NEXT_JS_PROXY=true -NEXT_PUBLIC_HAS_USER_OPS=true -NEXT_PUBLIC_ADVANCED_FILTER_ENABLED=false \ No newline at end of file +NEXT_PUBLIC_MULTICHAIN_AGGREGATOR_API_HOST=https://multichain-aggregator.services.blockscout.com +NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'octav', 'url_template': 'https://pro.octav.fi/?addresses={address}', 'logo': 'https://blockscout-icons.s3.us-east-1.amazonaws.com/Octav.png'}] +NEXT_PUBLIC_MULTICHAIN_CLUSTER=multichain +NEXT_PUBLIC_MULTICHAIN_ENABLED=true +NEXT_PUBLIC_MULTICHAIN_STATS_API_HOST=https://multichain-stats.k8s-prod-3.blockscout.com +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/multichain-light.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/multichain-dark.svg +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/multichain-light.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/multichain-dark.svg +NEXT_PUBLIC_NETWORK_NAME=Multichain +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/multichain.png +NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar \ No newline at end of file diff --git a/configs/envs/.env.numine b/configs/envs/.env.numine index a4b93b6abf..11fd736719 100644 --- a/configs/envs/.env.numine +++ b/configs/envs/.env.numine @@ -9,19 +9,20 @@ NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws -NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED=true -NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST=https://interchain-indexer.k8s-dev.blockscout.com - # Instance ENVs NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY=true -NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS=['talentprotocol','drops','blockscoutbadges','gitpoap','efp','etherscore','webacy','humanpassport','trustblock','smartmuv','humanode'] +NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS=['talentprotocol','drops','blockscoutbadges','gitpoap','efp','etherscore','webacy','humanpassport','smartmuv','humanode'] NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/widgets/config.json NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_DOCS_ALERT_MESSAGE=Deprecation Notice: Chain-specific API keys are being deprecated.
Please migrate to the Blockscout PRO API for multichain access.
Existing API keys will remain valid until July 1, 2026 NEXT_PUBLIC_API_HOST=numine.blockscout.com +NEXT_PUBLIC_API_KEYS_ALERT_MESSAGE=Deprecation Notice: Chain-specific API keys are being deprecated.
Please migrate to the Blockscout PRO API for multichain access.
Existing API keys will remain valid until July 1, 2026 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com +NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED=true NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST=https://bridge-indexer.k8s-dev.blockscout.com NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_MIXPANEL_CONFIG_OVERRIDES={"record_sessions_percent": 0.5,"record_heatmap_data": true} NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 diff --git a/configs/envs/.env.rari_testnet b/configs/envs/.env.rari_testnet index 4294b0923f..bc18d908b2 100644 --- a/configs/envs/.env.rari_testnet +++ b/configs/envs/.env.rari_testnet @@ -11,7 +11,7 @@ NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws # Instance ENVs NEXT_PUBLIC_AD_BANNER_PROVIDER=slise -NEXT_PUBLIC_AD_TEXT_PROVIDER=coinzilla +NEXT_PUBLIC_AD_TEXT_PROVIDER=sevio NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_HOST=rari-testnet.cloud.blockscout.com NEXT_PUBLIC_COLOR_THEME_DEFAULT=light @@ -34,4 +34,4 @@ NEXT_PUBLIC_ROLLUP_PARENT_CHAIN={'baseUrl':'https://arbitrum-sepolia.blockscout. NEXT_PUBLIC_ROLLUP_TYPE=arbitrum NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=false NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=jazzicon -NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com diff --git a/configs/envs/.env.zkevm b/configs/envs/.env.zkevm deleted file mode 100644 index faf8365bac..0000000000 --- a/configs/envs/.env.zkevm +++ /dev/null @@ -1,49 +0,0 @@ -# Set of ENVs for Polygon CDK Stavanger network explorer -# https://polygon-cdk-stavanger.blockscout.com -# This is an auto-generated file. To update all values, run "pnpm dev:preset:sync --name=zkevm" - -# Local ENVs -NEXT_PUBLIC_APP_PROTOCOL=http -NEXT_PUBLIC_APP_HOST=localhost -NEXT_PUBLIC_APP_PORT=3000 -NEXT_PUBLIC_APP_ENV=development -NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws - -# Instance ENVs -NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY=true -NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS=['talentprotocol','drops','blockscoutbadges','gitpoap','efp','etherscore','webacy','humanpassport','trustblock','smartmuv','humanode','deepdao'] -NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/widgets/config.json -NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com -NEXT_PUBLIC_API_BASE_PATH=/ -NEXT_PUBLIC_API_HOST=polygon-cdk-stavanger.blockscout.com -NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}] -NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com -NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/cdk-stavanger.json -NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge -NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=true -NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] -NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(122deg, rgba(162, 41, 197, 1) 0%, rgba(123, 63, 228, 1) 100%)'],'text_color':['rgba(255, 255, 255, 1)']} -NEXT_PUBLIC_IS_TESTNET=true -NEXT_PUBLIC_MARKETPLACE_ENABLED=false -NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com -NEXT_PUBLIC_METASUITES_ENABLED=true -NEXT_PUBLIC_MIXPANEL_CONFIG_OVERRIDES={"record_sessions_percent": 0.5,"record_heatmap_data": true} -NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG={"img_url": {"small": "https://blockscout-merits-images.s3.us-east-1.amazonaws.com/banners/sidemenu-banner-small.png", "large": "https://blockscout-merits-images.s3.us-east-1.amazonaws.com/banners/sidemenu-banner-big.png"}, "link_url": "https://www.blockscout.com/?utm_source=blockscout&utm_medium=side-menu-banner"} -NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 -NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ETH -NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH -NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/polygon-short.svg -NEXT_PUBLIC_NETWORK_ID=686669576 -NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/polygon.svg -NEXT_PUBLIC_NETWORK_NAME=Polygon CDK Stavanger -NEXT_PUBLIC_NETWORK_RPC_URL=https://sn2-stavanger-rpc.eu-north-2.gateway.fm -NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true -NEXT_PUBLIC_PUZZLE_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/capyPuzzleBadge -NEXT_PUBLIC_ROLLUP_PARENT_CHAIN={'baseUrl':'https://eth-sepolia.blockscout.com'} -NEXT_PUBLIC_ROLLUP_TYPE=zkEvm -NEXT_PUBLIC_STATS_API_BASE_PATH=/stats-service -NEXT_PUBLIC_STATS_API_HOST=https://polygon-cdk-stavanger.blockscout.com -NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout -NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true -NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com -NEXT_PUBLIC_WEB3_WALLETS=['token_pocket', 'metamask'] \ No newline at end of file diff --git a/cspell.jsonc b/cspell.jsonc index 7ad6f5a70b..dbe9aba120 100644 --- a/cspell.jsonc +++ b/cspell.jsonc @@ -15,7 +15,7 @@ "playwright/fixtures/rewards.ts", "public/static/capybara/index.js", "ui/showcases/utils.ts", - "ui/tx/TxExternalTxs.pw.tsx" + "client/features/external-txs/components/TxExternalTxs.pw.tsx" ], "enableGlobDot": true, "ignoreRandomStrings": true, @@ -82,8 +82,6 @@ "chainscout", "chakra", "clstr", - "coinzilla", - "coinzillatag", "commitish", "Computor", "contentscript", @@ -261,6 +259,7 @@ "utka", "utko", "UUPS", + "Valibot", "VCALENDAR", "vercel", "verifreg", diff --git a/deploy/helmfile.yaml.gotmpl b/deploy/helmfile.yaml.gotmpl index c38dce96ec..6d1f731e48 100644 --- a/deploy/helmfile.yaml.gotmpl +++ b/deploy/helmfile.yaml.gotmpl @@ -15,18 +15,18 @@ repositories: --- releases: - # Deploy review L2 + # Deploy demo 2 - for case when ENVs validator needs to be disabled (for example, multichain) - name: bs-stack chart: blockscout/blockscout-stack version: 1.*.* - namespace: review-l2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} + namespace: review-2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} labels: - app: review-l2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} + app: review-2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} values: - - values/review-l2/values.yaml.gotmpl + - values/review-2/values.yaml.gotmpl - global: env: "review" - # Deploy review + # Deploy demo - name: bs-stack chart: blockscout/blockscout-stack version: 1.*.* diff --git a/deploy/scripts/og_image_generator.js b/deploy/scripts/og_image_generator.js index 6f607376ff..2d232f1407 100755 --- a/deploy/scripts/og_image_generator.js +++ b/deploy/scripts/og_image_generator.js @@ -23,7 +23,7 @@ if (process.env.NEXT_PUBLIC_OG_IMAGE_URL) { try { const bannerConfig = JSON.parse(process.env.NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG?.replaceAll('\'', '"') || '{}'); const data = { - title: `${ process.env.NEXT_PUBLIC_NETWORK_NAME } explorer`, + title: bannerConfig.text || `${ process.env.NEXT_PUBLIC_NETWORK_NAME } explorer`, logo_url: process.env.NEXT_PUBLIC_NETWORK_LOGO_DARK ?? process.env.NEXT_PUBLIC_NETWORK_LOGO, background: bannerConfig.background?.[0], title_color: bannerConfig.text_color?.[0], diff --git a/deploy/tools/envs-validator/package.json b/deploy/tools/envs-validator/package.json index 779253fcae..94a61193d6 100644 --- a/deploy/tools/envs-validator/package.json +++ b/deploy/tools/envs-validator/package.json @@ -11,7 +11,7 @@ "devDependencies": { "dotenv-cli": "^7.2.1", "typescript": "5.4.2", - "vite": "6.4.1", + "vite": "6.4.2", "yup": "^1.2.0" } } diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index da9ef07747..64a9692368 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -9,7 +9,13 @@ declare module 'yup' { import * as yup from 'yup'; -import type { AddressProfileAPIConfig } from 'types/client/addressProfileAPIConfig'; +type AddressProfileAPIConfig = { + api_url_template: string; + tag_link_template?: string; + tag_icon?: string; + tag_bg_color?: string; + tag_text_color?: string; +}; import type { GasRefuelProviderConfig } from 'types/client/gasRefuelProviderConfig'; import { GAS_UNITS } from 'types/client/gasTracker'; import type { GasUnit } from 'types/client/gasTracker'; diff --git a/deploy/tools/envs-validator/schema_multichain.ts b/deploy/tools/envs-validator/schema_multichain.ts index 85e6f76ff4..474b1780f1 100644 --- a/deploy/tools/envs-validator/schema_multichain.ts +++ b/deploy/tools/envs-validator/schema_multichain.ts @@ -12,7 +12,7 @@ import * as uiSchemas from './schemas/ui'; import * as featuresSchemas from './schemas/features'; import servicesSchemas from './schemas/services'; import { replaceQuotes } from 'configs/app/utils'; -import { IDENTICON_TYPES } from 'types/views/address'; +import { IDENTICON_TYPES } from 'client/slices/address/types/view'; const schema = yup .object() diff --git a/deploy/tools/envs-validator/schemas/apis.ts b/deploy/tools/envs-validator/schemas/apis.ts index 112ceb839f..bf1106c137 100644 --- a/deploy/tools/envs-validator/schemas/apis.ts +++ b/deploy/tools/envs-validator/schemas/apis.ts @@ -15,8 +15,28 @@ export default yup.object({ NEXT_PUBLIC_VISUALIZE_API_BASE_PATH: yup.string(), NEXT_PUBLIC_CONTRACT_INFO_API_HOST: yup.string().test(urlTest), + NEXT_PUBLIC_CONTRACT_INFO_INSTANCE_ID: yup.string() + .when('NEXT_PUBLIC_CONTRACT_INFO_API_HOST', { + is: (value: string) => Boolean(value), + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_CONTRACT_INFO_INSTANCE_ID can only be used with NEXT_PUBLIC_CONTRACT_INFO_API_HOST', + value => value === undefined, + ), + }), NEXT_PUBLIC_ADMIN_SERVICE_API_HOST: yup.string().test(urlTest), + NEXT_PUBLIC_ADMIN_RS_INSTANCE_ID: yup.string() + .when('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST', { + is: (value: string) => Boolean(value), + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_ADMIN_RS_INSTANCE_ID can only be used with NEXT_PUBLIC_ADMIN_SERVICE_API_HOST', + value => value === undefined, + ), + }), NEXT_PUBLIC_REWARDS_SERVICE_API_HOST: yup.string().test(urlTest), diff --git a/deploy/tools/envs-validator/schemas/chain.ts b/deploy/tools/envs-validator/schemas/chain.ts index 16bfcb1a79..580be16f39 100644 --- a/deploy/tools/envs-validator/schemas/chain.ts +++ b/deploy/tools/envs-validator/schemas/chain.ts @@ -33,10 +33,10 @@ export default yup.object({ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: yup .string().oneOf([ 'validation', 'mining', 'fee reception' ]) .when('NEXT_PUBLIC_ROLLUP_TYPE', { - is: (value: string) => value === 'arbitrum' || value === 'zkEvm', + is: (value: string) => value === 'arbitrum', then: (schema) => schema.test( 'not-exist', - 'NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE can not be set for Arbitrum and ZkEVM rollups', + 'NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE can not be set for Arbitrum rollups', value => value === undefined, ), otherwise: (schema) => schema, diff --git a/deploy/tools/envs-validator/schemas/features/address3rdPartyWidgets.ts b/deploy/tools/envs-validator/schemas/features/address3rdPartyWidgets.ts index 7c08e25692..42c35774a2 100644 --- a/deploy/tools/envs-validator/schemas/features/address3rdPartyWidgets.ts +++ b/deploy/tools/envs-validator/schemas/features/address3rdPartyWidgets.ts @@ -1,5 +1,5 @@ import { replaceQuotes } from "configs/app/utils"; -import { Address3rdPartyWidget, ADDRESS_3RD_PARTY_WIDGET_PAGES } from "types/views/address"; +import { Address3rdPartyWidget, ADDRESS_3RD_PARTY_WIDGET_PAGES } from "client/features/address-3rd-party-widgets/types/view"; import * as yup from 'yup'; export const address3rdPartyWidgetsConfigSchema = yup @@ -21,6 +21,7 @@ export const address3rdPartyWidgetsConfigSchema = yup title: yup.string().required(), hint: yup.string().optional(), valuePath: yup.string().required(), + valueTitlePath: yup.string().optional(), pages: yup.array().of(yup.string().oneOf(ADDRESS_3RD_PARTY_WIDGET_PAGES)).required(), chainIds: yup.object>().optional(), }), diff --git a/deploy/tools/envs-validator/schemas/features/ads.ts b/deploy/tools/envs-validator/schemas/features/ads.ts index f7ae63cb3e..0b0d780a3b 100644 --- a/deploy/tools/envs-validator/schemas/features/ads.ts +++ b/deploy/tools/envs-validator/schemas/features/ads.ts @@ -1,11 +1,11 @@ import * as yup from 'yup'; import { replaceQuotes } from 'configs/app/utils'; import type { AdBannerProviders, AdBannerAdditionalProviders, AdTextProviders } from 'types/client/adProviders'; -import type { AdButlerConfig } from 'types/client/adButlerConfig'; +import type { AdButlerDeviceConfig } from 'types/client/adButlerConfig'; import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS, SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS } from 'types/client/adProviders'; const adButlerConfigSchema = yup - .object() + .object() .transform(replaceQuotes) .json() .when('NEXT_PUBLIC_AD_BANNER_PROVIDER', { @@ -29,11 +29,22 @@ const adButlerConfigSchema = yup .required(), }); +const sevioZonesSchema = yup + .array() + .transform(replaceQuotes) + .json() + .of(yup.string().required()) + .when('NEXT_PUBLIC_AD_BANNER_PROVIDER', { + is: (value: AdBannerProviders) => value === 'sevio', + then: (schema) => schema.length(2), + }); + export const adsSchema = yup.object({ NEXT_PUBLIC_AD_TEXT_PROVIDER: yup.string().oneOf(SUPPORTED_AD_TEXT_PROVIDERS), NEXT_PUBLIC_AD_BANNER_PROVIDER: yup.string().oneOf(SUPPORTED_AD_BANNER_PROVIDERS), NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER: yup.string().oneOf(SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS), + NEXT_PUBLIC_AD_BANNER_SEVIO_ZONES: sevioZonesSchema, NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP: adButlerConfigSchema, NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: adButlerConfigSchema, NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY: yup.boolean(), -}); \ No newline at end of file +}); diff --git a/deploy/tools/envs-validator/schemas/ui.ts b/deploy/tools/envs-validator/schemas/ui.ts index 47cd49c87f..aff2380358 100644 --- a/deploy/tools/envs-validator/schemas/ui.ts +++ b/deploy/tools/envs-validator/schemas/ui.ts @@ -8,8 +8,8 @@ import { CustomLink, CustomLinksGroup } from 'types/footerLinks'; import { COLOR_THEME_IDS } from 'types/settings'; import { FontFamily } from 'types/ui'; import { ContractCodeIde, SMART_CONTRACT_EXTRA_VERIFICATION_METHODS, type SmartContractVerificationMethodExtra } from 'types/client/contract'; -import type { AddressFormat, AddressViewId } from 'types/views/address'; -import { ADDRESS_FORMATS, ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'types/views/address'; +import type { AddressFormat, AddressViewId } from 'client/slices/address/types/view'; +import { ADDRESS_FORMATS, ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'client/slices/address/types/view'; import { BLOCK_FIELDS_IDS } from 'types/views/block'; import type { BlockFieldId } from 'types/views/block'; import type { TxAdditionalFieldsId, TxFieldsId } from 'types/views/tx'; @@ -38,6 +38,7 @@ const heroBannerSchema: yup.ObjectSchema = yup.object() search: yup.object({ border_width: yup.array().max(2).of(yup.string()), }), + text: yup.string(), }); export const homepageSchema = yup.object({ diff --git a/deploy/tools/envs-validator/test/.env.base b/deploy/tools/envs-validator/test/.env.base index 5ac596a930..061b02cfe7 100644 --- a/deploy/tools/envs-validator/test/.env.base +++ b/deploy/tools/envs-validator/test/.env.base @@ -8,9 +8,10 @@ NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx NEXT_PUBLIC_MIXPANEL_CONFIG_OVERRIDES='{"record_sessions_percent": 0.5,"record_heatmap_data": true}' NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx -NEXT_PUBLIC_AD_TEXT_PROVIDER=coinzilla +NEXT_PUBLIC_AD_TEXT_PROVIDER=sevio NEXT_PUBLIC_AD_BANNER_PROVIDER=slise NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://example.com +NEXT_PUBLIC_ADMIN_RS_INSTANCE_ID=420:duck NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_APP_ENV=development @@ -21,6 +22,7 @@ NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES=[{'type':'omni','title':'OmniBridge','short_t NEXT_PUBLIC_COLOR_THEME_DEFAULT=dim NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.blockscout.com/?address={hash}&blockscout={domain}','icon_url':'https://example.com/icon.svg'}] NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://example.com +NEXT_PUBLIC_CONTRACT_INFO_INSTANCE_ID=420:duck NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true NEXT_PUBLIC_FEATURED_NETWORKS=https://example.com NEXT_PUBLIC_FEATURED_NETWORKS_ALL_LINK=https://example.com @@ -95,4 +97,4 @@ NEXT_PUBLIC_HOMEPAGE_HIGHLIGHTS_CONFIG=https://example.com NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://example.com NEXT_PUBLIC_NAME_SERVICE_PROTOCOLS=['duck','goose'] NEXT_PUBLIC_CLUSTERS_API_HOST=https://example.com -NEXT_PUBLIC_CLUSTERS_CDN_URL=https://example.com \ No newline at end of file +NEXT_PUBLIC_CLUSTERS_CDN_URL=https://example.com diff --git a/deploy/tools/envs-validator/test/.env.multichain b/deploy/tools/envs-validator/test/.env.multichain index cd65449496..36bf457d8f 100644 --- a/deploy/tools/envs-validator/test/.env.multichain +++ b/deploy/tools/envs-validator/test/.env.multichain @@ -10,7 +10,7 @@ NEXT_PUBLIC_HOMEPAGE_STATS=['total_txs','wallet_addresses'] NEXT_PUBLIC_API_DOCS_TABS=[] NEXT_PUBLIC_FEATURED_NETWORKS=http://example.com NEXT_PUBLIC_FOOTER_LINKS=http://example.com -NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(90deg, rgb(232, 52, 53) 0%, rgb(139, 28, 232) 100%)'],'text_color':['rgb(255, 255, 255)']} +NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(90deg, rgb(232, 52, 53) 0%, rgb(139, 28, 232) 100%)'],'text_color':['rgb(255, 255, 255)'],'text': 'Duck migration observer'} NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}] NEXT_PUBLIC_NETWORK_ICON=http://example.com NEXT_PUBLIC_NETWORK_ICON_DARK=http://example.com diff --git a/deploy/tools/envs-validator/test/.env.sevio b/deploy/tools/envs-validator/test/.env.sevio new file mode 100644 index 0000000000..468c3302f7 --- /dev/null +++ b/deploy/tools/envs-validator/test/.env.sevio @@ -0,0 +1,2 @@ +NEXT_PUBLIC_AD_BANNER_PROVIDER=sevio +NEXT_PUBLIC_AD_BANNER_SEVIO_ZONES=['52909312-7ebb-4bd5-9006-5e4f7041ed63','07cabd45-77f1-4203-8081-868bae776981'] diff --git a/deploy/tools/envs-validator/test/assets/configs/address_3rd_party_widgets_config.json b/deploy/tools/envs-validator/test/assets/configs/address_3rd_party_widgets_config.json index a323d46c84..05f93a55d2 100644 --- a/deploy/tools/envs-validator/test/assets/configs/address_3rd_party_widgets_config.json +++ b/deploy/tools/envs-validator/test/assets/configs/address_3rd_party_widgets_config.json @@ -6,6 +6,7 @@ "title": "Widget 1", "hint": "Widget 1 hint", "valuePath": "result.value", + "valueTitlePath": "result.valueTitle", "pages": [ "eoa", "contract", "token" ] }, "widget-2": { diff --git a/deploy/tools/envs-validator/vite.config.ts b/deploy/tools/envs-validator/vite.config.ts index b7b3bfd43d..1f6bc33af4 100644 --- a/deploy/tools/envs-validator/vite.config.ts +++ b/deploy/tools/envs-validator/vite.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ }, resolve: { alias: { + client: resolve(__dirname, '../../../client'), configs: resolve(__dirname, '../../../configs'), lib: resolve(__dirname, '../../../lib'), toolkit: resolve(__dirname, '../../../toolkit'), diff --git a/deploy/tools/essential-dapps-chains-config-generator/package.json b/deploy/tools/essential-dapps-chains-config-generator/package.json index b76a1aee96..289808802a 100644 --- a/deploy/tools/essential-dapps-chains-config-generator/package.json +++ b/deploy/tools/essential-dapps-chains-config-generator/package.json @@ -16,6 +16,6 @@ "@types/node": "22.12.0", "dotenv-cli": "10.0.0", "typescript": "5.4.2", - "vite": "6.4.1" + "vite": "6.4.2" } } diff --git a/deploy/tools/essential-dapps-chains-config-generator/vite.config.ts b/deploy/tools/essential-dapps-chains-config-generator/vite.config.ts index 812dfb9dd1..a3e0ad243c 100644 --- a/deploy/tools/essential-dapps-chains-config-generator/vite.config.ts +++ b/deploy/tools/essential-dapps-chains-config-generator/vite.config.ts @@ -23,6 +23,7 @@ export default defineConfig({ }, resolve: { alias: { + client: resolve(__dirname, '../../../client'), configs: resolve(__dirname, '../../../configs'), lib: resolve(__dirname, '../../../lib'), toolkit: resolve(__dirname, '../../../toolkit'), diff --git a/deploy/tools/feature-reporter/package.json b/deploy/tools/feature-reporter/package.json index 7cb13e221c..0d09b81bb4 100644 --- a/deploy/tools/feature-reporter/package.json +++ b/deploy/tools/feature-reporter/package.json @@ -1,7 +1,13 @@ { "name": "feature-reporter", "version": "1.0.0", +<<<<<<< HEAD "main": "index.js", +======= + "private": true, + "type": "module", + "main": "dist/index.js", +>>>>>>> v2.8.0-alpha.1 "scripts": { "compile_config": "yarn tsc -p ./tsconfig.json && yarn tsc-alias -p ./tsconfig.json", "build": "yarn webpack-cli -c ./webpack.config.js", @@ -16,6 +22,13 @@ "webpack-cli": "^5.1.4" }, "devDependencies": { +<<<<<<< HEAD "dotenv-cli": "^7.2.1" +======= + "@types/node": "22.12.0", + "dotenv-cli": "^7.2.1", + "typescript": "5.4.2", + "vite": "6.4.2" +>>>>>>> v2.8.0-alpha.1 } } diff --git a/deploy/tools/feature-reporter/vite.config.ts b/deploy/tools/feature-reporter/vite.config.ts index 849c0da483..3946e9faaf 100644 --- a/deploy/tools/feature-reporter/vite.config.ts +++ b/deploy/tools/feature-reporter/vite.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ }, resolve: { alias: { + client: resolve(__dirname, '../../../client'), configs: resolve(__dirname, '../../../configs'), lib: resolve(__dirname, '../../../lib'), toolkit: resolve(__dirname, '../../../toolkit'), diff --git a/deploy/tools/llms-txt-generator/generate-pro-api.ts b/deploy/tools/llms-txt-generator/generate-pro-api.ts new file mode 100644 index 0000000000..816e63e455 --- /dev/null +++ b/deploy/tools/llms-txt-generator/generate-pro-api.ts @@ -0,0 +1,511 @@ +import config from 'configs/app'; +import dedent from 'dedent'; +import { layerLabels } from 'lib/rollups/utils'; + +const PRO_API_URL = 'https://api.blockscout.com'; + +export function generateProApi(): string { + const chainName = config.chain.name ?? ''; + const chainId = config.chain.id ?? ''; + + const rollupFeature = config.features.rollup; + const parentChainUrl = rollupFeature.isEnabled ? rollupFeature.parentChain.baseUrl : undefined; + const currentToParentLayerLabel = layerLabels.current + '→' + layerLabels.parent; + const parentToCurrentLayerLabel = layerLabels.parent + '→' + layerLabels.current; + + const validatorsFeature = config.features.validators; + + const GENERAL_COUNTERS_TEMPLATE = chainId ? ` + ### General Counters + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/stats-service/api/v1/counters' + \`\`\` + ` : '{blank}'; + + const USER_OPS_TEMPLATE = config.features.userOps.isEnabled ? ` + ### Account Abstraction info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/proxy/account-abstraction/accounts/{account_address}' + \`\`\` + + ### User Operations by Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/proxy/account-abstraction/operations?sender={account_address}' + \`\`\` + + ### User Operations by Transaction + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/proxy/account-abstraction/operations?transaction_hash={transaction_hash}' + \`\`\` + + ### User Operation Details + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/proxy/account-abstraction/operations/{user_operation_hash}' + \`\`\` + ` : '{blank}'; + + const BEACON_CHAIN_TEMPLATE = config.features.beaconChain.isEnabled ? ` + ### Deposits by Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/beacon/deposits' + \`\`\` + + ### Deposits by Block + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/blocks/{block_hash_or_number}/beacon/deposits' + \`\`\` + + ### Withdrawals by Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/withdrawals' + \`\`\` + + ### Withdrawals by Block + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/blocks/{block_hash_or_number}/withdrawals' + \`\`\` + ` : undefined; + + const ARBITRUM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' ? ` + ### Latest Committed Batch Number + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/main-page/arbitrum/batches/latest-number' + \`\`\` + + ### Batch Info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/arbitrum/batches/{batch_number}' + \`\`\` + + ### Blocks By Batch + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/blocks/arbitrum-batch/{batch_number}' + \`\`\` + + ### Get ${parentToCurrentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/arbitrum/messages/to-rollup' + \`\`\` + + ### Get ${currentToParentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/arbitrum/messages/from-rollup' + \`\`\` + + ### ${currentToParentLayerLabel} messages by transaction: + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/arbitrum/messages/withdrawals/{transactions_hash}' + \`\`\` + ` : undefined; + + const OPTIMISM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'optimistic' ? ` + ### Latest Committed Batch Number (top of) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/optimism/batches' + \`\`\` + + ### Batch Info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/optimism/batches/{batch_number}' + \`\`\` + + ### Blocks By Batch (TODO: not needed if the batch returns blocks range) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/blocks/optimism-batch/{batch_number}' + \`\`\` + + ### Dispute Games + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/optimism/games' + \`\`\` + + ### Get ${parentToCurrentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/optimism/deposits' + \`\`\` + + ### Get ${currentToParentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/optimism/withdrawals' + \`\`\` + ` : undefined; + + const CELO_CHAIN_TEMPLATE = config.features.celo.isEnabled ? ` + ### Latest Finalized Epoch (top of) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/celo/epochs' + \`\`\` + + ### Get Epoch Information + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/celo/epochs/{epoch_number}' + \`\`\` + + ### Validator Group Reward by Epoch + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/celo/epochs/{epoch_number}/election-rewards/group' + \`\`\` + + ### Validator Rewards by Epoch + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/celo/epochs/{epoch_number}/election-rewards/validator' + \`\`\` + + ### Voting Rewards by Epoch + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/celo/epochs/{epoch_number}/election-rewards/voter' + \`\`\` + ` : undefined; + + const ZKSYNC_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'zkSync' ? ` + ### Latest Committed Batch Number + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/main-page/zksync/batches/latest-number' + \`\`\` + + ### Batch info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/zksync/batches/{batch_number}' + \`\`\` + ` : undefined; + + const TAC_CHAIN_TEMPLATE = config.features.tac.isEnabled && config.apis.tac ? ` + > **Note:** TAC operations endpoints below are served by a separate microservice and are **not** part of the Blockscout PRO API. The \`Authorization: Bearer {api_key}\` requirement stated in the introduction does not apply here. + + ### TAC Operations: + + \`\`\`bash + curl --request GET --url '${config.apis.tac.endpoint}/api/v1/tac/operations' + \`\`\` + + ### TAC Operation Info: + + \`\`\`bash + curl --request GET --url '${config.apis.tac.endpoint}/api/v1/tac/operations/{operation_id}' + \`\`\` + ` : undefined; + + const REDSTONE_CHAIN_TEMPLATE = config.features.mudFramework.isEnabled ? ` + ### MUD Worlds + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/mud/worlds' + \`\`\` + + ### MUD World Tables + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/mud/worlds/{contract_address}/tables' + \`\`\` + + ### MUD World Table Records + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/mud/worlds/{contract_address}/tables/{table_id}/records' + \`\`\` + + ### MUD World Table Record + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/mud/worlds/{contract_address}/tables/{table_id}/records/{record_id}' + \`\`\` + ` : undefined; + + const SCROLL_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'scroll' ? ` + ### Latest Committed Batch Number (top of) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/scroll/batches' + \`\`\` + + ### Batch Info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/scroll/batches/{batch_number}' + \`\`\` + + ### Blocks By Batch + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/blocks/scroll-batch/{batch_number}' + \`\`\` + + ### Deposits (${parentToCurrentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/scroll/deposits' + \`\`\` + + ### Withdrawals (${currentToParentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/scroll/withdrawals' + \`\`\` + ` : undefined; + + // Shibarium endpoints are not yet listed in the PRO API index (tracked in blockscout/blockscout#14322); the indexer exposes them and the OpenAPI listing is forthcoming. + const SHIBARIUM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'shibarium' ? ` + ### Deposits (${parentToCurrentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/shibarium/deposits' + \`\`\` + + ### Withdrawals (${currentToParentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/shibarium/withdrawals' + \`\`\` + ` : undefined; + + const ZILLIQA_CHAIN_TEMPLATE = validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa' ? ` + ### Validators list: + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/validators/zilliqa' + \`\`\` + + ### Validator Info: + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/validators/zilliqa/{validator_public_key}' + \`\`\` + ` : undefined; + + // Stability validators endpoint is not yet listed in the PRO API index (tracked in blockscout/blockscout#14323); the indexer exposes it and the OpenAPI listing is forthcoming. + const STABILITY_CHAIN_TEMPLATE = validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability' ? ` + ### Validators list: + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/validators/stability' + \`\`\` + ` : undefined; + + const CHAIN_SPECIFIC_DATA = [ + BEACON_CHAIN_TEMPLATE, + ARBITRUM_CHAIN_TEMPLATE, + OPTIMISM_CHAIN_TEMPLATE, + CELO_CHAIN_TEMPLATE, + ZKSYNC_CHAIN_TEMPLATE, + TAC_CHAIN_TEMPLATE, + REDSTONE_CHAIN_TEMPLATE, + SCROLL_CHAIN_TEMPLATE, + ZILLIQA_CHAIN_TEMPLATE, + STABILITY_CHAIN_TEMPLATE, + SHIBARIUM_CHAIN_TEMPLATE, + ].filter(Boolean); + + const CHAIN_SPECIFIC_TEMPLATE = CHAIN_SPECIFIC_DATA.length > 0 ? ` + ## Chain-Specific Data + ${ CHAIN_SPECIFIC_DATA.join('\n') } + ` : '{blank}'; + + const MAIN_TEMPLATE = dedent` + # Blockscout - ${chainName} + + Blockscout is a human-friendly blockchain explorer for EVM-compatible networks. It lets users browse blocks, transactions, addresses, tokens (ERC-20/721/1155), logs, contract ABIs, and decoded contract interactions. While the site is primarily designed for people, all rendered information is backed by API endpoints that machines can consume. + + All endpoint examples below target the **Blockscout PRO API** at \`${PRO_API_URL}\` — a single HTTP API spanning 100+ EVM chains. Every request requires the header \`Authorization: Bearer {api_key}\`; for brevity it is omitted from the individual \`curl\` examples and must be added by the caller. Generate an API key at the Blockscout developer portal: \`https://dev.blockscout.com\` (free tier, no credit card required). For the full set of conventions (additional headers, response shape, pagination, plan limits) consult the public \`web3-dev\` agent skill linked from the "Additional Info" section at the bottom of this document. + + Chain name: ${chainName} + Chain ID: ${chainId} + ${parentChainUrl ? `Settlement layer Blockscout URL: ${parentChainUrl}` : '{blank}'} + + ## General Blockchain Data + + ### Specific Block Info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/blocks/{block_hash_or_number}' + \`\`\` + + ### Specific Transaction Info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/transactions/{transaction_hash}' + \`\`\` + + ### Get Transaction Logs + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/transactions/{transaction_hash}/logs' + \`\`\` + + ### Transaction Summary + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/transactions/{transaction_hash}/summary' + \`\`\` + + ### Specific Account Info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}' + \`\`\` + + ### Get Address by ENS Name + + Cross-chain ENS resolution is served by the multichain aggregator service; the path intentionally has no \`{chain_id}\` segment. + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/services/multichain/api/v1/search:quick?q={ens_name}' + \`\`\` + + ### Get Transactions By Address + + Use the \`advanced-filters\` endpoint with \`address_relation=or\` to match the address on either side of a transaction. + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/advanced-filters?from_address_hashes_to_include={account_address}&to_address_hashes_to_include={account_address}&address_relation=or&transaction_types=COIN_TRANSFER,CONTRACT_INTERACTION,CONTRACT_CREATION&age_from={YYYY-MM-DDTHH:MM:SSZ}&age_to={YYYY-MM-DDTHH:MM:SSZ}&methods={comma_separated_4byte_selectors_optional}' + \`\`\` + + ### Get Token Transfers by Address + + Use the \`advanced-filters\` endpoint with token-flavoured \`transaction_types\` and \`address_relation=or\`. + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/advanced-filters?from_address_hashes_to_include={account_address}&to_address_hashes_to_include={account_address}&address_relation=or&transaction_types=ERC-20,ERC-721,ERC-1155&age_from={YYYY-MM-DDTHH:MM:SSZ}&age_to={YYYY-MM-DDTHH:MM:SSZ}&token_contract_address_hashes_to_include={token_contract_address_optional}' + \`\`\` + + ### Get ERC20 Tokens By Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/tokens' + \`\`\` + + ### Get NFT Tokens By Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/nft' + \`\`\` + + ### Lookup Token By Symbol + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/search/quick?q={token_symbol}' + \`\`\` + + ### Get Contract ABI + + The verified-contract response includes the ABI as a field; no separate ABI-only endpoint is needed. + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/smart-contracts/{contract_address}' + \`\`\` + + ### Read Contract Data + + Read calls go through the JSON-RPC gateway as \`eth_call\`. This is the only example in this document that uses \`POST\`. + + \`\`\`bash + curl --request POST --url '${PRO_API_URL}/${chainId}/json-rpc' --header 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"{contract_address}","data":"{encoded_calldata}"},"latest"]}' + \`\`\` + + ### Inspect Source Tree of Verified Contract + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/smart-contracts/{contract_address}' + \`\`\` + + ## Miscellaneous Blockchain Data + + ${GENERAL_COUNTERS_TEMPLATE} + ### Gas Tracker + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/stats' + \`\`\` + + ### Coin Balance History by Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/coin-balance-history-by-day' + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/coin-balance-history' + \`\`\` + + ### Logs Emitted by Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/logs' + \`\`\` + + ### Blocks Validated by Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/addresses/{account_address}/blocks-validated' + \`\`\` + ${USER_OPS_TEMPLATE} + ### Holders By Token Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/tokens/{token_contract_address}/holders' + \`\`\` + + ### NFT Inventory By Token Address + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/tokens/{token_contract_address}/instances' + \`\`\` + + ### NFT Instance Info + + \`\`\`bash + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/tokens/{token_contract_address}/instances/{instance_id}' + curl --request GET --url '${PRO_API_URL}/${chainId}/api/v2/tokens/{token_contract_address}/instances/{instance_id}/transfers' + \`\`\` + ${CHAIN_SPECIFIC_TEMPLATE} + ## Additional Info + + ### Blockscout PRO API — deterministic workflow construction + + The Blockscout PRO API is the recommended surface for building repeatable services, tools, and automations on top of indexed blockchain data. A single HTTP API spans 100+ EVM chains; one Bearer token authenticates every call. + + - Generate an API key at the Blockscout developer portal: https://dev.blockscout.com (free tier, no credit card). + - For AI agents, install the \`web3-dev\` skill — it encodes the conventions, headers, pagination, and pre-flight checks needed to call the PRO API correctly. Installation instructions: https://raw.githubusercontent.com/blockscout/agent-skills/refs/heads/main/README.md + + ### Blockscout MCP server — ad-hoc agent interaction + + The Blockscout MCP server is for open-ended, exploratory interaction with chains: ask, inspect, follow clues, explain findings. Use it when an agent needs to reason over data on the fly rather than encode a fixed workflow. + + - MCP endpoint: https://mcp.blockscout.com/mcp/ + - For AI agents, install the \`blockscout-analysis\` skill — it provides the architectural rules, REST API conventions for scripts, endpoint reference files, and response-transformation guidance the MCP surface assumes. Installation instructions: https://raw.githubusercontent.com/blockscout/agent-skills/refs/heads/main/README.md + + ### Other Blockscout instances (chain registry) + + Look up other Blockscout instances by chain id or name: + + \`\`\`bash + curl --request GET --url 'https://chains.blockscout.com/api/chains' + \`\`\` + `; + + return MAIN_TEMPLATE.replace('{blank}\n', ''); +} diff --git a/deploy/tools/llms-txt-generator/generate-standard.ts b/deploy/tools/llms-txt-generator/generate-standard.ts new file mode 100644 index 0000000000..4fd2cdb98f --- /dev/null +++ b/deploy/tools/llms-txt-generator/generate-standard.ts @@ -0,0 +1,494 @@ +import config from 'configs/app'; +import dedent from 'dedent'; +import { layerLabels } from 'lib/rollups/utils'; + +const MCP_SERVER_URL = 'https://mcp.blockscout.com'; + +export function generateStandard(): string { + const chainName = config.chain.name ?? ''; + const chainId = config.chain.id ?? ''; + const generalApiUrl = config.apis.general ? config.apis.general.endpoint + config.apis.general.basePath : ''; + const statsApiUrl = config.apis.stats ? config.apis.stats.endpoint + config.apis.stats.basePath : undefined; + + const rollupFeature = config.features.rollup; + const parentChainUrl = rollupFeature.isEnabled ? rollupFeature.parentChain.baseUrl : undefined; + const currentToParentLayerLabel = layerLabels.current + '→' + layerLabels.parent; + const parentToCurrentLayerLabel = layerLabels.parent + '→' + layerLabels.current; + + const validatorsFeature = config.features.validators; + + const GENERAL_COUNTERS_TEMPLATE = statsApiUrl ? ` + ### General Counters + + \`\`\`bash + curl --request GET --url '${statsApiUrl}/api/v1/counters' + \`\`\` + ` : '{blank}'; + + const USER_OPS_TEMPLATE = config.features.userOps.isEnabled ? ` + ### Account Abstraction info + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/proxy/account-abstraction/accounts/{account_address}' + \`\`\` + + ### User Operations by Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/proxy/account-abstraction/operations?sender={account_address}' + \`\`\` + + ### User Operations by Transaction + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/proxy/account-abstraction/operations?transaction_hash={transaction_hash}' + \`\`\` + + ### User Operation Details + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/proxy/account-abstraction/operations/{user_operation_hash}' + \`\`\` + ` : '{blank}'; + + const BEACON_CHAIN_TEMPLATE = config.features.beaconChain.isEnabled ? ` + ### Deposits by Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/addresses/{account_address}/beacon/deposits' + \`\`\` + + ### Deposits by Block + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/blocks/{block_hash_or_number}/beacon/deposits' + \`\`\` + + ### Withdrawals by Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/addresses/{account_address}/withdrawals' + \`\`\` + + ### Withdrawals by Block + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/blocks/{block_number}/withdrawals' + \`\`\` + ` : undefined; + + const ARBITRUM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' ? ` + ### Latest Committed Batch Number + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/main-page/arbitrum/batches/latest-number' + \`\`\` + + ### Batch Info + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/arbitrum/batches/{batch_number}' + \`\`\` + + ### Blocks By Batch + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/blocks/arbitrum-batch/{batch_number}' + \`\`\` + + ### Get ${parentToCurrentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/arbitrum/messages/to-rollup' + \`\`\` + + ### Get ${currentToParentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/arbitrum/messages/from-rollup' + \`\`\` + + ### ${currentToParentLayerLabel} messages by transaction: + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/arbitrum/messages/withdrawals/{transactions_hash}' + \`\`\` + ` : undefined; + + const OPTIMISM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'optimistic' ? ` + ### Latest Committed Batch Number (top of) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/optimism/batches' + \`\`\` + + ### Batch Info + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/optimism/batches/{batch_number}' + \`\`\` + + ### Blocks By Batch (TODO: not needed if the batch returns blocks range) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/blocks/optimism-batch/{batch_number}' + \`\`\` + + ### Dispute Games + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/optimism/games' + \`\`\` + + ### Get ${parentToCurrentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/optimism/deposits' + \`\`\` + + ### Get ${currentToParentLayerLabel} messages + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/optimism/withdrawals' + \`\`\` + ` : undefined; + + const CELO_CHAIN_TEMPLATE = config.features.celo.isEnabled ? ` + ### Latest Finalized Epoch (top of) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/celo/epochs' + \`\`\` + + ### Get Epoch Information + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/celo/epochs/{epoch_number}' + \`\`\` + + ### Validator Group Reward by Epoch + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/celo/epochs/{epoch_number}/election-rewards/group' + \`\`\` + + ### Validator Rewards by Epoch + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/celo/epochs/{epoch_number}/election-rewards/validator' + \`\`\` + + ### Voting Rewards by Epoch + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/celo/epochs/{epoch_number}/election-rewards/voter' + \`\`\` + ` : undefined; + + const ZKSYNC_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'zkSync' ? ` + ### Latest Committed Batch Number + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/main-page/zksync/batches/latest-number' + \`\`\` + + ### Batch info + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/zksync/batches/{batch_number}' + \`\`\` + ` : undefined; + + const TAC_CHAIN_TEMPLATE = config.features.tac.isEnabled && config.apis.tac ? ` + ### TAC Operations: + + \`\`\`bash + curl --request GET --url '${config.apis.tac.endpoint}/api/v1/tac/operations' + \`\`\` + + ### TAC Operation Info: + + \`\`\`bash + curl --request GET --url '${config.apis.tac.endpoint}/api/v1/tac/operations/{operation_id}' + \`\`\` + ` : undefined; + + const REDSTONE_CHAIN_TEMPLATE = config.features.mudFramework.isEnabled ? ` + ### MUD Worlds + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/mud/worlds' + \`\`\` + + ### MUD World Tables + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/mud/worlds/{contract_address}/tables' + \`\`\` + + ### MUD World Table Records + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/mud/worlds/{contract_address}/tables/{table_id}/records' + \`\`\` + + ### MUD World Table Record + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/mud/worlds/{contract_address}/tables/{table_id}/records/{record_id}' + \`\`\` + ` : undefined; + + const SCROLL_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'scroll' ? ` + ### Latest Committed Batch Number (top of) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/scroll/batches' + \`\`\` + + ### Batch Info + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/scroll/batches/{batch_number}' + \`\`\` + + ### Blocks By Batch + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/blocks/scroll-batch/{batch_number}' + \`\`\` + + ### Deposits (${parentToCurrentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/scroll/deposits' + \`\`\` + + ### Withdrawals (${currentToParentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/scroll/withdrawals' + \`\`\` + ` : undefined; + + const SHIBARIUM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'shibarium' ? ` + ### Deposits (${parentToCurrentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/shibarium/deposits' + \`\`\` + + ### Withdrawals (${currentToParentLayerLabel}) + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/shibarium/withdrawals' + \`\`\` + ` : undefined; + + const ZILLIQA_CHAIN_TEMPLATE = validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa' ? ` + ### Validators list: + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/validators/zilliqa' + \`\`\` + + ### Validator Info: + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/validators/zilliqa/{validator_public_key}' + \`\`\` + ` : undefined; + + const STABILITY_CHAIN_TEMPLATE = validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability' ? ` + ### Validators list: + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/validators/stability' + \`\`\` + ` : undefined; + + const CHAIN_SPECIFIC_DATA = [ + BEACON_CHAIN_TEMPLATE, + ARBITRUM_CHAIN_TEMPLATE, + OPTIMISM_CHAIN_TEMPLATE, + CELO_CHAIN_TEMPLATE, + ZKSYNC_CHAIN_TEMPLATE, + TAC_CHAIN_TEMPLATE, + REDSTONE_CHAIN_TEMPLATE, + SCROLL_CHAIN_TEMPLATE, + ZILLIQA_CHAIN_TEMPLATE, + STABILITY_CHAIN_TEMPLATE, + SHIBARIUM_CHAIN_TEMPLATE, + ].filter(Boolean); + + const CHAIN_SPECIFIC_TEMPLATE = CHAIN_SPECIFIC_DATA.length > 0 ? ` + ## Chain-Specific Data + ${ CHAIN_SPECIFIC_DATA.join('\n') } + ` : '{blank}'; + + const MAIN_TEMPLATE = dedent` + # Blockscout - ${chainName} + + Blockscout is a human-friendly blockchain explorer for EVM-compatible networks. It lets users browse blocks, transactions, addresses, tokens (ERC-20/721/1155), logs, contract ABIs, and decoded contract interactions. While the site is primarily designed for people, all rendered information is backed by API endpoints that machines can consume. Large Language Models should prefer the LLM-ready endpoints listed below to retrieve precise, structured data. + + Chain name: ${chainName} + Chain ID: ${chainId} + ${parentChainUrl ? `Settlement layer Blockscout URL: ${parentChainUrl}` : '{blank}'} + + ## General LLM-ready Data + + The links below lead to the REST API of the Blockscout MCP server, they are considered as LLM-ready. More detail: https://github.com/blockscout/mcp-server/blob/main/API.md + + ### Specific Block Info + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_block_info?chain_id=${chainId}&number_or_hash={block_number_or_hash}&include_transactions=false' + \`\`\` + + ### Specific Transaction Info + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_transaction_info?chain_id=${chainId}&transaction_hash={transaction_hash}' + \`\`\` + + ### Get Transaction Logs + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_transaction_logs?chain_id=${chainId}&transaction_hash={transaction_hash}' + \`\`\` + + ### Transaction Summary + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/transaction_summary?chain_id=${chainId}&transaction_hash={transaction_hash}' + \`\`\` + + ### Specific Account info + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_address_info?chain_id=${chainId}&address={account_address}' + \`\`\` + + ### Get Address by ENS name + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_address_by_ens_name?name={ens_name}' + \`\`\` + + ### Get Transactions By Address + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_transactions_by_address?chain_id=${chainId}&address={account_address}&age_from={YYYY-MM-DDTHH:MM:SS.00Z}&age_to={YYYY-MM-DDTHH:MM:SS.00Z}&methods={4_bytes_method_signature_optional}' + \`\`\` + + ### Get Token Transfers by Address + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_token_transfers_by_address?chain_id=${chainId}&address={account_address}&age_from={YYYY-MM-DDTHH:MM:SS.00Z}&age_to={YYYY-MM-DDTHH:MM:SS.00Z}&token={token_contract_address_optional}' + \`\`\` + + ### Get ERC20 Tokens By Address + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_tokens_by_address?chain_id=${chainId}&address={account_address}' + \`\`\` + + ### Get NFT Tokens By Address + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/nft_tokens_by_address?chain_id=${chainId}&address={account_address}' + \`\`\` + + ### Lookup Token By Symbol + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/lookup_token_by_symbol?chain_id=${chainId}&symbol={token_symbol}' + \`\`\` + + ### Get Contract ABI + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/get_contract_abi?chain_id=${chainId}&address={contract_address}' + \`\`\` + + ### Read Contract Data + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/read_contract?chain_id=${chainId}&address={contract_address}&abi={string_encoded_function_abi}&function_name={function_name}&args={string_encoded_arguments_list}' + \`\`\` + + ### Get Source Files Tree of Verified Contract + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/inspect_contract_code?chain_id=${chainId}&address={contract_address}' + \`\`\` + + ### Inspect Specific Source File of Verified Contract + + \`\`\`bash + curl --request GET --url '${MCP_SERVER_URL}/v1/inspect_contract_code?chain_id=${chainId}&address={contract_address}&file_name={file_name}' + \`\`\` + + ## Miscellaneous Blockchain Data + + ${GENERAL_COUNTERS_TEMPLATE} + ### Gas Tracker + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/stats' + \`\`\` + + ### Coin Balance History by Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/addresses/{account_address}/coin-balance-history-by-day' + curl --request GET --url '${generalApiUrl}/api/v2/addresses/{account_address}/coin-balance-history' + \`\`\` + + ### Logs Emitted by Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/addresses/{account_address}/logs' + \`\`\` + + ### Blocks Validated by Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/addresses/{account_address}/blocks-validated' + \`\`\` + ${USER_OPS_TEMPLATE} + ### Holders By Token Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/tokens/{token_contract_address}/holders' + \`\`\` + + ### NFT Inventory By Token Address + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/tokens/{token_contract_address}/instances' + \`\`\` + + ### NFT Instance Info + + \`\`\`bash + curl --request GET --url '${generalApiUrl}/api/v2/tokens/{token_contract_address}/instances/{instance_id}' + curl --request GET --url '${generalApiUrl}/api/v2/tokens/{token_contract_address}/instances/{instance_id}/transfers' + \`\`\` + ${CHAIN_SPECIFIC_TEMPLATE} + ## Additional Info + + **Recommendation**: For programmatic access and LLM workflows, prefer the MCP Server endpoints (HTTP, not SSE). + + - MCP landing: [${MCP_SERVER_URL}](${MCP_SERVER_URL}) + - MCP server root: [${MCP_SERVER_URL}/mcp/](${MCP_SERVER_URL}/mcp/) + + ### Other Blockscout instances (lookup by chain id or name): + + \`\`\`bash + curl --request GET --url 'https://chains.blockscout.com/api/chains' + \`\`\` + `; + + return MAIN_TEMPLATE.replace('{blank}\n', ''); +} diff --git a/deploy/tools/llms-txt-generator/index.ts b/deploy/tools/llms-txt-generator/index.ts index 2bcaecdd68..658ff7fc80 100644 --- a/deploy/tools/llms-txt-generator/index.ts +++ b/deploy/tools/llms-txt-generator/index.ts @@ -2,570 +2,29 @@ import config from 'configs/app'; import { writeFileSync } from 'node:fs'; import { dirname, resolve as resolvePath } from 'node:path'; import { fileURLToPath } from 'node:url'; -import dedent from 'dedent'; -import { layerLabels } from 'lib/rollups/utils'; + +import { generateProApi } from './generate-pro-api'; +import { generateStandard } from './generate-standard'; const currentFilePath = fileURLToPath(import.meta.url); const currentDir = dirname(currentFilePath); const outputDir = resolvePath(currentDir, '../../../../public'); const outputFile = resolvePath(outputDir, 'llms.txt'); -function run() { +function run(): void { try { - if(config.features.multichain.isEnabled){ + if (config.features.multichain.isEnabled) { console.log('⏭️ Skipping llms.txt generation for multichain explorer'); return; } - console.log('🌀 Generating llms.txt...'); - - const chainName = config.chain.name ?? ''; - const chainId = config.chain.id ?? ''; - const generalApiUrl = config.apis.general? config.apis.general.endpoint + config.apis.general.basePath : ''; - const statsApiUrl = config.apis.stats ? config.apis.stats.endpoint + config.apis.stats.basePath : undefined; - - const rollupFeature = config.features.rollup; - const parentChainUrl = rollupFeature.isEnabled ? rollupFeature.parentChain.baseUrl : undefined; - const currentToParentLayerLabel = layerLabels.current + '→' + layerLabels.parent; - const parentToCurrentLayerLabel = layerLabels.parent + '→' + layerLabels.current; - - const validatorsFeature = config.features.validators; - - const MCP_SERVER_URL = 'https://mcp.blockscout.com'; - - const GENERAL_COUNTERS_TEMPLATE = statsApiUrl ? ` - ### General Counters - - \`\`\`bash - curl --request GET --url '${statsApiUrl}/api/v1/counters' - \`\`\` - ` : '{blank}'; - - const USER_OPS_TEMPLATE = config.features.userOps.isEnabled ? ` - - ### Account Abstraction info - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/proxy/account-abstraction/accounts/{account_address}' - \`\`\` - - ### User Operations by Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/proxy/account-abstraction/operations?sender={account_address}' - \`\`\` - - ### User Operations by Transaction - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/proxy/account-abstraction/operations?transaction_hash={transaction_hash}' - \`\`\` - - ### User Operation Details - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/proxy/account-abstraction/operations/{user_operation_hash}' - \`\`\` - - ` : '{blank}'; - - const BEACON_CHAIN_TEMPLATE = config.features.beaconChain.isEnabled ? ` - ### Deposits by Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/addresses/{account_address}/beacon/deposits' - \`\`\` - - ### Deposits by Block - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/block/{block_number}/beacon/deposits' - \`\`\` - - ### Withdrawals by Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/addresses/{account_address}/withdrawals' - \`\`\` - - ### Withdrawals by Block - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/blocks/{block_number}/withdrawals' - \`\`\` - ` : undefined; - - const ARBITRUM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' ? ` - ### Latest Committed Batch Number - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/main-page/arbitrum/batches/latest-number' - \`\`\` - - ### Batch Info - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/arbitrum/batches/{batch_number}' - \`\`\` - - ### Blocks By Batch - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/blocks/arbitrum-batch/{batch_number}' - \`\`\` - - ### Get ${ parentToCurrentLayerLabel } messages - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/arbitrum/messages/to-rollup' - \`\`\` - - ### Get ${ currentToParentLayerLabel } messages - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/arbitrum/messages/from-rollup' - \`\`\` - - ### ${ currentToParentLayerLabel } messages by transaction: - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/arbitrum/messages/withdrawals/{transactions_hash}' - \`\`\` - ` : undefined; - - const OPTIMISM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'optimistic' ? ` - ### Latest Committed Batch Number (top of) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/optimism/batches' - \`\`\` - - ### Batch Info - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/optimism/batches/{batch_number}' - \`\`\` - - ### Blocks By Batch (TODO: not needed if the batch returns blocks range) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/blocks/optimism-batch/{batch_number}' - \`\`\` - - ### Dispute Games - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/optimism/games' - \`\`\` - - ### Get ${ parentToCurrentLayerLabel } messages - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/optimism/deposits' - \`\`\` - - ### Get ${ currentToParentLayerLabel } messages - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/optimism/withdrawals' - \`\`\` - ` : undefined; - - const CELO_CHAIN_TEMPLATE = config.features.celo.isEnabled ? ` - ### Latest Finalized Epoch (top of) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/celo/epochs' - \`\`\` - - ### Get Epoch Information - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/celo/epochs/{epoch_number}' - \`\`\` - - ### Validator Group Reward by Epoch - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/celo/epochs/{epoch_number}/election-rewards/group' - \`\`\` - - ### Validator Rewards by Epoch - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/celo/epochs/{epoch_number}/election-rewards/validator' - \`\`\` - - ### Voting Rewards by Epoch - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/celo/epochs/{epoch_number}/election-rewards/voter' - \`\`\` - ` : undefined; - - const ZKSYNC_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'zkSync' ? ` - ### Latest Committed Batch Number - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/main-page/zksync/batches/latest-number' - \`\`\` - - ### Batch info - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/zksync/batches/{batch_number}' - \`\`\` - ` : undefined; - - const ZKEVM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' ? ` - ### Latest Committed Batch Number (top of) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/zkevm/batches/confirmed' - \`\`\` - - ### Batch Info - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/zkevm/batches/{batch_number}' - \`\`\` - - ### Deposits - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/zkevm/deposits' - \`\`\` - - ### Withdrawals - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/zkevm/withdrawals' - \`\`\` - ` : undefined; - - const TAC_CHAIN_TEMPLATE = config.features.tac.isEnabled && config.apis.tac ? ` - ### TAC Operations: - - \`\`\`bash - curl --request GET --url '${config.apis.tac.endpoint }/api/v1/tac/operations' - \`\`\` - - ### TAC Operation Info: - - \`\`\`bash - curl --request GET --url '${config.apis.tac.endpoint }/api/v1/tac/operations/{operation_id}' - \`\`\` - ` : undefined; - - const REDSTONE_CHAIN_TEMPLATE = config.features.mudFramework.isEnabled ? ` - ### MUD Worlds - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/mud/worlds' - \`\`\` - - ### MUD World Tables - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/mud/worlds/{contract_address}/tables' - \`\`\` - - ### MUD World Table Records - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/mud/worlds/{contract_address}/tables/{table_id}/records' - \`\`\` - - ### MUD World Table Record - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/mud/worlds/{contract_address}/tables/{table_id}/records/{record_id}' - \`\`\` - ` : undefined; - - const SCROLL_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'scroll' ? ` - ### Latest Committed Batch Number (top of) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/scroll/batches' - \`\`\` - - ### Batch Info - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/scroll/batches/{batch_number}' - \`\`\` - - ### Blocks By Batch - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/blocks/scroll-batch/{batch_number}' - \`\`\` - - ### Deposits (${ parentToCurrentLayerLabel }) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/scroll/deposits' - \`\`\` - - ### Withdrawals (${ currentToParentLayerLabel }) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/scroll/withdrawals' - \`\`\` - ` : undefined; - - const SHIBARIUM_CHAIN_TEMPLATE = rollupFeature.isEnabled && rollupFeature.type === 'shibarium' ? ` - ### Deposits (${ parentToCurrentLayerLabel }) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/shibarium/deposits' - \`\`\` - - ### Withdrawals (${ currentToParentLayerLabel }) - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/shibarium/withdrawals' - \`\`\` - ` : undefined; - - const ZILLIQA_CHAIN_TEMPLATE = validatorsFeature.isEnabled && validatorsFeature.chainType === 'zilliqa' ? ` - ### Validators list: - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/validators/zilliqa' - \`\`\` - - ### Validator Info: - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/validators/zilliqa/{validator_public_key}' - \`\`\` - ` : undefined; - - const STABILITY_CHAIN_TEMPLATE = validatorsFeature.isEnabled && validatorsFeature.chainType === 'stability' ? ` - ### Validators list: - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/validators/stability' - \`\`\` - ` : undefined; - - const CHAIN_SPECIFIC_DATA = [ - BEACON_CHAIN_TEMPLATE, - ARBITRUM_CHAIN_TEMPLATE, - OPTIMISM_CHAIN_TEMPLATE, - CELO_CHAIN_TEMPLATE, - ZKSYNC_CHAIN_TEMPLATE, - ZKEVM_CHAIN_TEMPLATE, - TAC_CHAIN_TEMPLATE, - REDSTONE_CHAIN_TEMPLATE, - SCROLL_CHAIN_TEMPLATE, - ZILLIQA_CHAIN_TEMPLATE, - STABILITY_CHAIN_TEMPLATE, - SHIBARIUM_CHAIN_TEMPLATE - ].filter(Boolean); - - const CHAIN_SPECIFIC_TEMPLATE = CHAIN_SPECIFIC_DATA.length > 0 ? ` - - ## Chain-Specific Data - ${ CHAIN_SPECIFIC_DATA.join('\n') } - - ` : '{blank}'; - - const MAIN_TEMPLATE = dedent` - # Blockscout - ${ chainName } - - - Blockscout is a human-friendly blockchain explorer for EVM-compatible networks. It lets users browse blocks, transactions, addresses, tokens (ERC-20/721/1155), logs, contract ABIs, and decoded contract interactions. While the site is primarily designed for people, all rendered information is backed by API endpoints that machines can consume. Large Language Models should prefer the LLM-ready endpoints listed below to retrieve precise, structured data. - - - - Chain name: ${ chainName } - Chain ID: ${ chainId } - ${ parentChainUrl ? `Settlement layer Blockscout URL: ${ parentChainUrl }` : '{blank}' } - - - - ## General LLM-ready Data - - The links below lead to the REST API of the Blockscout MCP server, they are considered as LLM-ready. More detail: https://github.com/blockscout/mcp-server/blob/main/API.md - - - ### Specific Block Info - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_block_info?chain_id=${ chainId }&number_or_hash={block_number_or_hash}&include_transactions=false' - \`\`\` - - - - ### Specific Transaction Info - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_transaction_info?chain_id=${ chainId }&transaction_hash={transaction_hash}' - \`\`\` - - ### Get Transaction Logs - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_transaction_logs?chain_id=${ chainId }&transaction_hash={transaction_hash}' - \`\`\` - - ### Transaction Summary - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/transaction_summary?chain_id=${ chainId }&transaction_hash={transaction_hash}' - \`\`\` - - - - ### Specific Account info - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_address_info?chain_id=${ chainId }&address={account_address}' - \`\`\` - - ### Get Address by ENS name - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_address_by_ens_name?name={ens_name}' - \`\`\` - - ### Get Transactions By Address - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_transactions_by_address?chain_id=${ chainId }&address={account_address}&age_from={YYYY-MM-DDTHH:MM:SS.00Z}&age_to={YYYY-MM-DDTHH:MM:SS.00Z}&methods={4_bytes_method_signature_optional}' - \`\`\` - - ### Get Token Transfers by Address - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_token_transfers_by_address?chain_id=${ chainId }&address={account_address}&age_from={YYYY-MM-DDTHH:MM:SS.00Z}&age_to={YYYY-MM-DDTHH:MM:SS.00Z}&token={token_contract_address_optional}' - \`\`\` - - - - ### Get ERC20 Tokens By Address - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_tokens_by_address?chain_id=${ chainId }&address={account_address}' - \`\`\` - - ### Get NFT Tokens By Address - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/nft_tokens_by_address?chain_id=${ chainId }&address={account_address}' - \`\`\` - - ### Lookup Token By Symbol - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/lookup_token_by_symbol?chain_id=${ chainId }&symbol={token_symbol}' - \`\`\` - - - - ### Get Contract ABI - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/get_contract_abi?chain_id=${ chainId }&address={contract_address}' - \`\`\` - - ### Read Contract Data - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/read_contract?chain_id=${ chainId }&address={contract_address}&abi={string_encoded_function_abi}&function_name={function_name}&args={string_encoded_arguments_list}' - \`\`\` - - ### Get Source Files Tree of Verified Contract - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/inspect_contract_code?chain_id=${ chainId }&address={contract_address}' - \`\`\` - - ### Inspect Specific Source File of Verified Contract - - \`\`\`bash - curl --request GET --url '${ MCP_SERVER_URL }/v1/inspect_contract_code?chain_id=${ chainId }&address={contract_address}&file_name={file_name}' - \`\`\` - - - - - - ## Miscellaneous Blockchain Data - - - ${ GENERAL_COUNTERS_TEMPLATE } - ### Gas Tracker - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/stats' - \`\`\` - - - - ### Coin Balance History by Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/addresses/{account_address}/coin-balance-history-by-day' - curl --request GET --url '${ generalApiUrl }/api/v2/addresses/{account_address}/coin-balance-history' - \`\`\` - - ### Logs Emitted by Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/addresses/{account_address}/logs' - \`\`\` - - ### Blocks Validated by Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/addresses/{account_address}/blocks-validated' - \`\`\` - - ${USER_OPS_TEMPLATE} - - ### Holders By Token Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/tokens/{token_contract_address}/holders' - \`\`\` - - ### NFT Inventory By Token Address - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/tokens/{token_contract_address}/instances' - \`\`\` - - ### NFT Instance Info - - \`\`\`bash - curl --request GET --url '${ generalApiUrl }/api/v2/tokens/{token_contract_address}/instances/{instance_id}' - curl --request GET --url '${ generalApiUrl }/api/v2/tokens/{token_contract_address}/instances/{instance_id}/transfers' - \`\`\` - - - - ${ CHAIN_SPECIFIC_TEMPLATE } - - ## Additional Info - - **Recommendation**: For programmatic access and LLM workflows, prefer the MCP Server endpoints (HTTP, not SSE). - - - MCP landing: [${ MCP_SERVER_URL }](${ MCP_SERVER_URL }) - - MCP server root: [${ MCP_SERVER_URL }/mcp/](${ MCP_SERVER_URL }/mcp/) - - ### Other Blockscout instances (lookup by chain id or name): + const accountFeature = config.features.account; + const isInProApi = accountFeature.isEnabled && accountFeature.apiKeysButton === false; + const mode = isInProApi ? 'PRO API' : 'standard'; - \`\`\`bash - curl --request GET --url 'https://chains.blockscout.com/api/chains' - \`\`\` - - `; + console.log(`🌀 Generating llms.txt (${ mode } mode)...`); - const content = MAIN_TEMPLATE.replace('{blank}\n', ''); + const content = isInProApi ? generateProApi() : generateStandard(); writeFileSync(outputFile, content); console.log('👍 Done!\n'); @@ -576,4 +35,4 @@ function run() { } } -run(); \ No newline at end of file +run(); diff --git a/deploy/tools/llms-txt-generator/package.json b/deploy/tools/llms-txt-generator/package.json index 0dd609c27c..b45c60b34b 100644 --- a/deploy/tools/llms-txt-generator/package.json +++ b/deploy/tools/llms-txt-generator/package.json @@ -17,6 +17,6 @@ "@types/node": "22.12.0", "dotenv-cli": "10.0.0", "typescript": "5.4.2", - "vite": "6.4.1" + "vite": "6.4.2" } } diff --git a/deploy/tools/llms-txt-generator/vite.config.ts b/deploy/tools/llms-txt-generator/vite.config.ts index 849c0da483..3946e9faaf 100644 --- a/deploy/tools/llms-txt-generator/vite.config.ts +++ b/deploy/tools/llms-txt-generator/vite.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ }, resolve: { alias: { + client: resolve(__dirname, '../../../client'), configs: resolve(__dirname, '../../../configs'), lib: resolve(__dirname, '../../../lib'), toolkit: resolve(__dirname, '../../../toolkit'), diff --git a/deploy/tools/multichain-config-generator/package.json b/deploy/tools/multichain-config-generator/package.json index 5d33b836bf..d044ff1576 100644 --- a/deploy/tools/multichain-config-generator/package.json +++ b/deploy/tools/multichain-config-generator/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "typescript": "5.4.2", - "vite": "6.4.1", + "vite": "6.4.2", "@types/node": "22.12.0" } } \ No newline at end of file diff --git a/deploy/tools/multichain-config-generator/vite.config.ts b/deploy/tools/multichain-config-generator/vite.config.ts index 5d0f671c65..c83014624b 100644 --- a/deploy/tools/multichain-config-generator/vite.config.ts +++ b/deploy/tools/multichain-config-generator/vite.config.ts @@ -23,6 +23,7 @@ export default defineConfig({ }, resolve: { alias: { + client: resolve(__dirname, '../../../client'), configs: resolve(__dirname, '../../../configs'), lib: resolve(__dirname, '../../../lib'), toolkit: resolve(__dirname, '../../../toolkit'), diff --git a/deploy/tools/sitemap-generator/next-sitemap.config.js b/deploy/tools/sitemap-generator/next-sitemap.config.js index 753ff474cd..ac6d1e5429 100644 --- a/deploy/tools/sitemap-generator/next-sitemap.config.js +++ b/deploy/tools/sitemap-generator/next-sitemap.config.js @@ -59,7 +59,7 @@ module.exports = { { userAgent: '*', allow: '/', - disallow: ['/auth/*', '/login', '/chakra', '/sprite', '/account/*', '/csv-export'], + disallow: ['/auth/*', '/login', '/chakra', '/sprite', '/account/*'], }, ], }, @@ -71,7 +71,6 @@ module.exports = { '/login', '/sprite', '/chakra', - '/csv-export', ], transform: async(config, path) => { switch (path) { diff --git a/deploy/values/review-l2/values.yaml.gotmpl b/deploy/values/review-2/values.yaml.gotmpl similarity index 64% rename from deploy/values/review-l2/values.yaml.gotmpl rename to deploy/values/review-2/values.yaml.gotmpl index 1a3dc3bf90..8d7a0b10f5 100644 --- a/deploy/values/review-l2/values.yaml.gotmpl +++ b/deploy/values/review-2/values.yaml.gotmpl @@ -4,16 +4,16 @@ imagePullSecrets: - name: regcred config: network: - id: 420 - name: "Base" - shortname: Base + id: 11155111 + name: Blockscout + shortname: Blockscout currency: name: Ether symbol: ETH decimals: 18 account: enabled: true - testnet: true + testnet: false blockscout: enabled: false stats: @@ -23,7 +23,7 @@ frontend: replicaCount: 1 image: repository: ghcr.io/blockscout/frontend-private - tag: review-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} + tag: review-2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} pullPolicy: Always ingress: enabled: true @@ -38,7 +38,7 @@ frontend: nginx.ingress.kubernetes.io/proxy-buffer-size: "128k" nginx.ingress.kubernetes.io/proxy-buffers-number: "8" cert-manager.io/cluster-issuer: "zerossl-prod" - hostname: review-l2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }}.k8s-dev.blockscout.com + hostname: review-2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }}.k8s-dev.blockscout.com resources: limits: @@ -52,6 +52,9 @@ frontend: NEXT_PUBLIC_USE_NEXT_JS_PROXY: true SKIP_ENVS_VALIDATION: true envFromSecret: - NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID - NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY: ref+vault://deployment-values/blockscout/eth-sepolia/testnet?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/RE_CAPTCHA_CLIENT_KEY + NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID + NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID + NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN + NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY: ref+vault://deployment-values/blockscout/eth-sepolia/testnet?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/RE_CAPTCHA_CLIENT_KEY + NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_ROLLBAR_CLIENT_TOKEN \ No newline at end of file diff --git a/deploy/values/review/values.yaml.gotmpl b/deploy/values/review/values.yaml.gotmpl index 66c47c7274..9967924e80 100644 --- a/deploy/values/review/values.yaml.gotmpl +++ b/deploy/values/review/values.yaml.gotmpl @@ -13,7 +13,7 @@ config: decimals: 18 account: enabled: true - testnet: true + testnet: false blockscout: enabled: false stats: @@ -49,36 +49,10 @@ frontend: cpu: 250m env: NEXT_PUBLIC_APP_ENV: review - NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-sepolia.json - NEXT_PUBLIC_FEATURED_NETWORKS_ALL_LINK: https://chains.blockscout.com/ - NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg - NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png NEXT_PUBLIC_API_HOST: eth-sepolia.k8s-dev.blockscout.com - NEXT_PUBLIC_STATS_API_HOST: https://stats-sepolia.k8s-dev.blockscout.com/ - NEXT_PUBLIC_VISUALIZE_API_HOST: http://visualizer-svc.visualizer-testing.svc.cluster.local/ - NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com - NEXT_PUBLIC_ADMIN_SERVICE_API_HOST: https://admin-rs-test.k8s-dev.blockscout.com - NEXT_PUBLIC_NAME_SERVICE_API_HOST: https://bens-rs-test.k8s-dev.blockscout.com - NEXT_PUBLIC_METADATA_SERVICE_API_HOST: https://metadata-test.k8s-dev.blockscout.com - NEXT_PUBLIC_HOMEPAGE_CHARTS: "['daily_txs','coin_price','market_cap']" NEXT_PUBLIC_NETWORK_RPC_URL: https://eth-sepolia.public.blastapi.io - NEXT_PUBLIC_NETWORK_EXPLORERS: "[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','logo':'https://github.com/blockscout/frontend-configs/blob/main/configs/explorer-logos/etherscan.png?raw=true','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]" - NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE: gradient_avatar - NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED: true NEXT_PUBLIC_USE_NEXT_JS_PROXY: true - NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES: "[{'name':'LooksRare','collection_url':'https://goerli.looksrare.org/collections/{hash}','instance_url':'https://goerli.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]" - NEXT_PUBLIC_HAS_USER_OPS: true - NEXT_PUBLIC_CONTRACT_CODE_IDES: "[{'title':'Remix IDE','url':'https://remix.blockscout.com/?address={hash}&blockscout=eth-goerli.blockscout.com','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]" - NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER: blockscout - NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS: true - NEXT_PUBLIC_AD_BANNER_PROVIDER: slise - NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true - NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']" PROMETHEUS_METRICS_ENABLED: true - NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true - NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED: true - NEXT_PUBLIC_CLUSTERS_API_HOST: https://api.clusters.xyz - NEXT_PUBLIC_CLUSTERS_CDN_URL: https://cdn.clusters.xyz envFromSecret: NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index a6f17fc3de..f1b652e6bd 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -100,7 +100,7 @@ These are the steps that you have to follow to make everything work: 4. For local development purposes add the variable with its appropriate values to pre-defined ENV configs `configs/envs` where it is needed 5. Add the variable to CI configs where it is needed - `deploy/values/review/values.yaml.gotmpl` - review development environment - - `deploy/values/review-l2/values.yaml.gotmpl` - review development environment for L2 networks + - `deploy/values/review-2/values.yaml.gotmpl` - review development environment (second instance) 6. If your variable is meant to receive a link to some external resource (image or JSON-config file), extend the array `ASSETS_ENVS` in `deploy/scripts/download_assets.sh` with your variable name 7. Add validation schema for the new variable into the file `deploy/tools/envs-validator/schema.ts` 8. Check if modified validation schema is valid by doing the following steps: diff --git a/docs/ENVS.md b/docs/ENVS.md index 76c7f8944f..95ff8365f9 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -116,7 +116,7 @@ Also, be aware that if you customize the name of the currency or any of its deno | NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | Network currency decimals | - | `18` | `6` | v1.0.x+ | | NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL | `string` | Network secondary coin symbol. | - | - | `GNO` | v1.29.0+ | | NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES | `boolean` | Set to `true` for networks where users can pay transaction fees in either the native coin or ERC-20 tokens. | - | `false` | `true` | v1.33.0+ | -| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` \| `mining` \| 'fee reception' | Verification type in the network. Irrelevant for Arbitrum (verification type is always `posting`) and ZkEvm (verification type is always `sequencing`) L2s | - | `mining` | `validation` | v1.0.x+ | +| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` \| `mining` \| 'fee reception' | Verification type in the network. Irrelevant for Arbitrum (verification type is always `posting`) L2s | - | `mining` | `validation` | v1.0.x+ | | NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME | `string` | Name of the standard for creating tokens | - | `ERC` | `BEP` | v1.31.0+ | | NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES | `Array<{id: string; name: string;}>` | List of additional **ERC-20-like (fungible)** token types supported by the explorer UI (extends token type filters/labels). *Note: the token type id must also be supported by the backend API responses and filters.* | - | `[]` | `'[{"id":"ERC-777","name":"ERC-777"}]'` | v2.6.0+ | | NEXT_PUBLIC_IS_TESTNET | `boolean`| Set to true if network is testnet | - | `false` | `true` | v1.0.x+ | @@ -142,16 +142,17 @@ Also, be aware that if you customize the name of the currency or any of its deno | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'daily_operational_txs' \| 'coin_price' \| 'secondary_coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` | v1.0.x+ | -| NEXT_PUBLIC_HOMEPAGE_STATS | `Array<'latest_batch' \| 'total_blocks' \| 'average_block_time' \| 'total_txs' \| 'total_operational_txs' \| 'latest_l1_state_batch' \| 'wallet_addresses' \| 'gas_tracker' \| 'btc_locked' \| 'current_epoch'>` | List of stats widgets displayed on the home page | - | For zkSync, zkEvm and Arbitrum rollups: `['latest_batch','average_block_time','total_txs','wallet_addresses','gas_tracker']`, for other cases: `['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker']` | `['total_blocks','total_txs','wallet_addresses']` | v1.35.x+ | +| NEXT_PUBLIC_HOMEPAGE_STATS | `Array<'latest_batch' \| 'total_blocks' \| 'average_block_time' \| 'total_txs' \| 'total_operational_txs' \| 'latest_l1_state_batch' \| 'wallet_addresses' \| 'gas_tracker' \| 'btc_locked' \| 'current_epoch'>` | List of stats widgets displayed on the home page | - | For zkSync and Arbitrum rollups: `['latest_batch','average_block_time','total_txs','wallet_addresses','gas_tracker']`, for other cases: `['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker']` | `['total_blocks','total_txs','wallet_addresses']` | v1.35.x+ | | NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG | `HeroBannerConfig`, see details [below](#hero-banner-configuration-properties) | Configuration of hero banner appearance. | - | - | See [below](#hero-banner-configuration-properties) | v1.35.0+ | | NEXT_PUBLIC_HOMEPAGE_HIGHLIGHTS_CONFIG | `string` | URL of the file (in `.json` format only) that contains the configuration for banners on the application's homepage, showcasing some of its key functionality. See the full config format [below](#highlights-banner-configuration-properties). The config should contain at least 2 banners, but only 3 banners will be visible at the same time. A larger number of banners in the config allows for random banner rotation upon page load. | - | - | See [below](#highlights-banner-configuration-properties) | v2.6.0+ | #### Hero banner configuration properties -_Note_ Here, all values are arrays of up to two strings. The first string represents the value for the light color mode, and the second string represents the value for the dark color mode. If the array contains only one string, it will be used for both color modes. +_Note_ Here, some values are arrays of up to two strings. The first string represents the value for the light color mode, and the second string represents the value for the dark color mode. If the array contains only one string, it will be used for both color modes. | Variable | Type| Description | Compulsoriness | Default value | Example value | | --- | --- | --- | --- | --- | --- | +| text | `string` | The text on the banner | - | `{ chain_name } explorer` | `Duck migration observer` | | background | `[string, string]` | Banner background (could be a solid color, gradient or picture). The string should be a valid `background` CSS property value. | - | `['radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)']` | `['lightpink','no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)']` | | text_color | `[string, string]` | Banner text background. The string should be a valid `color` CSS property value. | - | `['white']` | `['lightpink','#DCFE76']` | | border | `[string, string]` | Banner border. The string should be a valid `border` CSS property value. | - | - | `['1px solid yellow','4px dashed #DCFE76']` | @@ -462,7 +463,9 @@ This feature is **enabled by default**. To switch it off pass `NEXT_PUBLIC_ADVAN | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_CONTRACT_INFO_API_HOST | `string` | Contract Info API endpoint url | Required | - | `https://contracts-info.services.blockscout.com` | v1.1.0+ | +| NEXT_PUBLIC_CONTRACT_INFO_INSTANCE_ID | `string` | Instance ID to use in the service resource paths | - | same as `NEXT_PUBLIC_NETWORK_ID` | `420:duck` | | | NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url | Required | - | `https://admin-rs.services.blockscout.com` | v1.1.0+ | +| NEXT_PUBLIC_ADMIN_RS_INSTANCE_ID | `string` | Instance ID to use in the service resource paths | - | same as `NEXT_PUBLIC_NETWORK_ID` | `420:duck` | |   @@ -487,8 +490,9 @@ Ads are enabled by default on all self-hosted instances. If you would like to di | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_AD_BANNER_PROVIDER | `slise` \| `adbutler` \| `coinzilla` \| `none` | Ads provider | - | `slise` | `coinzilla` | v1.0.x+ | +| NEXT_PUBLIC_AD_BANNER_PROVIDER | `slise` \| `adbutler` \| `sevio` \| `none` | Ads provider | - | `slise` | `sevio` | v1.0.x+ | | NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER | `adbutler` | Additional ads provider to mix with the main one | - | - | `adbutler` | v1.28.0+ | +| NEXT_PUBLIC_AD_BANNER_SEVIO_ZONES | `Array` | Optional Sevio banner zone overrides in `[mobileZone, desktopZone]` format | - | `['52909312-7ebb-4bd5-9006-5e4f7041ed63','07cabd45-77f1-4203-8081-868bae776981']` | `['52909312-7ebb-4bd5-9006-5e4f7041ed63','07cabd45-77f1-4203-8081-868bae776981']` | v2.8.0+ | | NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP | `{ id: string; width: string; height: string }` | Placement config for desktop Adbutler banner | - | - | `{'id':'123456','width':'728','height':'90'}` | v1.3.0+ | | NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE | `{ id: string; width: number; height: number }` | Placement config for mobile Adbutler banner | - | - | `{'id':'654321','width':'300','height':'100'}` | v1.3.0+ | | NEXT_PUBLIC_AD_BANNER_ENABLE_SPECIFY | `boolean` | Enables Specify ads in addition to the main ad banner provider | - | - | `true` | v2.4.0+ | @@ -501,7 +505,7 @@ Ads are enabled by default on all self-hosted instances. If you would like to di | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_AD_TEXT_PROVIDER | `coinzilla` \| `none` | Ads provider | - | `coinzilla` | `none` | v1.0.x+ | +| NEXT_PUBLIC_AD_TEXT_PROVIDER | `sevio` \| `none` | Ads provider | - | `sevio` | `none` | v1.0.x+ |   @@ -529,7 +533,7 @@ Ads are enabled by default on all self-hosted instances. If you would like to di | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_ROLLUP_TYPE | `'optimistic' \| 'arbitrum' \| 'shibarium' \| 'zkEvm' \| 'zkSync' \| 'scroll'` | Rollup chain type | Required | - | `'optimistic'` | v1.24.0+ | +| NEXT_PUBLIC_ROLLUP_TYPE | `'optimistic' \| 'arbitrum' \| 'shibarium' \| 'zkSync' \| 'scroll'` | Rollup chain type | Required | - | `'optimistic'` | v1.24.0+ | | NEXT_PUBLIC_ROLLUP_PARENT_CHAIN | `ParentChain`, see details [below](#parent-chain-configuration-properties) | Configuration parameters for the parent chain. | Required | - | `{'baseUrl':'https://explorer.duckchain.io'}` | v1.38.0+ | | NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | `string` | URL for rollup to parent chain withdrawals (Optimistic stack only) | - | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0+ | | NEXT_PUBLIC_ROLLUP_LAYER_NUMBER | `number` | Layer number of the rollup | - | `2` | `3` | v2.7.0+ | @@ -605,6 +609,7 @@ Ads are enabled by default on all self-hosted instances. If you would like to di | NEXT_PUBLIC_MARKETPLACE_ENABLED | `boolean` | `true` means that the marketplace page will be enabled | Required | - | `true` | v1.24.1+ | | NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | `string` | URL of configuration file (`.json` format only) which contains list of apps that will be shown on the marketplace page. See [below](#marketplace-app-configuration-properties) list of available properties for an app. Can be replaced with NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | Required | - | `https://example.com/marketplace_config.json` | v1.0.x+ | | NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url. Can be used instead of NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | - | - | `https://admin-rs.services.blockscout.com` | v1.1.0+ | +| NEXT_PUBLIC_ADMIN_RS_INSTANCE_ID | `string` | Instance ID to use in the service resource paths | - | same as `NEXT_PUBLIC_NETWORK_ID` | `420:duck` | | | NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | Required | - | `https://airtable.com/shrqUAcjgGJ4jU88C` | v1.0.x+ | | NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM | `string` | Link to form where users can suggest ideas for the marketplace | - | - | `https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form` | v1.24.0+ | | NEXT_PUBLIC_NETWORK_RPC_URL | `string` | See in [Blockchain parameters](#blockchain-parameters) section | Required | - | `https://core.poa.network` | v1.0.x+ | @@ -701,6 +706,7 @@ This feature is **enabled by default** with the `['metamask']` value. To switch | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_CONTRACT_INFO_API_HOST | `string` | Contract Info API endpoint url | Required | - | `https://contracts-info.services.blockscout.com` | v1.0.x+ | +| NEXT_PUBLIC_CONTRACT_INFO_INSTANCE_ID | `string` | Instance ID to use in the service resource paths | - | same as `NEXT_PUBLIC_NETWORK_ID` | `420:duck` | |   @@ -745,6 +751,7 @@ This feature allows you to submit an application with a public address tag. | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_METADATA_SERVICE_API_HOST | `string` | Metadata Service API endpoint url | Required | - | `https://metadata.services.blockscout.com` | v1.30.0+ | | NEXT_PUBLIC_ADMIN_SERVICE_API_HOST | `string` | Admin Service API endpoint url | Required | - | `https://admin-rs.services.blockscout.com` | v1.1.0+ | +| NEXT_PUBLIC_ADMIN_RS_INSTANCE_ID | `string` | Instance ID to use in the service resource paths | - | same as `NEXT_PUBLIC_NETWORK_ID` | `420:duck` | | | NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | See [below](#google-recaptcha) | true | - | `` | v1.0.x+ |   @@ -980,6 +987,7 @@ This feature enables Blockscout Merits program. It requires that the [My account | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_DEX_POOLS_ENABLED | `boolean` | Set to true to enable the feature | Required | - | `true` | v1.37.0+ | | NEXT_PUBLIC_CONTRACT_INFO_API_HOST | `string` | Contract Info API endpoint url | Required | - | `https://contracts-info.services.blockscout.com` | v1.0.x+ | +| NEXT_PUBLIC_CONTRACT_INFO_INSTANCE_ID | `string` | Instance ID to use in the service resource paths | - | same as `NEXT_PUBLIC_NETWORK_ID` | `420:duck` | |   @@ -1022,6 +1030,7 @@ This feature allows to display widgets on the address page with data from 3rd pa | title | `string` | Title of displayed data | Required | - | `'Multichain balance'` | | hint | `string` | Hint for displayed data | - | - | `'Widget hint'` | | valuePath | `string` | Path to the field in the API response that contains the value to be displayed | Required | - | `'result.balance'` | +| valueTitlePath | `string` | Path in the API response that contains the text to display next to the widget title | - | - | `'result.risk.status'` | | pages | `Array<'eoa' \| 'contract' \| 'token'>` | List of pages where the widget should be displayed | Required | - | `['eoa']` | | chainIds | `Record` | Mapping of chain IDs to custom values that will be used in `url` template | - | - | `{'1': 'eth', '10': 'op'}` | @@ -1073,7 +1082,7 @@ This feature enables cross-chain transaction tracking and visualization, allowin | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED | `boolean` | The flag that enables the feature | Required | - | `true` | v2.7.0+ | -| NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST | `string` | Interchain indexer API service host used to fetch cross-chain transaction data and metadata | Required | - | `https://interchain-indexer.k8s-dev.blockscout.com` | v2.7.0+ | +| NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST | `string` | Interchain indexer API service host used to fetch cross-chain transaction data and metadata | Required | - | `https://bridge-indexer.k8s-dev.blockscout.com` | v2.7.0+ |   diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md new file mode 100644 index 0000000000..eaaa2ccafa --- /dev/null +++ b/docs/GLOSSARY.md @@ -0,0 +1,67 @@ +# Ubiquitous Language Glossary + +Domain terms used in the Blockscout frontend codebase. Intended for both engineers and agents. +Terms are listed alphabetically within each section. Where a codename differs from the product name, both are shown. + +--- + +## Architecture + +Terms specific to how this codebase is structured. Read this section first when navigating the repo or `ARCH_REDESIGN.md`. + +| Term | Definition | +|------|------------| +| **Chain Variant** | A non-rollup chain which may or may not have its own env var config. It has custom UI or domain entities (e.g. Celo, TAC, ZetaChain, Beacon Chain, MUD, Zilliqa). Grouped under `client/features/chain-variants//`. Grouped under `features/chain-variants//`. Distinct from a **Rollup**, which implies an L1/L2 settlement relationship. | +| **Feature** | An optional, config-gated product area (e.g. `stats`, `marketplace`, `user-ops`). Corresponds roughly to a flag in `configs/app/features/`. Lives under `client/features/`. Contrast with **Slice**. | +| **Rollup** | A chain that settles transactions on a parent (L1) chain. Introduces specific entities: deposits, withdrawals, transaction batches, output roots. Organized by rollup type under `features/rollup//` (e.g. `optimism`, `arbitrum`, `zk-sync`). Contrast with **Chain Variant**. | +| **Slice** | A core explorer domain entity that is always present on any EVM chain, regardless of feature flags (e.g. `tx`, `block`, `address`, `token`). Lives under `client/slices/`. Contrast with **Feature**. | + +--- + +## Domain entities + +Blockchain objects that appear as first-class UI elements — detail pages, list rows, API resources, stubs. + +| Term | Definition | +|------|------------| +| **Blob** | An individual EIP-4844 data blob attached to a transaction. A single transaction can carry multiple blobs. Each blob has its own detail page (`/blob/[hash]`) and is a first-class entity in the UI (with its own components, API resource, and stub). The term "blob" remains correct for the entity — only the *feature folder* uses `data-availability` (see **Data Availability**). | +| **CCTX (Cross-Chain Transaction)** | A ZetaChain-specific transaction type that spans multiple chains. Displayed as a separate tab on the transactions list page. Unrelated to the general cross-chain transactions feature (`interchainIndexer`). | +| **Dispute Games** | Part of the Optimism **Fault Proof System**. On-chain games used to challenge and resolve disputed L2 output roots. Displayed in the rollup navigation when `faultProofSystem` is enabled. | +| **Epoch** | A consensus time period specific to **Celo**. Has its own index and detail pages in the UI. Not a generic blockchain concept in this codebase — always refers to a Celo epoch. Lives under `features/chain-variants/celo/`. | +| **Interop Messages** | Cross-rollup messages passed between OP Stack chains using the native interoperability protocol. Visible in rollup navigation when `NEXT_PUBLIC_INTEROP_ENABLED` is set. Distinct from **Interchain Indexer** messages. Currently in deprecated state. | +| **Kettle** | In the **SUAVE** architecture, a Kettle is a trusted execution environment (TEE) node that processes MEV bundles. Transactions on SUAVE are associated with a Kettle, and the UI exposes a per-Kettle transaction list at `/txs/kettle/[hash]`. | +| **Operation** | A **TAC**-specific entity representing a bridge operation between the TON and EVM ecosystems. Has no equivalent on standard EVM chains. Exposed at `/operations` and `/operation/[id]`. | +| **User Op (User Operation)** | An ERC-4337 account abstraction operation — a transaction-like object submitted to a bundler rather than directly to the network. Displayed in a dedicated list at `/ops` and detail page at `/op/[hash]`. Config-gated via `NEXT_PUBLIC_HAS_USER_OPS`. | + +--- + +## Features & integrations + +Optional product areas and their backing services. Each corresponds to a folder under `client/features/`. + +| Term | Definition | +|------|------------| +| **Advanced Filter** | A UI feature for filtering transactions using complex multi-criteria queries (address, method, token, amount ranges, etc.). Distinct from the basic filter controls present on most list pages. Config-gated via `NEXT_PUBLIC_ADVANCED_FILTER_ENABLED`. | +| **Alternative Explorers** | A feature that adds a "Verify with other explorers" menu to transaction, block, address, and token pages, linking to third-party explorers (e.g. Etherscan). The set of explorers is configured via `NEXT_PUBLIC_NETWORK_EXPLORERS`. Lives under `client/features/alternative-explorers/`. | +| **Beacon Chain** | The Ethereum Proof-of-Stake consensus layer. When enabled, exposes beacon chain deposits and withdrawals alongside regular transactions. Not a rollup — it is the Ethereum L1 consensus mechanism. | +| **Address Metadata** | The feature that provides address labels, metadata enrichment, and label-based address search. Backs the metadata panel on address detail pages and the Label Search page (`/accounts/label/[slug]`). Feature folder: `client/features/address-metadata/`. Config-gated via `NEXT_PUBLIC_METADATA_SERVICE_API_HOST`. Note: **Public Tags** is one sub-feature of Address Metadata, not a synonym for the whole. | +| **BENS (Blockscout Name Service)** | Blockscout's own address naming service, chain-agnostic and operated by Blockscout. Distinct from ENS (which is Ethereum-specific). BENS domain lookups and **Clusters** are co-located under the `name-services/` sub-structure: `client/features/name-services/domains/` for BENS/ENS and `client/features/name-services/clusters/` for Clusters. | +| **Clusters** | An address identity and grouping service. Aggregates multiple addresses under a named cluster (individual, protocol, organization). Exposes a directory, leaderboard, and per-address lookup. Backed by the Clusters API (`NEXT_PUBLIC_CLUSTERS_API_HOST`). Co-located with **BENS** under `client/features/name-services/clusters/`. | +| **Data Availability** | The config-gated *feature* that surfaces **Blob** data posted to Ethereum (EIP-4844). `data-availability` is the name used for the feature folder and config flag — the underlying entity is still called a **Blob**. Config-gated via `NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED`. | +| **Fault Proof System** | Optimism's mechanism for proving the correctness of L2 state transitions on L1 via **Dispute Games**. Enabled by the `NEXT_PUBLIC_FAULT_PROOF_ENABLED` feature flag. | +| **Flashblocks** | MegaETH's sub-second block streaming mechanism. Delivers real-time pre-confirmation block data via WebSocket. Specific to the MegaETH chain variant. | +| **Hot Contracts** | A ranked list of the most recently and frequently interacted-with smart contracts on the network. Config-gated via `NEXT_PUBLIC_HOT_CONTRACTS_ENABLED`. | +| **Interchain Indexer** | A microservice that indexes cross-chain messages and token transfers across heterogeneous chains (not ZetaChain-specific — a general interoperability indexer). Distinct from **CCTX**. Backed by `NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED`. | +| **Marketplace** | A curated directory of dApps and DeFi applications integrated with Blockscout. Config-gated via `NEXT_PUBLIC_MARKETPLACE_ENABLED`. Has its own full-page UI with categories and app detail views. | +| **MetaSuites** | A third-party browser extension that enhances the Blockscout UI with additional data and links. The frontend notifies it about route changes via a dedicated hook. | +| **MUD Worlds** | Instances of the MUD framework — an on-chain autonomous world / game engine. Chains that use MUD expose a list of active worlds. Config-gated via `mudFramework` feature flag. Treated as **Chain-variant**; lives under `client/features/chain-variant/mud/`. | +| **Multichain** | The feature that aggregates data across multiple Blockscout-indexed chains into a single explorer view. Introduces an "Ecosystems" page and a multichain balance button. Config-gated via `NEXT_PUBLIC_MULTICHAIN_ENABLED`. | +| **Pools** | DEX liquidity pool positions tracked on-chain. Exposed as a "DEX tracker" section under Tokens. Config-gated via `NEXT_PUBLIC_DEX_POOLS_ENABLED`. | +| **Public Tags** | Community-submitted labels for addresses, visible on address pages. Submitted via a dedicated form. Config-gated via `NEXT_PUBLIC_METADATA_SERVICE_API_HOST`. | +| **Rewards** | A token rewards and incentives program operated by Blockscout. Has its own API, context provider, and UI modals. Config-gated via `NEXT_PUBLIC_REWARDS_SERVICE_API_HOST`. | +| **SolidityScan** | A third-party smart contract security vulnerability scanner integrated into contract detail pages. Displays a security score and known vulnerability findings. | +| **SUAVE** | A MEV-focused chain developed by Flashbots, built around a trusted execution environment (TEE) architecture. Introduces the **Kettle** entity. | +| **TAC (Ton Application Chain)** | A chain that bridges the TON blockchain and EVM ecosystems. Introduces the **Operation** entity. Its data comes from a dedicated `tac-operation-lifecycle` microservice. Config-gated via `NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST`. | +| **Validators** | The set of active block validators on chains that expose this concept (e.g. zkSync Era, Celo). Displayed in a dedicated list. Not present on standard PoW/PoS EVM chains without explicit support. Config-gated via `NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE`. | +| **Visualize** | A service that converts Solidity source code into UML diagrams (class and storage layout). Accessed from contract detail pages. Backed by `NEXT_PUBLIC_VISUALIZE_API_HOST`. | +| **Watchlist** | An **Account** feature that lets authenticated users track a set of addresses and receive notifications for their activity. | diff --git a/eslint.config.mjs b/eslint.config.mjs index dce413065a..1d02072825 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -3,6 +3,7 @@ import jsPlugin from '@eslint/js'; import nextJsPlugin from '@next/eslint-plugin-next'; import stylisticPlugin from '@stylistic/eslint-plugin'; import reactQueryPlugin from '@tanstack/eslint-plugin-query'; +import boundariesPlugin from 'eslint-plugin-boundaries'; import consistentDefaultExportNamePlugin from 'eslint-plugin-consistent-default-export-name'; import importPlugin from 'eslint-plugin-import'; import importHelpersPlugin from 'eslint-plugin-import-helpers'; @@ -57,6 +58,20 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const gitignorePath = path.resolve(__dirname, '.gitignore'); +/** @see client/ARCH_REDESIGN.md §8 — dependency layers for migrated code */ +const ARCH_BOUNDARY_ELEMENTS = [ + { type: 'client-api', pattern: 'client/api/**', mode: 'full' }, + { type: 'client-slices', pattern: 'client/slices/**', mode: 'full' }, + { type: 'client-features', pattern: 'client/features/**', mode: 'full' }, + { type: 'client-shared', pattern: 'client/shared/**', mode: 'full' }, + { type: 'client-shell', pattern: 'client/shell/**', mode: 'full' }, + { type: 'configs', pattern: 'configs/**', mode: 'full' }, + { type: 'toolkit', pattern: 'toolkit/**', mode: 'full' }, + { type: 'nextjs', pattern: 'nextjs/**', mode: 'full' }, + { type: 'legacy-lib', pattern: 'lib/**', mode: 'full' }, + { type: 'legacy-ui', pattern: 'ui/**', mode: 'full' }, +]; + /** @type {import('eslint').Linter.Config[]} */ export default tseslint.config( includeIgnoreFile(gitignorePath), @@ -72,7 +87,12 @@ export default tseslint.config( { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, - { settings: { react: { version: 'detect' } } }, + { + settings: { + react: { version: 'detect' }, + 'boundaries/elements': ARCH_BOUNDARY_ELEMENTS, + }, + }, jsPlugin.configs.recommended, @@ -288,6 +308,105 @@ export default tseslint.config( }, }, + // I have to disable this rule because of performance issues: + // https://github.com/import-js/eslint-plugin-import/issues/3060 + // Examples in our CI: + // before: 1m15s - https://github.com/blockscout/frontend/actions/runs/23210591334/job/67457992864 + // after: 4m27s - https://github.com/blockscout/frontend/actions/runs/25108323146/job/73585871924 + // + // { + // files: [ 'client/**' ], + // plugins: { + // 'import': importPlugin, + // }, + // rules: { + // 'import/no-cycle': [ 'error', { maxDepth: 10 } ], + // }, + // }, + // { + // files: [ 'lib/**', 'ui/**' ], + // plugins: { + // 'import': importPlugin, + // }, + // rules: { + // 'import/no-cycle': 'warn', + // }, + // }, + + /* + * ARCH_REDESIGN.md §10 — public type surface: consumers should import slice/feature types only from + * `types/api.ts`. eslint-plugin-boundaries can enforce this with `capture` on element patterns and + * targeted `disallow` rules; that is left for a follow-up once more slices/features exist under client/. + * Type-only cross-layer imports are approximated via per-rule `importKind: 'type'` (v4 has no `allowTypeImports` option). + */ + { + files: [ + 'client/**/*.{ts,tsx}', + 'configs/**/*.{ts,tsx,mjs}', + ], + plugins: { + boundaries: boundariesPlugin, + }, + rules: { + 'boundaries/element-types': [ 'error', { + 'default': 'disallow', + rules: [ + { + from: 'client-api', + allow: [ 'client-shared', 'legacy-lib' ], + }, + { + from: 'client-api', + allow: [ 'client-slices', 'client-features' ], + importKind: 'type', + }, + { + from: 'client-slices', + allow: [ 'client-api', 'client-shared', 'client-slices', 'legacy-lib', 'legacy-ui', 'toolkit', 'configs' ], + }, + { + from: 'client-slices', + allow: [ 'client-features' ], + importKind: 'type', + }, + { + from: 'client-features', + allow: [ 'client-api', 'client-shared', 'client-slices', 'client-features', 'legacy-lib', 'legacy-ui', 'toolkit', 'configs' ], + }, + { + from: 'client-shared', + allow: [ 'client-shared', 'legacy-lib', 'legacy-ui', 'toolkit', 'configs' ], + }, + { + from: 'client-shared', + allow: [ 'client-slices' ], + importKind: 'type', + }, + { + from: 'client-shell', + allow: [ + 'client-api', + 'client-slices', + 'client-features', + 'client-shared', + 'client-shell', + 'legacy-lib', + 'legacy-ui', + 'toolkit', + 'nextjs', + 'configs', + ], + }, + { + from: 'configs', + // `configs` must not import `client/*`; `lib/*` stays allowed until config-owned types move out of legacy paths + allow: [ 'configs', 'legacy-lib' ], + }, + ], + } ], + }, + }, + { plugins: { 'import-helpers': importHelpersPlugin, @@ -301,6 +420,10 @@ export default tseslint.config( 'module', '/types/', [ '/^nextjs/' ], + [ '/^client/api/' ], + [ '/^client/slices/' ], + [ '/^client/features/' ], + [ '/^client/shared/' ], [ '/^configs/', '/^data/', @@ -337,6 +460,7 @@ export default tseslint.config( }, files: [ 'ui/**/[A-Z]*.tsx', + 'client/**/*.tsx', ], ignores: [ '**/*.pw.*', @@ -409,6 +533,9 @@ export default tseslint.config( '@stylistic/template-curly-spacing': [ 'error', 'always' ], '@stylistic/wrap-iife': [ 'error', 'inside' ], }, + ignores: [ + 'next-env.d.ts', + ], }, { @@ -424,7 +551,7 @@ export default tseslint.config( 'no-redeclare': 'off', // rules customizations - eqeqeq: [ 'error', 'allow-null' ], + eqeqeq: [ 'error' ], 'id-match': [ 'error', '^[\\w$]+$' ], 'max-len': [ 'error', 160, 4 ], 'no-console': 'error', diff --git a/global.d.ts b/global.d.ts index 4ec828336a..2e1414bb74 100644 --- a/global.d.ts +++ b/global.d.ts @@ -3,16 +3,9 @@ import type { MultichainConfig } from 'types/multichain'; import type { WalletProvider } from 'types/web3'; import 'vitest-fetch-mock'; -type CPreferences = { - zone: string; - width: string; - height: string; -}; - declare global { export interface Window { ethereum?: WalletProvider | undefined; - coinzilla_display: Array; sevioads: Array>> | undefined; ga?: { getAll: () => Array<{ get: (prop: string) => string }>; diff --git a/icons/download.svg b/icons/download.svg new file mode 100644 index 0000000000..010272325f --- /dev/null +++ b/icons/download.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/files/csv.svg b/icons/files/csv.svg index 30c43d356c..1410a43115 100644 --- a/icons/files/csv.svg +++ b/icons/files/csv.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/icons/navigation/ictt_users.svg b/icons/navigation/ictt_users.svg new file mode 100644 index 0000000000..d2ca4510d8 --- /dev/null +++ b/icons/navigation/ictt_users.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/api/services/utils.ts b/lib/api/services/utils.ts deleted file mode 100644 index c4cf663c57..0000000000 --- a/lib/api/services/utils.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { ApiResource } from '../types'; - -export type IsPaginated = R extends { paginated: true } ? true : false; diff --git a/lib/blob/guessDataType.ts b/lib/blob/guessDataType.ts index ea149236a0..3510c7be89 100644 --- a/lib/blob/guessDataType.ts +++ b/lib/blob/guessDataType.ts @@ -1,6 +1,6 @@ import filetype from 'magic-bytes.js'; -import hexToBytes from 'lib/hexToBytes'; +import hexToBytes from 'client/shared/transformers/hex-to-bytes'; import removeNonSignificantZeroBytes from './removeNonSignificantZeroBytes'; diff --git a/lib/clusters/detectInputType.spec.ts b/lib/clusters/detectInputType.spec.ts index 004e4f9c9f..9d854dbd27 100644 --- a/lib/clusters/detectInputType.spec.ts +++ b/lib/clusters/detectInputType.spec.ts @@ -1,40 +1,11 @@ -import { isEvmAddress } from 'lib/address/isEvmAddress'; -import { describe, it, expect } from 'vitest'; +import { it, expect } from 'vitest'; import { detectInputType } from './detectInputType'; -describe('detectInputType', () => { - it('should detect EVM address format', () => { - expect(detectInputType('0x1234567890123456789012345678901234567890')).toBe('address'); - }); - - it('should detect cluster name format', () => { - expect(detectInputType('test-cluster')).toBe('cluster_name'); - }); +it('should detect EVM address format', () => { + expect(detectInputType('0x1234567890123456789012345678901234567890')).toBe('address'); }); -describe('isEvmAddress', () => { - it('should return true for valid EVM address', () => { - expect(isEvmAddress('0x1234567890123456789012345678901234567890')).toBe(true); - expect(isEvmAddress('0xabcdef1234567890123456789012345678901234')).toBe(true); - expect(isEvmAddress('0xABCDEF1234567890123456789012345678901234')).toBe(true); - }); - - it('should return false for invalid EVM address', () => { - expect(isEvmAddress('0x123')).toBe(false); - expect(isEvmAddress('123456789012345678901234567890123456789')).toBe(false); - expect(isEvmAddress('0xGGGGGG1234567890123456789012345678901234')).toBe(false); - expect(isEvmAddress('0x12345678901234567890123456789012345678901')).toBe(false); - }); - - it('should return false for empty or null input', () => { - expect(isEvmAddress('')).toBe(false); - expect(isEvmAddress(null as unknown as string)).toBe(false); - expect(isEvmAddress(undefined as unknown as string)).toBe(false); - }); - - it('should handle addresses with extra whitespace', () => { - expect(isEvmAddress(' 0x1234567890123456789012345678901234567890 ')).toBe(true); - expect(isEvmAddress(' 0x123 ')).toBe(false); - }); +it('should detect cluster name format', () => { + expect(detectInputType('test-cluster')).toBe('cluster_name'); }); diff --git a/lib/clusters/useAddressClusters.spec.ts b/lib/clusters/useAddressClusters.spec.ts index 871d222437..841efa6ade 100644 --- a/lib/clusters/useAddressClusters.spec.ts +++ b/lib/clusters/useAddressClusters.spec.ts @@ -9,7 +9,7 @@ const { mockUseApiQuery } = vi.hoisted(() => ({ mockUseApiQuery: vi.fn(), })); -vi.mock('lib/api/useApiQuery', () => ({ +vi.mock('client/api/hooks/useApiQuery', () => ({ 'default': mockUseApiQuery, })); diff --git a/lib/clusters/useAddressClusters.ts b/lib/clusters/useAddressClusters.ts index 49303d2eb3..b51943b2b0 100644 --- a/lib/clusters/useAddressClusters.ts +++ b/lib/clusters/useAddressClusters.ts @@ -1,5 +1,6 @@ +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; const feature = config.features.nameServices; diff --git a/lib/clusters/useClusterPagination.spec.ts b/lib/clusters/useClusterPagination.spec.ts index 69ee7c5d1f..3db1df3189 100644 --- a/lib/clusters/useClusterPagination.spec.ts +++ b/lib/clusters/useClusterPagination.spec.ts @@ -2,8 +2,9 @@ import { useRouter } from 'next/router'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { useQueryParams } from 'lib/router/useQueryParams'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import { useQueryParams } from 'client/shared/router/useQueryParams'; + import type { Mock } from 'vitest'; import { describe, it, expect, beforeEach, vi } from 'vitest'; import { renderHook, act } from 'vitest/lib'; @@ -13,8 +14,8 @@ import { useClusterPagination } from '../clusters/useClusterPagination'; vi.mock('next/router', () => ({ useRouter: vi.fn(), })); -vi.mock('lib/router/useQueryParams'); -vi.mock('lib/router/getQueryParamString'); +vi.mock('client/shared/router/useQueryParams'); +vi.mock('client/shared/router/get-query-param-string'); const mockUseRouter = useRouter as Mock; const mockUseQueryParams = useQueryParams as Mock; diff --git a/lib/clusters/useClusterPagination.ts b/lib/clusters/useClusterPagination.ts index ee7fe5c057..d3196961c1 100644 --- a/lib/clusters/useClusterPagination.ts +++ b/lib/clusters/useClusterPagination.ts @@ -3,8 +3,8 @@ import { useCallback, useMemo } from 'react'; import type { PaginationParams } from 'ui/shared/pagination/types'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { useQueryParams } from 'lib/router/useQueryParams'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import { useQueryParams } from 'client/shared/router/useQueryParams'; export function useClusterPagination(hasNextPage: boolean, isLoading: boolean) { const router = useRouter(); diff --git a/lib/clusters/useClusterSearch.ts b/lib/clusters/useClusterSearch.ts index 01848c2370..71b553ae0c 100644 --- a/lib/clusters/useClusterSearch.ts +++ b/lib/clusters/useClusterSearch.ts @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; -import useDebounce from 'lib/hooks/useDebounce'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useDebounce from 'client/shared/hooks/useDebounce'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; export function useClusterSearch() { const router = useRouter(); diff --git a/lib/clusters/useClustersData.spec.ts b/lib/clusters/useClustersData.spec.ts index 483fd24edb..e790c72a7e 100644 --- a/lib/clusters/useClustersData.spec.ts +++ b/lib/clusters/useClustersData.spec.ts @@ -2,14 +2,15 @@ import { renderHook } from '@testing-library/react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { detectInputType } from 'lib/clusters/detectInputType'; import type { Mock } from 'vitest'; import { describe, it, expect, beforeEach, vi } from 'vitest'; import { useClustersData } from './useClustersData'; -vi.mock('lib/api/useApiQuery'); +vi.mock('client/api/hooks/useApiQuery'); const mockUseApiQuery = useApiQuery as Mock; type MockQueryResult = ReturnType; diff --git a/lib/clusters/useClustersData.ts b/lib/clusters/useClustersData.ts index ba46da8cf8..7a211c4d2b 100644 --- a/lib/clusters/useClustersData.ts +++ b/lib/clusters/useClustersData.ts @@ -3,7 +3,8 @@ import React from 'react'; import type { ClustersByAddressObject } from 'types/api/clusters'; import { ClustersOrderBy } from 'types/api/clusters'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { detectInputType } from 'lib/clusters/detectInputType'; import { CLUSTER_ITEM } from 'stubs/clusters'; diff --git a/lib/contexts/multichain.tsx b/lib/contexts/multichain.tsx index 009debf3dc..3561243e50 100644 --- a/lib/contexts/multichain.tsx +++ b/lib/contexts/multichain.tsx @@ -3,9 +3,10 @@ import React from 'react'; import type { ClusterChainConfig } from 'types/multichain'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import getChainIdFromSlug from 'lib/multichain/getChainIdFromSlug'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import getChainIdFromSlugOrId from 'lib/multichain/getChainIdFromSlugOrId'; interface MultichainProviderProps { children: React.ReactNode; @@ -20,13 +21,13 @@ export const MultichainContext = React.createContext( export function MultichainProvider({ children, chainId: chainIdProp }: MultichainProviderProps) { const router = useRouter(); - const chainSlugQueryParam = router.pathname.includes('chain_slug') ? getQueryParamString(router.query.chain_slug) : undefined; + const chainSlugOrIdQueryParam = router.pathname.includes('chain_slug_or_id') ? getQueryParamString(router.query.chain_slug_or_id) : undefined; const chainIdQueryParam = router.query.chain_id ? getQueryParamString(router.query.chain_id) : undefined; const [ chainId, setChainId ] = React.useState( chainIdProp ?? chainIdQueryParam ?? - (chainSlugQueryParam ? getChainIdFromSlug(chainSlugQueryParam) : undefined), + (chainSlugOrIdQueryParam ? getChainIdFromSlugOrId(chainSlugOrIdQueryParam) : undefined), ); React.useEffect(() => { diff --git a/lib/contexts/rewards.tsx b/lib/contexts/rewards.tsx index 735196e939..15dd3935cf 100644 --- a/lib/contexts/rewards.tsx +++ b/lib/contexts/rewards.tsx @@ -6,17 +6,20 @@ import { useSignMessage, useSwitchChain } from 'wagmi'; import type * as rewards from '@blockscout/points-types'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; +import type { ResourceError } from 'client/api/resources'; + +import useAccount from 'client/features/connect-wallet/hooks/useAccount'; + +import decodeJWT from 'client/shared/auth/decode-jwt'; +import getErrorMessage from 'client/shared/errors/get-error-message'; +import getErrorObjPayload from 'client/shared/errors/get-error-obj-payload'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import removeQueryParam from 'client/shared/router/remove-query-param'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -import * as cookies from 'lib/cookies'; -import decodeJWT from 'lib/decodeJWT'; -import getErrorMessage from 'lib/errors/getErrorMessage'; -import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import removeQueryParam from 'lib/router/removeQueryParam'; -import useAccount from 'lib/web3/useAccount'; import { toaster } from 'toolkit/chakra/toaster'; import { YEAR } from 'toolkit/utils/consts'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; diff --git a/lib/contexts/settings.tsx b/lib/contexts/settings.tsx index c59d5658bc..9990a9a2b8 100644 --- a/lib/contexts/settings.tsx +++ b/lib/contexts/settings.tsx @@ -1,9 +1,9 @@ import React from 'react'; +import { ADDRESS_FORMATS, type AddressFormat } from 'client/slices/address/types/view'; import type { TimeFormat } from 'types/settings'; -import { ADDRESS_FORMATS, type AddressFormat } from 'types/views/address'; -import * as cookies from 'lib/cookies'; +import * as cookies from 'client/shared/storage/cookies'; import { useAppContext } from './app'; diff --git a/lib/hooks/useAdblockDetect.tsx b/lib/hooks/useAdblockDetect.tsx index 43998e7552..50c35d8707 100644 --- a/lib/hooks/useAdblockDetect.tsx +++ b/lib/hooks/useAdblockDetect.tsx @@ -2,9 +2,10 @@ import { useEffect } from 'react'; import type { AdBannerProviders } from 'types/client/adProviders'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; import { useAppContext } from 'lib/contexts/app'; -import * as cookies from 'lib/cookies'; import { isBrowser } from 'toolkit/utils/isBrowser'; const DEFAULT_URL = 'https://request-global.czilladx.com'; @@ -13,7 +14,7 @@ const DEFAULT_URL = 'https://request-global.czilladx.com'; // but we see some false-positive results in certain browsers const TEST_URLS: Record = { slise: 'https://v1.slise.xyz/serve', - coinzilla: 'https://request-global.czilladx.com', + sevio: 'https://request-global.czilladx.com', adbutler: 'https://servedbyadbutler.com/app.js', none: DEFAULT_URL, }; diff --git a/lib/hooks/useGetCsrfToken.tsx b/lib/hooks/useGetCsrfToken.tsx index db3c74b7e3..105a98a7b9 100644 --- a/lib/hooks/useGetCsrfToken.tsx +++ b/lib/hooks/useGetCsrfToken.tsx @@ -1,10 +1,11 @@ import { useQuery } from '@tanstack/react-query'; -import buildUrl from 'lib/api/buildUrl'; -import isNeedProxy from 'lib/api/isNeedProxy'; -import { getResourceKey } from 'lib/api/useApiQuery'; -import * as cookies from 'lib/cookies'; -import useFetch from 'lib/hooks/useFetch'; +import buildUrl from 'client/api/build-url'; +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useFetch from 'client/api/hooks/useFetch'; +import isNeedProxy from 'client/api/is-need-proxy'; + +import * as cookies from 'client/shared/storage/cookies'; export default function useGetCsrfToken() { const nodeApiFetch = useFetch(); diff --git a/lib/hooks/useGraphLinks.tsx b/lib/hooks/useGraphLinks.tsx index 8943f38bae..b780d95df4 100644 --- a/lib/hooks/useGraphLinks.tsx +++ b/lib/hooks/useGraphLinks.tsx @@ -1,8 +1,9 @@ import { useQuery } from '@tanstack/react-query'; +import useFetch from 'client/api/hooks/useFetch'; +import type { ResourceError } from 'client/api/resources'; + import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useFetch from 'lib/hooks/useFetch'; const feature = config.features.marketplace; diff --git a/lib/hooks/useIsSafeAddress.tsx b/lib/hooks/useIsSafeAddress.tsx index e9203fdc46..9e468b66e6 100644 --- a/lib/hooks/useIsSafeAddress.tsx +++ b/lib/hooks/useIsSafeAddress.tsx @@ -1,7 +1,8 @@ import { useQuery } from '@tanstack/react-query'; +import useFetch from 'client/api/hooks/useFetch'; + import config from 'configs/app'; -import useFetch from 'lib/hooks/useFetch'; const feature = config.features.safe; diff --git a/lib/hooks/useNavItems.tsx b/lib/hooks/useNavItems.tsx index 3c5c5a9210..711d45abfb 100644 --- a/lib/hooks/useNavItems.tsx +++ b/lib/hooks/useNavItems.tsx @@ -42,7 +42,7 @@ export default function useNavItems(): ReturnType { text: 'Blocks', nextRoute: { pathname: '/blocks' as const }, icon: 'navigation/block', - isActive: pathname === '/blocks' || pathname === '/block/[height_or_hash]' || pathname === '/chain/[chain_slug]/block/[height_or_hash]', + isActive: pathname === '/blocks' || pathname === '/block/[height_or_hash]' || pathname === '/chain/[chain_slug_or_id]/block/[height_or_hash]', }; const txs: NavItem | null = { text: 'Transactions', @@ -52,7 +52,7 @@ export default function useNavItems(): ReturnType { // sorry, but this is how it was designed (pathname === '/txs' && (!config.features.zetachain.isEnabled || !tab || !tab.includes('cctx'))) || pathname === '/tx/[hash]' || - pathname === '/chain/[chain_slug]/tx/[hash]', + pathname === '/chain/[chain_slug_or_id]/tx/[hash]', }; const cctxs: NavItem | null = config.features.zetachain.isEnabled ? { text: 'Cross-chain transactions', @@ -76,7 +76,7 @@ export default function useNavItems(): ReturnType { text: 'User operations', nextRoute: { pathname: '/ops' as const }, icon: 'navigation/user_op', - isActive: pathname === '/ops' || pathname === '/op/[hash]' || pathname === '/chain/[chain_slug]/op/[hash]', + isActive: pathname === '/ops' || pathname === '/op/[hash]' || pathname === '/chain/[chain_slug_or_id]/op/[hash]', } : null; const verifiedContracts: NavItem | null = @@ -153,7 +153,6 @@ export default function useNavItems(): ReturnType { if (rollupFeature.isEnabled && ( rollupFeature.type === 'optimistic' || rollupFeature.type === 'arbitrum' || - rollupFeature.type === 'zkEvm' || rollupFeature.type === 'scroll' )) { blockchainNavItems = [ @@ -296,6 +295,12 @@ export default function useNavItems(): ReturnType { icon: 'navigation/gas_tracker', isActive: pathname.startsWith('/gas-tracker'), }, + config.features.crossChainTxs.isEnabled && { + text: 'ICTT users', + nextRoute: { pathname: '/ictt-users' as const }, + icon: 'navigation/ictt_users', + isActive: pathname.startsWith('/ictt-users'), + }, ].filter(Boolean); if (items.length === 0) { diff --git a/lib/hooks/useNotifyOnNavigation.tsx b/lib/hooks/useNotifyOnNavigation.tsx index a3b7a7c92f..a347d78d21 100644 --- a/lib/hooks/useNotifyOnNavigation.tsx +++ b/lib/hooks/useNotifyOnNavigation.tsx @@ -2,8 +2,9 @@ import { usePathname } from 'next/navigation'; import { useRouter } from 'next/router'; import React from 'react'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import getQueryParamString from 'lib/router/getQueryParamString'; export default function useNotifyOnNavigation() { const router = useRouter(); diff --git a/lib/hooks/useRewardsActivity.tsx b/lib/hooks/useRewardsActivity.tsx index c681e4131d..2efb19e7b6 100644 --- a/lib/hooks/useRewardsActivity.tsx +++ b/lib/hooks/useRewardsActivity.tsx @@ -2,9 +2,10 @@ import { useCallback, useRef, useEffect } from 'react'; import type { PreSubmitTransactionResponse } from '@blockscout/points-types'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiFetch from 'lib/api/useApiFetch'; -import useApiQuery from 'lib/api/useApiQuery'; import { useRewardsContext } from 'lib/contexts/rewards'; import { MINUTE } from 'toolkit/utils/consts'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; diff --git a/lib/multichain/getChainIdFromSlug.ts b/lib/multichain/getChainIdFromSlug.ts deleted file mode 100644 index b9cff67092..0000000000 --- a/lib/multichain/getChainIdFromSlug.ts +++ /dev/null @@ -1,10 +0,0 @@ -import multichainConfig from 'configs/multichain'; - -export default function getChainIdFromSlug(slug: string) { - const config = multichainConfig(); - if (!config) { - return undefined; - } - - return config.chains.find((chain) => chain.slug === slug)?.id; -} diff --git a/lib/multichain/getChainIdFromSlugOrId.ts b/lib/multichain/getChainIdFromSlugOrId.ts new file mode 100644 index 0000000000..4389c29fc8 --- /dev/null +++ b/lib/multichain/getChainIdFromSlugOrId.ts @@ -0,0 +1,10 @@ +import multichainConfig from 'configs/multichain'; + +export default function getChainIdFromSlugOrId(slugOrId: string) { + const config = multichainConfig(); + if (!config) { + return undefined; + } + + return config.chains.find((chain) => chain.slug === slugOrId || chain.id === slugOrId)?.id; +} diff --git a/lib/multichain/getChainValueFromQuery.ts b/lib/multichain/getChainValueFromQuery.ts index 74d05646ed..23af86240a 100644 --- a/lib/multichain/getChainValueFromQuery.ts +++ b/lib/multichain/getChainValueFromQuery.ts @@ -1,7 +1,8 @@ import type { Router } from 'next/router'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import getQueryParamString from 'lib/router/getQueryParamString'; export default function getChainValueFromQuery( query: Router['query'], @@ -14,7 +15,7 @@ export default function getChainValueFromQuery( } const chainId = getQueryParamString(query.chain_id); - const chainSlug = getQueryParamString(query.chain_slug); + const chainSlugOrId = getQueryParamString(query.chain_slug_or_id); if (chainId) { if (config.chains.some((chain) => chain.id === chainId)) { @@ -22,8 +23,8 @@ export default function getChainValueFromQuery( } } - if (chainSlug) { - const chain = config.chains.find((chain) => chain.slug === chainSlug); + if (chainSlugOrId) { + const chain = config.chains.find((chain) => chain.slug === chainSlugOrId || chain.id === chainSlugOrId); if (chain) { return chain.id; } diff --git a/lib/rollups/utils.ts b/lib/rollups/utils.ts index aea480d965..fcc7a8a524 100644 --- a/lib/rollups/utils.ts +++ b/lib/rollups/utils.ts @@ -1,5 +1,3 @@ -import type { ZKEVM_L2_TX_STATUSES } from 'types/api/transaction'; -import type { ZKEVM_L2_TX_BATCH_STATUSES } from 'types/api/zkEvmL2'; import type { ZKSYNC_L2_TX_BATCH_STATUSES } from 'types/api/zkSyncL2'; import config from 'configs/app'; @@ -14,24 +12,6 @@ export const layerLabels = feature.isEnabled ? { parent: 'L1', }; -export const formatZkEvmTxStatus = (status: typeof ZKEVM_L2_TX_STATUSES[number]) => { - switch (status) { - case 'L1 Confirmed': - return `${ layerLabels.parent } Confirmed`; - default: - return status; - } -}; - -export const formatZkEvmL2TxnBatchStatus = (status: typeof ZKEVM_L2_TX_BATCH_STATUSES[number]) => { - switch (status) { - case 'L1 Sequence Confirmed': - return `${ layerLabels.parent } Sequence Confirmed`; - default: - return status; - } -}; - export const formatZkSyncL2TxnBatchStatus = (status: typeof ZKSYNC_L2_TX_BATCH_STATUSES[number]) => { return status.replace('L2', layerLabels.current).replace('L1', layerLabels.parent); }; diff --git a/lib/settings/identIcon.ts b/lib/settings/identIcon.ts index 1c26b6ef0f..197baa46ee 100644 --- a/lib/settings/identIcon.ts +++ b/lib/settings/identIcon.ts @@ -1,4 +1,4 @@ -import type { IdenticonType } from 'types/views/address'; +import type { IdenticonType } from 'client/slices/address/types/view'; export const IDENTICONS: Array<{ label: string; id: IdenticonType; sampleBg: string }> = [ { diff --git a/lib/solidityScan/useFetchReport.ts b/lib/solidityScan/useFetchReport.ts index 4df19bc85c..531d61e2c3 100644 --- a/lib/solidityScan/useFetchReport.ts +++ b/lib/solidityScan/useFetchReport.ts @@ -1,8 +1,9 @@ import React from 'react'; import * as v from 'valibot'; -import buildUrl from 'lib/api/buildUrl'; -import useApiQuery from 'lib/api/useApiQuery'; +import buildUrl from 'client/api/build-url'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { SOLIDITY_SCAN_REPORT } from 'stubs/contract'; import { SolidityScanSchema } from './schema'; diff --git a/lib/xStarScore/useFetchXStarScore.ts b/lib/xStarScore/useFetchXStarScore.ts index 9fe773baa1..39da3a016f 100644 --- a/lib/xStarScore/useFetchXStarScore.ts +++ b/lib/xStarScore/useFetchXStarScore.ts @@ -1,9 +1,10 @@ import React from 'react'; import * as v from 'valibot'; +import buildUrl from 'client/api/build-url'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import buildUrl from 'lib/api/buildUrl'; -import useApiQuery from 'lib/api/useApiQuery'; interface Params { hash: string; diff --git a/mocks/address/address.ts b/mocks/address/address.ts index c6358afc0e..c69673a404 100644 --- a/mocks/address/address.ts +++ b/mocks/address/address.ts @@ -1,5 +1,4 @@ -import type { Address } from 'types/api/address'; -import type { AddressParam } from 'types/api/addressParams'; +import type { Address, AddressParam } from 'client/slices/address/types/api'; import { publicTag, privateTag, watchlistName } from 'mocks/address/tag'; import { tokenInfo } from 'mocks/tokens/tokenInfo'; diff --git a/mocks/address/coinBalanceHistory.ts b/mocks/address/coinBalanceHistory.ts index cc78d75602..3f7c73e257 100644 --- a/mocks/address/coinBalanceHistory.ts +++ b/mocks/address/coinBalanceHistory.ts @@ -1,4 +1,4 @@ -import type { AddressCoinBalanceHistoryItem, AddressCoinBalanceHistoryResponse, AddressCoinBalanceHistoryChart } from 'types/api/address'; +import type { AddressCoinBalanceHistoryItem, AddressCoinBalanceHistoryResponse, AddressCoinBalanceHistoryChart } from 'client/slices/address/types/api'; export const base: AddressCoinBalanceHistoryItem = { block_number: 30367643, diff --git a/mocks/address/counters.ts b/mocks/address/counters.ts index 81f8baf62d..e2170ad927 100644 --- a/mocks/address/counters.ts +++ b/mocks/address/counters.ts @@ -1,4 +1,4 @@ -import type { AddressCounters } from 'types/api/address'; +import type { AddressCounters } from 'client/slices/address/types/api'; export const forAddress: AddressCounters = { gas_usage_count: '319340525', diff --git a/mocks/address/epochRewards.ts b/mocks/address/epochRewards.ts index 5b42bf3a46..a71af33bb9 100644 --- a/mocks/address/epochRewards.ts +++ b/mocks/address/epochRewards.ts @@ -1,4 +1,4 @@ -import type { AddressEpochRewardsResponse } from 'types/api/address'; +import type { AddressEpochRewardsResponse } from 'client/features/chain-variants/celo/types/api'; import { tokenInfo } from 'mocks/tokens/tokenInfo'; diff --git a/mocks/address/tabCounters.ts b/mocks/address/tabCounters.ts index 27864a3ad3..c3fab20653 100644 --- a/mocks/address/tabCounters.ts +++ b/mocks/address/tabCounters.ts @@ -1,4 +1,4 @@ -import type { AddressTabsCounters } from 'types/api/address'; +import type { AddressTabsCounters } from 'client/slices/address/types/api'; export const base: AddressTabsCounters = { internal_transactions_count: 13, diff --git a/mocks/address/tag.ts b/mocks/address/tag.ts index bb1b066675..73f26f3705 100644 --- a/mocks/address/tag.ts +++ b/mocks/address/tag.ts @@ -1,4 +1,4 @@ -import type { AddressTag, WatchlistName } from 'types/api/addressParams'; +import type { AddressTag, WatchlistName } from 'client/slices/address/types/api'; export const privateTag: AddressTag = { label: 'my-private-tag', diff --git a/mocks/address/tokens.ts b/mocks/address/tokens.ts index 7505eebc57..4cabdc1b90 100644 --- a/mocks/address/tokens.ts +++ b/mocks/address/tokens.ts @@ -1,4 +1,4 @@ -import type { AddressCollectionsResponse, AddressNFTsResponse, AddressTokenBalance, AddressTokensResponse } from 'types/api/address'; +import type { AddressCollectionsResponse, AddressNFTsResponse, AddressTokenBalance, AddressTokensResponse } from 'client/slices/address/types/api'; import * as tokens from 'mocks/tokens/tokenInfo'; import * as tokenInstance from 'mocks/tokens/tokenInstance'; diff --git a/mocks/address/widgets.ts b/mocks/address/widgets.ts index 849b0dfab5..512f752b17 100644 --- a/mocks/address/widgets.ts +++ b/mocks/address/widgets.ts @@ -1,4 +1,4 @@ -import type { Address3rdPartyWidget } from 'types/views/address'; +import type { Address3rdPartyWidget } from 'client/features/address-3rd-party-widgets/types/view'; export const widgets = [ 'widget-1', @@ -23,6 +23,7 @@ export const config: Record = { title: 'Value', hint: 'Hint', valuePath: 'value', + valueTitlePath: 'valueTitle', }, 'widget-2': { name: 'Widget 2', diff --git a/mocks/config/footerLinks.ts b/mocks/config/footerLinks.ts index 4188db770c..c01658b11b 100644 --- a/mocks/config/footerLinks.ts +++ b/mocks/config/footerLinks.ts @@ -6,7 +6,7 @@ export const FOOTER_LINKS: Array = [ links: [ { text: 'Advertise', - url: 'https://coinzilla.com/', + url: 'https://sevio.com/', }, { text: 'Staking', diff --git a/mocks/deposits/deposits.ts b/mocks/deposits/deposits.ts index 64dfd3ada9..d8f4e1fa23 100644 --- a/mocks/deposits/deposits.ts +++ b/mocks/deposits/deposits.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from 'types/api/addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; import type { DepositsResponse } from 'types/api/deposits'; export const data: DepositsResponse = { diff --git a/mocks/metadata/address.ts b/mocks/metadata/address.ts index 3de38b8c45..e113a08c6b 100644 --- a/mocks/metadata/address.ts +++ b/mocks/metadata/address.ts @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import type { AddressMetadataTagApi } from 'types/api/addressMetadata'; +import type { AddressMetadataTagApi } from 'client/features/address-metadata/types/api'; export const nameTag: AddressMetadataTagApi = { slug: 'quack-quack', diff --git a/mocks/metadata/appActionButton.ts b/mocks/metadata/appActionButton.ts index f8d152deaa..edf2ea6eb9 100644 --- a/mocks/metadata/appActionButton.ts +++ b/mocks/metadata/appActionButton.ts @@ -1,4 +1,4 @@ -import type { AddressMetadataTagApi } from 'types/api/addressMetadata'; +import type { AddressMetadataTagApi } from 'client/features/address-metadata/types/api'; const appID = 'uniswap'; const appMarketplaceURL = 'https://example.com'; diff --git a/mocks/mud/mudTables.ts b/mocks/mud/mudTables.ts index b5f0186c91..1d3c176585 100644 --- a/mocks/mud/mudTables.ts +++ b/mocks/mud/mudTables.ts @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import type { AddressMudRecord, AddressMudRecords, AddressMudRecordsItem, AddressMudTables } from 'types/api/address'; +import type { AddressMudRecord, AddressMudRecords, AddressMudRecordsItem, AddressMudTables } from 'client/features/chain-variants/mud/types/api'; import type { MudWorldSchema, MudWorldTable } from 'types/api/mudWorlds'; export const table1: MudWorldTable = { diff --git a/mocks/multichain/chains.ts b/mocks/multichain/chains.ts index 25729146dc..ddc44c971a 100644 --- a/mocks/multichain/chains.ts +++ b/mocks/multichain/chains.ts @@ -59,6 +59,11 @@ export const chainA = { }, }, }, + services: { + reCaptchaV2: { + siteKey: 'xxx', + }, + }, }, } as unknown as ClusterChainConfig; diff --git a/mocks/multichain/stats.ts b/mocks/multichain/stats.ts index d7cdf8dfd8..8fbdc8c79c 100644 --- a/mocks/multichain/stats.ts +++ b/mocks/multichain/stats.ts @@ -1,7 +1,7 @@ import type * as multichain from '@blockscout/multichain-aggregator-types'; import type * as stats from '@blockscout/stats-types'; -import { averageGasPrice } from 'mocks/stats/line'; +import { averageGasPrice } from 'client/features/chain-stats/mocks/line'; import { chainA, chainB, chainC } from './chains'; diff --git a/mocks/noves/transaction.ts b/mocks/noves/transaction.ts index 6feb72a564..09f2e0a66d 100644 --- a/mocks/noves/transaction.ts +++ b/mocks/noves/transaction.ts @@ -1,6 +1,6 @@ import type { NovesResponseData } from 'types/api/noves'; -import type { TokensData } from 'ui/tx/assetFlows/utils/getTokensData'; +import type { TokensData } from 'client/features/tx-interpretation/noves/utils/getTokensData'; export const hash = '0x380400d04ebb4179a35b1d7fdef260776915f015e978f8587ef2704b843d4e53'; diff --git a/mocks/stats/main.tsx b/mocks/stats/main.tsx index a3a6bbd016..d3b7e3f69c 100644 --- a/mocks/stats/main.tsx +++ b/mocks/stats/main.tsx @@ -1,6 +1,6 @@ import type * as stats from '@blockscout/stats-types'; -import { averageGasPrice } from './line'; +import { averageGasPrice } from 'client/features/chain-stats/mocks/line'; export const base: stats.MainPageStats = { average_block_time: { @@ -19,7 +19,7 @@ export const base: stats.MainPageStats = { total_blocks: { id: 'totalBlocks', value: '7660515', - title: 'Total blocks', + title: 'Latest block', description: 'Number of blocks over all time', }, total_transactions: { diff --git a/mocks/withdrawals/withdrawals.ts b/mocks/withdrawals/withdrawals.ts index 97742fe3d6..6b6a6d5ad1 100644 --- a/mocks/withdrawals/withdrawals.ts +++ b/mocks/withdrawals/withdrawals.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from 'types/api/addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; import type { WithdrawalsResponse } from 'types/api/withdrawals'; export const data: WithdrawalsResponse = { diff --git a/mocks/zkEvm/deposits.ts b/mocks/zkEvm/deposits.ts deleted file mode 100644 index 91ecc077c3..0000000000 --- a/mocks/zkEvm/deposits.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ZkEvmL2DepositsResponse } from 'types/api/zkEvmL2'; - -export const baseResponse: ZkEvmL2DepositsResponse = { - items: [ - { - block_number: 19681943, - index: 182177, - l1_transaction_hash: '0x29074452f976064aca1ca5c6e7c82d890c10454280693e6eca0257ae000c8e85', - l2_transaction_hash: null, - symbol: 'DAI', - timestamp: '2022-04-18T11:08:11.000000Z', - value: '0.003', - }, - { - block_number: 19681894, - index: 182176, - l1_transaction_hash: '0x0b7d58c0a6b4695ba28d99df928591fb931c812c0aab6d0093ff5040d2f9bc5e', - l2_transaction_hash: '0x210d9f70f411de1079e32a98473b04345a5ea6ff2340a8511ebc2df641274436', - symbol: 'ETH', - timestamp: '2022-04-18T10:58:23.000000Z', - value: '0.0046651390188845', - }, - ], - next_page_params: { - items_count: 50, - index: 1, - }, -}; diff --git a/mocks/zkEvm/txnBatches.ts b/mocks/zkEvm/txnBatches.ts deleted file mode 100644 index d7e73dd772..0000000000 --- a/mocks/zkEvm/txnBatches.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesResponse } from 'types/api/zkEvmL2'; - -export const txnBatchData: ZkEvmL2TxnBatch = { - acc_input_hash: '0x4bf88aabe33713b7817266d7860912c58272d808da7397cdc627ca53b296fad3', - global_exit_root: '0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5', - number: 5, - sequence_transaction_hash: '0x7ae010e9758441b302db10282807358af460f38c49c618d26a897592f64977f7', - state_root: '0x183b4a38a4a6027947ceb93b323cc94c548c8c05cf605d73ca88351d77cae1a3', - status: 'Finalized', - timestamp: '2023-10-20T10:08:18.000000Z', - transactions: [ - '0xb5d432c270057c223b973f3b5f00dbad32823d9ef26f3e8d97c819c7c573453a', - ], - verify_transaction_hash: '0x6f7eeaa0eb966e63d127bba6bf8f9046d303c2a1185b542f0b5083f682a0e87f', -}; - -export const txnBatchesData: ZkEvmL2TxnBatchesResponse = { - items: [ - { - timestamp: '2023-06-01T14:46:48.000000Z', - status: 'Finalized', - verify_transaction_hash: '0x48139721f792d3a68c3781b4cf50e66e8fc7dbb38adff778e09066ea5be9adb8', - sequence_transaction_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b', - number: 5218590, - transactions_count: 9, - }, - { - timestamp: '2023-06-01T14:46:48.000000Z', - status: 'Unfinalized', - verify_transaction_hash: null, - sequence_transaction_hash: null, - number: 5218591, - transactions_count: 9, - }, - ], - next_page_params: { - number: 5902834, - items_count: 50, - }, -}; diff --git a/mocks/zkEvm/withdrawals.ts b/mocks/zkEvm/withdrawals.ts deleted file mode 100644 index c89635f4e5..0000000000 --- a/mocks/zkEvm/withdrawals.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ZkEvmL2WithdrawalsResponse } from 'types/api/zkEvmL2'; - -export const baseResponse: ZkEvmL2WithdrawalsResponse = { - items: [ - { - block_number: 11722417, - index: 47040, - l1_transaction_hash: null, - l2_transaction_hash: '0x68c378e412e51553524545ef1d3f00f69496fb37827c0b3b7e0870d245970408', - symbol: 'ETH', - timestamp: '2022-04-18T09:20:37.000000Z', - value: '0.025', - }, - { - block_number: 11722480, - index: 47041, - l1_transaction_hash: '0xbf76feb85b8b8f24dacb17f962dd359f82efc512928d7b11ffca92fb812ad6a5', - l2_transaction_hash: '0xfe3c168ac1751b8399f1e819f1d83ee4cf764128bc604d454abee29114dabf49', - symbol: 'ETH', - timestamp: '2022-04-18T09:23:45.000000Z', - value: '4', - }, - ], - next_page_params: { - items_count: 50, - index: 1, - }, -}; diff --git a/next-env.d.ts b/next-env.d.ts index d3956e1409..7996d352f4 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import './.next/dev/types/routes.d.ts'; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/next.config.js b/next.config.js index abba6271fb..12434eb265 100644 --- a/next.config.js +++ b/next.config.js @@ -74,6 +74,9 @@ const moduleExports = { 'static': 180, }, }, + + // workaround for passing outDir to nextjs-routes CLI + outDir: 'nextjs', }; module.exports = withBundleAnalyzer(withRoutes(moduleExports)); diff --git a/nextjs/PageMetadata.tsx b/nextjs/PageMetadata.tsx index 2d72fade19..e0ef5f9301 100644 --- a/nextjs/PageMetadata.tsx +++ b/nextjs/PageMetadata.tsx @@ -4,8 +4,9 @@ import React from 'react'; import type { Route } from 'nextjs-routes'; import type { Props as PageProps } from 'nextjs/getServerSideProps/handlers'; +import * as metadata from 'client/shared/metadata'; + import config from 'configs/app'; -import * as metadata from 'lib/metadata'; interface Props { pathname: Pathname; diff --git a/nextjs/PageNextJs.tsx b/nextjs/PageNextJs.tsx index 39759cd51c..53c399aa8f 100644 --- a/nextjs/PageNextJs.tsx +++ b/nextjs/PageNextJs.tsx @@ -3,11 +3,12 @@ import React from 'react'; import type { Route } from 'nextjs-routes'; import type { Props as PageProps } from 'nextjs/getServerSideProps/handlers'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useIsMounted from 'client/shared/hooks/useIsMounted'; + import useAdblockDetect from 'lib/hooks/useAdblockDetect'; import useGetCsrfToken from 'lib/hooks/useGetCsrfToken'; -import useIsMounted from 'lib/hooks/useIsMounted'; import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation'; -import * as mixpanel from 'lib/mixpanel'; interface Props { pathname: Pathname; diff --git a/nextjs/csp/index.ts b/nextjs/csp/index.ts index c6d3b399d3..3db788854f 100644 --- a/nextjs/csp/index.ts +++ b/nextjs/csp/index.ts @@ -8,8 +8,11 @@ import * as cookiesLib from 'lib/cookies'; import generateCspPolicy from './generateCspPolicy'; import generateNftHtmlEmbedCspPolicy from './generateNftHtmlEmbedCspPolicy'; +<<<<<<< HEAD const marketplaceFeature = appConfig.features.marketplace; +======= +>>>>>>> v2.8.0-alpha.1 const NFT_HTML_EMBED_PATH = '/nft-html-embed.html'; let cspPolicies: { 'private': string; 'default': string } | undefined = undefined; diff --git a/nextjs/csp/policies/ad.ts b/nextjs/csp/policies/ad.ts index d14a64afcd..cfa8755630 100644 --- a/nextjs/csp/policies/ad.ts +++ b/nextjs/csp/policies/ad.ts @@ -7,13 +7,11 @@ import { connectAdbutler, placeAd } from 'ui/shared/ad/adbutlerScript'; export function ad(): CspDev.DirectiveDescriptor { return { 'connect-src': [ - // coinzilla - 'coinzilla.com', - '*.coinzilla.com', - 'https://request-global.czilladx.com', - - // sevio (coinzilla text ad) + // sevio '*.adx.ws', + 'https://id5-sync.com', + 'https://lb.eu-1-id5-sync.com/lb/v1', + 'https://request-global.czilladx.com', // adbutler 'servedbyadbutler.com', @@ -25,13 +23,10 @@ export function ad(): CspDev.DirectiveDescriptor { 'app.specify.sh', ], 'frame-src': [ - // coinzilla + // sevio 'https://request-global.czilladx.com', ], 'script-src': [ - // coinzilla - 'coinzillatag.com', - // adbutler 'servedbyadbutler.com', `'sha256-${ Base64.stringify(sha256(connectAdbutler)) }'`, @@ -45,18 +40,13 @@ export function ad(): CspDev.DirectiveDescriptor { 'cdn.adx.ws', ], 'img-src': [ - // coinzilla - 'cdn.coinzilla.io', - // adbutler 'servedbyadbutler.com', // sevio '*.adx.ws', - ], - 'font-src': [ - // coinzilla 'https://request-global.czilladx.com', ], + 'font-src': [], }; } diff --git a/nextjs/csp/policies/monaco.ts b/nextjs/csp/policies/monaco.ts index ea019817c8..11b7610104 100644 --- a/nextjs/csp/policies/monaco.ts +++ b/nextjs/csp/policies/monaco.ts @@ -7,7 +7,9 @@ export function monaco(): CspDev.DirectiveDescriptor { 'script-src': [ KEY_WORDS.BLOB, 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/loader.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/loader.js.map', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/editor/editor.main.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/editor/editor.main.js.map', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/editor/editor.main.nls.js', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/basic-languages/solidity/solidity.js', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/basic-languages/elixir/elixir.js', @@ -20,6 +22,10 @@ export function monaco(): CspDev.DirectiveDescriptor { 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/language/typescript/tsWorker.js', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/base/worker/workerMain.js', ], + 'connect-src': [ + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min-maps/vs/loader.js.map', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min-maps/vs/editor/editor.main.js.map', + ], 'style-src': [ 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/editor/editor.main.css', ], diff --git a/nextjs/getServerSideProps/guards.ts b/nextjs/getServerSideProps/guards.ts index 099095e669..f4a59a6703 100644 --- a/nextjs/getServerSideProps/guards.ts +++ b/nextjs/getServerSideProps/guards.ts @@ -68,14 +68,6 @@ export const apiDocs: Guard = (chainConfig: typeof config) => async() => { } }; -export const csvExport: Guard = (chainConfig: typeof config) => async() => { - if (!chainConfig.features.csvExport.isEnabled) { - return { - notFound: true, - }; - } -}; - export const stats: Guard = (chainConfig: typeof config) => async() => { if (!chainConfig.features.stats.isEnabled) { return { @@ -224,7 +216,7 @@ export const rollup: Guard = (chainConfig: typeof config) => async() => { } }; -const DEPOSITS_ROLLUP_TYPES: Array = [ 'optimistic', 'shibarium', 'zkEvm', 'arbitrum', 'scroll' ]; +const DEPOSITS_ROLLUP_TYPES: Array = [ 'optimistic', 'shibarium', 'arbitrum', 'scroll' ]; export const deposits: Guard = (chainConfig: typeof config) => async() => { const rollupFeature = chainConfig.features.rollup; const beaconChainFeature = chainConfig.features.beaconChain; @@ -237,7 +229,7 @@ export const deposits: Guard = (chainConfig: typeof config) => async() => { } }; -const WITHDRAWALS_ROLLUP_TYPES: Array = [ 'optimistic', 'shibarium', 'zkEvm', 'arbitrum', 'scroll' ]; +const WITHDRAWALS_ROLLUP_TYPES: Array = [ 'optimistic', 'shibarium', 'arbitrum', 'scroll' ]; export const withdrawals: Guard = (chainConfig: typeof config) => async() => { const rollupFeature = chainConfig.features.rollup; if ( @@ -250,7 +242,7 @@ export const withdrawals: Guard = (chainConfig: typeof config) => async() => { } }; -const BATCH_ROLLUP_TYPES: Array = [ 'zkEvm', 'zkSync', 'arbitrum', 'optimistic', 'scroll' ]; +const BATCH_ROLLUP_TYPES: Array = [ 'zkSync', 'arbitrum', 'optimistic', 'scroll' ]; export const batch: Guard = (chainConfig: typeof config) => async() => { const rollupFeature = chainConfig.features.rollup; if (!(rollupFeature.isEnabled && BATCH_ROLLUP_TYPES.includes(rollupFeature.type))) { diff --git a/nextjs/getServerSideProps/handlers.ts b/nextjs/getServerSideProps/handlers.ts index d73843063c..43ebde5285 100644 --- a/nextjs/getServerSideProps/handlers.ts +++ b/nextjs/getServerSideProps/handlers.ts @@ -4,9 +4,10 @@ import type { AdBannerProviders } from 'types/client/adProviders'; import type { Route } from 'nextjs-routes'; +import type * as metadata from 'client/shared/metadata'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; -import type * as metadata from 'lib/metadata'; import { isLikelyHumanBrowser, isKnownBotRequest } from '../utils/checkRealBrowser'; diff --git a/nextjs/getServerSideProps/main.ts b/nextjs/getServerSideProps/main.ts index 59580af45b..cb9642e6f8 100644 --- a/nextjs/getServerSideProps/main.ts +++ b/nextjs/getServerSideProps/main.ts @@ -12,7 +12,6 @@ export const userOps = factory([ guards.userOps ]); export const marketplace = factory([ guards.marketplace ]); export const marketplaceEssentialDapp = factory([ guards.marketplaceEssentialDapp ]); export const apiDocs = factory([ guards.apiDocs ]); -export const csvExport = factory([ guards.csvExport ]); export const stats = factory([ guards.stats ]); export const suave = factory([ guards.suave ]); export const nameServiceEns = factory([ guards.nameServiceEns ]); diff --git a/nextjs/getServerSideProps/multichain.ts b/nextjs/getServerSideProps/multichain.ts index b7ddee2274..438b01d1e4 100644 --- a/nextjs/getServerSideProps/multichain.ts +++ b/nextjs/getServerSideProps/multichain.ts @@ -5,4 +5,3 @@ export const base = factoryMultichain([ guards.multichain ]); export const userOps = factoryMultichain([ guards.multichain, guards.userOps ]); export const accountsLabelSearch = factoryMultichain([ guards.multichain, guards.accountsLabelSearch ]); export const advancedFilter = factoryMultichain([ guards.multichain, guards.advancedFilter ]); -export const csvExport = factoryMultichain([ guards.multichain, guards.csvExport ]); diff --git a/nextjs/getServerSideProps/utils.ts b/nextjs/getServerSideProps/utils.ts index a632febf8a..3af28d3d99 100644 --- a/nextjs/getServerSideProps/utils.ts +++ b/nextjs/getServerSideProps/utils.ts @@ -35,8 +35,8 @@ export const factory = (guards: Array>, chainConfig = confi export const factoryMultichain = (guards: Array) => { return async(context: GetServerSidePropsContext) => { - const chainSlug = context.params?.chain_slug; - const chain = multichainConfig()?.chains.find((chain) => chain.slug === chainSlug); + const chainSlugOrId = context.params?.chain_slug_or_id; + const chain = multichainConfig()?.chains.find((chain) => chain.slug === chainSlugOrId || chain.id === chainSlugOrId); if (!chain?.app_config) { return { diff --git a/nextjs/middlewares/account.ts b/nextjs/middlewares/account.ts index 7ab562542f..bad559becb 100644 --- a/nextjs/middlewares/account.ts +++ b/nextjs/middlewares/account.ts @@ -1,8 +1,9 @@ import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; export function account(req: NextRequest) { const feature = config.features.account; diff --git a/nextjs/middlewares/addressFormat.ts b/nextjs/middlewares/addressFormat.ts index 3925a1c673..a892b77a42 100644 --- a/nextjs/middlewares/addressFormat.ts +++ b/nextjs/middlewares/addressFormat.ts @@ -1,9 +1,10 @@ import type { NextRequest, NextResponse } from 'next/server'; -import type { AddressFormat } from 'types/views/address'; +import type { AddressFormat } from 'client/slices/address/types/view'; + +import * as cookiesLib from 'client/shared/storage/cookies'; import config from 'configs/app'; -import * as cookiesLib from 'lib/cookies'; export default function addressFormatMiddleware(req: NextRequest, res: NextResponse) { const addressFormatCookie = req.cookies.get(cookiesLib.NAMES.ADDRESS_FORMAT); diff --git a/nextjs/middlewares/appProfile.ts b/nextjs/middlewares/appProfile.ts index 15af875b37..bdf74560d9 100644 --- a/nextjs/middlewares/appProfile.ts +++ b/nextjs/middlewares/appProfile.ts @@ -1,6 +1,6 @@ import type { NextRequest, NextResponse } from 'next/server'; -import * as cookiesLib from 'lib/cookies'; +import * as cookiesLib from 'client/shared/storage/cookies'; const APP_PROFILE_HEADER = 'x-app-profile'; const APP_PROFILE_QUERY_PARAM = 'app-profile'; diff --git a/nextjs/middlewares/colorTheme.ts b/nextjs/middlewares/colorTheme.ts index d713eb7cac..2258f6bbd5 100644 --- a/nextjs/middlewares/colorTheme.ts +++ b/nextjs/middlewares/colorTheme.ts @@ -1,7 +1,8 @@ import type { NextRequest, NextResponse } from 'next/server'; +import * as cookiesLib from 'client/shared/storage/cookies'; + import appConfig from 'configs/app'; -import * as cookiesLib from 'lib/cookies'; export default function colorThemeMiddleware(req: NextRequest, res: NextResponse) { const colorModeCookie = req.cookies.get(cookiesLib.NAMES.COLOR_MODE); diff --git a/nextjs/middlewares/poorReputationTokens.ts b/nextjs/middlewares/poorReputationTokens.ts index ec0bfbc463..9bbb88ebfc 100644 --- a/nextjs/middlewares/poorReputationTokens.ts +++ b/nextjs/middlewares/poorReputationTokens.ts @@ -1,7 +1,8 @@ import type { NextRequest, NextResponse } from 'next/server'; +import * as cookiesLib from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookiesLib from 'lib/cookies'; export default function poorReputationTokensMiddleware(req: NextRequest, res: NextResponse) { if (config.features.multichain.isEnabled) { diff --git a/nextjs/middlewares/scamTokens.ts b/nextjs/middlewares/scamTokens.ts index e344af31ed..c111159d81 100644 --- a/nextjs/middlewares/scamTokens.ts +++ b/nextjs/middlewares/scamTokens.ts @@ -1,7 +1,8 @@ import type { NextRequest, NextResponse } from 'next/server'; +import * as cookiesLib from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookiesLib from 'lib/cookies'; export default function scamTokensMiddleware(req: NextRequest, res: NextResponse) { if (config.UI.views.token.hideScamTokensEnabled) { diff --git a/nextjs/nextjs-routes.d.ts b/nextjs/nextjs-routes.d.ts index 021307ae08..9249fe2308 100644 --- a/nextjs/nextjs-routes.d.ts +++ b/nextjs/nextjs-routes.d.ts @@ -1,11 +1,17 @@ // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. // This file will be automatically regenerated when your Next.js server is running. -// nextjs-routes version: 1.0.8 +// nextjs-routes version: 2.2.5 /* eslint-disable */ // prettier-ignore declare module "nextjs-routes" { + import type { + GetServerSidePropsContext as NextGetServerSidePropsContext, + GetServerSidePropsResult as NextGetServerSidePropsResult + } from "next"; + export type Route = + | StaticRoute<"/"> | StaticRoute<"/404"> | StaticRoute<"/account/api-key"> | StaticRoute<"/account/custom-abi"> @@ -15,9 +21,10 @@ declare module "nextjs-routes" { | StaticRoute<"/account/watchlist"> | StaticRoute<"/accounts"> | DynamicRoute<"/accounts/label/[slug]", { "slug": string }> - | DynamicRoute<"/address/[hash]/contract-verification", { "hash": string }> | DynamicRoute<"/address/[hash]", { "hash": string }> + | DynamicRoute<"/address/[hash]/contract-verification", { "hash": string }> | StaticRoute<"/advanced-filter"> + | StaticRoute<"/api-docs"> | StaticRoute<"/api/config"> | StaticRoute<"/api/csrf"> | StaticRoute<"/api/healthz"> @@ -27,73 +34,70 @@ declare module "nextjs-routes" { | StaticRoute<"/api/monitoring/pageview"> | StaticRoute<"/api/proxy"> | DynamicRoute<"/api/tokens/[hash]/instances/[id]/media-type", { "hash": string; "id": string }> - | StaticRoute<"/api-docs"> - | DynamicRoute<"/apps/[id]", { "id": string }> | StaticRoute<"/apps"> + | DynamicRoute<"/apps/[id]", { "id": string }> | StaticRoute<"/auth/profile"> + | StaticRoute<"/batches"> | DynamicRoute<"/batches/[number]", { "number": string }> | DynamicRoute<"/batches/celestia/[height]/[commitment]", { "height": string; "commitment": string }> - | StaticRoute<"/batches"> | DynamicRoute<"/blobs/[hash]", { "hash": string }> | DynamicRoute<"/block/[height_or_hash]", { "height_or_hash": string }> - | DynamicRoute<"/block/countdown/[height]", { "height": string }> | StaticRoute<"/block/countdown"> + | DynamicRoute<"/block/countdown/[height]", { "height": string }> | StaticRoute<"/blocks"> | DynamicRoute<"/cc/tx/[hash]", { "hash": string }> - | DynamicRoute<"/chain/[chain_slug]/accounts/label/[slug]", { "chain_slug": string; "slug": string }> - | DynamicRoute<"/chain/[chain_slug]/advanced-filter", { "chain_slug": string }> - | DynamicRoute<"/chain/[chain_slug]/block/[height_or_hash]", { "chain_slug": string; "height_or_hash": string }> - | DynamicRoute<"/chain/[chain_slug]/block/countdown/[height]", { "chain_slug": string; "height": string }> - | DynamicRoute<"/chain/[chain_slug]/block/countdown", { "chain_slug": string }> - | DynamicRoute<"/chain/[chain_slug]/csv-export", { "chain_slug": string }> - | DynamicRoute<"/chain/[chain_slug]/op/[hash]", { "chain_slug": string; "hash": string }> - | DynamicRoute<"/chain/[chain_slug]/token/[hash]", { "chain_slug": string; "hash": string }> - | DynamicRoute<"/chain/[chain_slug]/token/[hash]/instance/[id]", { "chain_slug": string; "hash": string; "id": string }> - | DynamicRoute<"/chain/[chain_slug]/tx/[hash]", { "chain_slug": string; "hash": string }> - | DynamicRoute<"/chain/[chain_slug]/visualize/sol2uml", { "chain_slug": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/accounts/label/[slug]", { "chain_slug_or_id": string; "slug": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/advanced-filter", { "chain_slug_or_id": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/block/[height_or_hash]", { "chain_slug_or_id": string; "height_or_hash": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/block/countdown", { "chain_slug_or_id": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/block/countdown/[height]", { "chain_slug_or_id": string; "height": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/op/[hash]", { "chain_slug_or_id": string; "hash": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/token/[hash]", { "chain_slug_or_id": string; "hash": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/token/[hash]/instance/[id]", { "chain_slug_or_id": string; "hash": string; "id": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/tx/[hash]", { "chain_slug_or_id": string; "hash": string }> + | DynamicRoute<"/chain/[chain_slug_or_id]/visualize/sol2uml", { "chain_slug_or_id": string }> | StaticRoute<"/chakra"> | StaticRoute<"/contract-verification"> | DynamicRoute<"/cross-chain-tx/[id]", { "id": string }> - | StaticRoute<"/csv-export"> | StaticRoute<"/deposits"> | StaticRoute<"/dispute-games"> | StaticRoute<"/ecosystems"> - | DynamicRoute<"/epochs/[number]", { "number": string }> | StaticRoute<"/epochs"> + | DynamicRoute<"/epochs/[number]", { "number": string }> | DynamicRoute<"/essential-dapps/[id]", { "id": string }> | StaticRoute<"/gas-tracker"> | StaticRoute<"/hot-contracts"> - | StaticRoute<"/"> + | StaticRoute<"/ictt-users"> | StaticRoute<"/internal-txs"> | StaticRoute<"/interop-messages"> | StaticRoute<"/login"> | StaticRoute<"/mud-worlds"> + | StaticRoute<"/name-services"> | DynamicRoute<"/name-services/clusters/[name]", { "name": string }> | DynamicRoute<"/name-services/domains/[name]", { "name": string }> - | StaticRoute<"/name-services"> | DynamicRoute<"/op/[hash]", { "hash": string }> | DynamicRoute<"/operation/[id]", { "id": string }> | StaticRoute<"/operations"> | StaticRoute<"/ops"> | StaticRoute<"/output-roots"> - | DynamicRoute<"/pools/[hash]", { "hash": string }> | StaticRoute<"/pools"> + | DynamicRoute<"/pools/[hash]", { "hash": string }> | StaticRoute<"/public-tags/submit"> | StaticRoute<"/search-results"> | StaticRoute<"/sprite"> - | DynamicRoute<"/stats/[id]", { "id": string }> | StaticRoute<"/stats"> + | DynamicRoute<"/stats/[id]", { "id": string }> + | StaticRoute<"/token-transfers"> | DynamicRoute<"/token/[hash]", { "hash": string }> | DynamicRoute<"/token/[hash]/instance/[id]", { "hash": string; "id": string }> - | StaticRoute<"/token-transfers"> | StaticRoute<"/tokens"> | DynamicRoute<"/tx/[hash]", { "hash": string }> | StaticRoute<"/txn-withdrawals"> | StaticRoute<"/txs"> | DynamicRoute<"/txs/kettle/[hash]", { "hash": string }> | StaticRoute<"/uptime"> - | DynamicRoute<"/validators/[id]", { "id": string }> | StaticRoute<"/validators"> + | DynamicRoute<"/validators/[id]", { "id": string }> | StaticRoute<"/verified-contracts"> | StaticRoute<"/visualize/sol2uml"> | StaticRoute<"/withdrawals">; @@ -114,55 +118,79 @@ declare module "nextjs-routes" { [key: string]: string | string[] | undefined; }; - export type RoutedQuery

= Extract< + export type RoutedQuery

= Extract< Route, { pathname: P } >["query"]; export type Locale = undefined; + type Brand = K & { __brand: T }; + + /** + * A string that is a valid application route. + */ + export type RouteLiteral = Brand + /** * A typesafe utility function for generating paths in your application. * * route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar". */ - export declare function route(r: Route): string; + export declare function route(r: Route): RouteLiteral; + + /** + * Nearly identical to GetServerSidePropsContext from next, but further narrows + * types based on nextjs-route's route data. + */ + export type GetServerSidePropsContext< + Pathname extends Route["pathname"] = Route["pathname"], + Preview extends NextGetServerSidePropsContext["previewData"] = NextGetServerSidePropsContext["previewData"] + > = Omit & { + params: Extract["query"]; + query: Query; + defaultLocale?: undefined; + locale?: Locale; + locales?: undefined; + }; + + /** + * Nearly identical to GetServerSideProps from next, but further narrows + * types based on nextjs-route's route data. + */ + export type GetServerSideProps< + Props extends { [key: string]: any } = { [key: string]: any }, + Pathname extends Route["pathname"] = Route["pathname"], + Preview extends NextGetServerSideProps["previewData"] = NextGetServerSideProps["previewData"] + > = ( + context: GetServerSidePropsContext + ) => Promise> } // prettier-ignore declare module "next/link" { - import type { Route } from "nextjs-routes"; + import type { Route } from "nextjs-routes";; import type { LinkProps as NextLinkProps } from "next/dist/client/link"; - import type { - AnchorHTMLAttributes, - DetailedReactHTMLElement, - MouseEventHandler, - PropsWithChildren, - } from "react"; - export * from "next/dist/client/link"; - - type Query = { query?: { [key: string]: string | string[] | undefined } }; + import type React from "react"; + type StaticRoute = Exclude["pathname"]; - export interface LinkProps - extends Omit, - AnchorHTMLAttributes { - href: Route | StaticRoute | Query; + export type LinkProps = Omit & { + href: Route | StaticRoute | Omit; locale?: false; } - type LinkReactElement = DetailedReactHTMLElement< - { - onMouseEnter?: MouseEventHandler | undefined; - onClick: MouseEventHandler; - href?: string | undefined; - ref?: any; - }, - HTMLElement - >; - - declare function Link(props: PropsWithChildren): LinkReactElement; - + /** + * A React component that extends the HTML `` element to provide [prefetching](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching) + * and client-side navigation between routes. + * + * It is the primary way to navigate between routes in Next.js. + * + * Read more: [Next.js docs: ``](https://nextjs.org/docs/app/api-reference/components/link) + */ + declare const Link: React.ForwardRefExoticComponent, keyof LinkProps> & LinkProps & { + children?: React.ReactNode; + } & React.RefAttributes>; export default Link; } @@ -175,34 +203,49 @@ declare module "next/router" { type NextTransitionOptions = NonNullable[2]>; type StaticRoute = Exclude["pathname"]; - type Query = { query?: { [key: string]: string | string[] | undefined } }; interface TransitionOptions extends Omit { locale?: false; } + type PathnameAndQuery = Required< + Pick, "pathname" | "query"> + >; + + type AutomaticStaticOptimizedQuery = Omit & { + query: Partial; + }; + + type BaseRouter = + | ({ isReady: false } & AutomaticStaticOptimizedQuery) + | ({ isReady: true } & PaQ); + export type NextRouter

= - Extract & + BaseRouter> & Omit< Router, - | "push" - | "replace" - | "locale" - | "locales" | "defaultLocale" | "domainLocales" + | "isReady" + | "locale" + | "locales" + | "pathname" + | "push" + | "query" + | "replace" + | "route" > & { defaultLocale?: undefined; domainLocales?: undefined; locale?: Locale; locales?: undefined; push( - url: Route | StaticRoute | Query, + url: Route | StaticRoute | Omit, as?: string, options?: TransitionOptions ): Promise; replace( - url: Route | StaticRoute | Query, + url: Route | StaticRoute | Omit, as?: string, options?: TransitionOptions ): Promise; diff --git a/nextjs/redirects.js b/nextjs/redirects.js index 33cbed6862..ea0d999990 100644 --- a/nextjs/redirects.js +++ b/nextjs/redirects.js @@ -244,14 +244,6 @@ const OLD_UI_URLS = [ source: '/l2-txn-batches', destination: '/batches', }, - { - source: '/zkevm-l2-txn-batches', - destination: '/batches', - }, - { - source: '/zkevm-l2-txn-batch/:path*', - destination: '/batches/:path*', - }, { source: '/l2-deposits', destination: '/deposits', diff --git a/nextjs/routes.ts b/nextjs/routes.ts index 8070653d6d..38a487422e 100644 --- a/nextjs/routes.ts +++ b/nextjs/routes.ts @@ -22,8 +22,8 @@ export const route = (route: Route, params?: RouteParams | null) => { export const routeParams = (route: Route, params?: RouteParams | null): Route => { if (!params?.external && params?.chain?.slug) { - const pathname = '/chain/[chain_slug]' + route.pathname; - return { ...route, pathname, query: { ...route.query, chain_slug: params.chain.slug } } as Route; + const pathname = '/chain/[chain_slug_or_id]' + route.pathname; + return { ...route, pathname, query: { ...route.query, chain_slug_or_id: params.chain.slug } } as Route; } return route; }; diff --git a/nextjs/utils/buildUrl.ts b/nextjs/utils/buildUrl.ts index a068e9d097..0759a3b617 100644 --- a/nextjs/utils/buildUrl.ts +++ b/nextjs/utils/buildUrl.ts @@ -1,7 +1,7 @@ import { compile } from 'path-to-regexp'; -import getResourceParams from 'lib/api/getResourceParams'; -import type { ResourceName } from 'lib/api/resources'; +import getResourceParams from 'client/api/get-resource-params'; +import type { ResourceName } from 'client/api/resources'; export default function buildUrl( _resource: ResourceName, diff --git a/nextjs/utils/fetchApi.ts b/nextjs/utils/fetchApi.ts index 807d49f679..22c3eb067f 100644 --- a/nextjs/utils/fetchApi.ts +++ b/nextjs/utils/fetchApi.ts @@ -3,7 +3,8 @@ import fetch, { AbortError } from 'node-fetch'; import buildUrl from 'nextjs/utils/buildUrl'; import { httpLogger } from 'nextjs/utils/logger'; -import type { ResourceName, ResourcePathParams, ResourcePayload } from 'lib/api/resources'; +import type { ResourceName, ResourcePathParams, ResourcePayload } from 'client/api/resources'; + import metrics from 'lib/monitoring/metrics'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/package.json b/package.json index 52c02b1f13..9c2175bccb 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "start:docker:preset": "./tools/scripts/docker.preset.sh", "chakra:snippets:add": "chakra snippet add --outdir ./toolkit/chakra", "chakra:typegen": "chakra typegen ./toolkit/theme/theme.ts", + "routes:generate": "nextjs-routes", "lint:eslint": "eslint .", "lint:eslint:fix": "eslint . --fix", "lint:tsc": "tsc -p ./tsconfig.json", @@ -45,7 +46,7 @@ }, "dependencies": { "@blockscout/bens-types": "1.4.1", - "@blockscout/interchain-indexer-types": "0.0.10", + "@blockscout/interchain-indexer-types": "1.3.0", "@blockscout/multichain-aggregator-types": "2.1.2", "@blockscout/points-types": "1.4.0-alpha.1", "@blockscout/stats-types": "2.11.1", @@ -61,9 +62,13 @@ "@helia/verified-fetch": "2.6.12", "@metamask/post-message-stream": "^7.0.0", "@metamask/providers": "^10.2.1", - "@monaco-editor/react": "^4.7.0", + "@monaco-editor/react": "4.7.0", "@multisender.app/multisender-react-widget": "0.2.3", +<<<<<<< HEAD "@next/bundle-analyzer": "15.5.9", +======= + "@next/bundle-analyzer": "16.2.4", +>>>>>>> v2.8.0-alpha.1 "@nouns/assets": "^0.10.0", "@nouns/sdk": "^1.2.0", "@opentelemetry/api": "^1.4.1", @@ -84,6 +89,11 @@ "@specify-sh/sdk": "0.4.2", "@tanstack/react-query": "5.55.4", "@tanstack/react-query-devtools": "5.55.4", +<<<<<<< HEAD +======= + "@types/d3-selection": "3.0.11", + "@types/d3-transition": "3.0.9", +>>>>>>> v2.8.0-alpha.1 "@types/papaparse": "^5.3.5", "@types/react-scroll": "^1.8.4", "@uidotdev/usehooks": "2.4.1", @@ -92,9 +102,14 @@ "blo": "^1.1.1", "brotli-compress": "1.3.3", "crypto-js": "^4.2.0", - "cspell": "9.6.4", + "cspell": "9.8.0", "d3": "^7.6.1", "d3-sankey": "^0.12.3", +<<<<<<< HEAD +======= + "d3-selection": "3.0.0", + "d3-transition": "3.0.1", +>>>>>>> v2.8.0-alpha.1 "dappscout-iframe": "0.4.0", "dayjs": "^1.11.5", "dom-to-image": "^2.6.0", @@ -108,13 +123,18 @@ "js-cookie": "^3.0.1", "magic-bytes.js": "1.8.0", "mixpanel-browser": "2.67.0", +<<<<<<< HEAD "monaco-editor": "^0.34.1", "next": "15.5.10", +======= + "monaco-editor": "0.52.2", + "next": "16.2.4", +>>>>>>> v2.8.0-alpha.1 "next-themes": "0.4.4", - "nextjs-routes": "^1.0.8", + "nextjs-routes": "2.2.5", "node-fetch": "^3.2.9", "papaparse": "^5.3.2", - "path-to-regexp": "8.1.0", + "path-to-regexp": "8.4.2", "phoenix": "^1.6.15", "pino-http": "^8.2.1", "pino-pretty": "^9.1.1", @@ -143,12 +163,16 @@ "@chakra-ui/cli": "3.33.0", "@eslint/compat": "1.2.2", "@eslint/js": "9.14.0", +<<<<<<< HEAD "@next/eslint-plugin-next": "15.0.3", +======= + "@next/eslint-plugin-next": "16.2.4", +>>>>>>> v2.8.0-alpha.1 "@playwright/experimental-ct-react": "1.57.0", "@playwright/test": "1.57.0", "@stylistic/eslint-plugin": "5.2.3", "@svgr/webpack": "^6.5.1", - "@tanstack/eslint-plugin-query": "5.91.2", + "@tanstack/eslint-plugin-query": "5.100.5", "@testing-library/react": "^14.0.0", "@total-typescript/ts-reset": "^0.4.0", "@types/crypto-js": "^4.1.1", @@ -171,7 +195,12 @@ "css-loader": "^6.7.3", "dotenv-cli": "^6.0.0", "eslint": "9.39.2", +<<<<<<< HEAD "eslint-config-next": "15.4.10", +======= + "eslint-config-next": "16.2.4", + "eslint-plugin-boundaries": "6.0.2", +>>>>>>> v2.8.0-alpha.1 "eslint-plugin-consistent-default-export-name": "^0.0.15", "eslint-plugin-import": "2.31.0", "eslint-plugin-import-helpers": "2.0.1", @@ -186,7 +215,7 @@ "jsdom": "27.3.0", "license-report": "6.8.1", "license-report-check": "0.1.2", - "lint-staged": ">=10", + "lint-staged": "16.4.0", "mockdate": "^3.0.5", "style-loader": "^3.3.1", "svg-icons-cli": "0.0.8", @@ -203,6 +232,7 @@ "lint-staged": { "*.{js,jsx,ts,tsx}": "eslint --cache --fix" }, +<<<<<<< HEAD "resolutions": { "@types/react": "18.3.12", "@types/react-dom": "18.3.1", @@ -218,6 +248,21 @@ "dappscout-iframe/**/ws": "8.17.1", "viem/**/@noble/hashes": "1.8.0", "@walletconnect/ethereum-provider/**/@reown/appkit/**/@noble/hashes": "1.8.0" +======= + "pnpm": { + "overrides": { + "ws": "8.17.1", + "axios": "1.12.0", + "tar-fs": "2.1.4", + "node-forge": "1.3.2", + "brace-expansion": "1.1.12", + "js-yaml": "4.1.1", + "@noble/hashes": "1.8.0", + "vite-plugin-dts>brace-expansion": "2.0.2", + "vite-plugin-svgr>js-yaml": "4.1.1", + "protobufjs": "7.5.5" + } +>>>>>>> v2.8.0-alpha.1 }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/pages/_app.tsx b/pages/_app.tsx index b2a553de2f..d9af519058 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -11,18 +11,29 @@ import type { NextPageWithLayout } from 'nextjs/types'; import type { Route } from 'nextjs-routes'; import PageMetadata from 'nextjs/PageMetadata'; +import getSocketUrl from 'client/api/get-socket-url'; +import useQueryClientConfig from 'client/api/hooks/useQueryClientConfig'; +import { SocketProvider } from 'client/api/socket/context'; + +import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; + +import { initGrowthBook } from 'client/shared/feature-flags/init'; +import useLoadFeatures from 'client/shared/feature-flags/useLoadFeatures'; +import { clientConfig as rollbarConfig, Provider as RollbarProvider } from 'client/shared/monitoring/rollbar'; + import config from 'configs/app'; -import getSocketUrl from 'lib/api/getSocketUrl'; -import useQueryClientConfig from 'lib/api/useQueryClientConfig'; import { AppContextProvider } from 'lib/contexts/app'; import { FallbackProvider } from 'lib/contexts/fallback'; import { MarketplaceContextProvider } from 'lib/contexts/marketplace'; import { SettingsContextProvider } from 'lib/contexts/settings'; +<<<<<<< HEAD import { initGrowthBook } from 'lib/growthbook/init'; import useLoadFeatures from 'lib/growthbook/useLoadFeatures'; import usePageViewTracking from 'lib/monitoring/usePageViewTracking'; import { clientConfig as rollbarConfig, Provider as RollbarProvider } from 'lib/rollbar'; import { SocketProvider } from 'lib/socket/context'; +======= +>>>>>>> v2.8.0-alpha.1 import { Provider as ChakraProvider } from 'toolkit/chakra/provider'; import { Toaster } from 'toolkit/chakra/toaster'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; @@ -35,7 +46,7 @@ const RewardsContextProvider = dynamic(() => import('lib/contexts/rewards').then const RewardsLoginModal = dynamic(() => import('ui/rewards/login/RewardsLoginModal'), { ssr: false }); const RewardsActivityTracker = dynamic(() => import('ui/rewards/RewardsActivityTracker'), { ssr: false }); -import 'lib/setLocale'; +import 'client/shared/i18n/set-locale'; // import 'focus-visible/dist/focus-visible'; import 'nextjs/global.css'; @@ -117,7 +128,9 @@ function MyApp({ Component, pageProps, router }: AppPropsWithLayout) { - { content } + + { content } + diff --git a/pages/_error.tsx b/pages/_error.tsx index d48cd9db32..e979aff372 100644 --- a/pages/_error.tsx +++ b/pages/_error.tsx @@ -5,8 +5,9 @@ import Rollbar from 'rollbar'; import type { Props as ServerSidePropsCommon } from 'nextjs/getServerSideProps/handlers'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; const rollbarFeature = config.features.rollbar; const rollbar = rollbarFeature.isEnabled ? new Rollbar({ diff --git a/pages/accounts/index.tsx b/pages/accounts/index.tsx index 2a82a66e04..efbeb2295f 100644 --- a/pages/accounts/index.tsx +++ b/pages/accounts/index.tsx @@ -11,7 +11,7 @@ const Accounts = dynamic(() => { return import('ui/multichain/accounts/MultichainAccounts'); } - return import('ui/pages/Accounts'); + return import('client/slices/address/pages/index/Accounts'); }, { ssr: false }); const Page: NextPage = () => { diff --git a/pages/address/[hash]/index.tsx b/pages/address/[hash]/index.tsx index e547cd4b93..cefad508d1 100644 --- a/pages/address/[hash]/index.tsx +++ b/pages/address/[hash]/index.tsx @@ -8,10 +8,12 @@ import PageNextJs from 'nextjs/PageNextJs'; import detectBotRequest from 'nextjs/utils/detectBotRequest'; import fetchApi from 'nextjs/utils/fetchApi'; +import Address from 'client/slices/address/pages/details/Address'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import getQueryParamString from 'lib/router/getQueryParamString'; import MultichainAddress from 'ui/multichain/address/MultichainAddress'; -import Address from 'ui/pages/Address'; const pathname: Route['pathname'] = '/address/[hash]'; diff --git a/pages/api/csrf.ts b/pages/api/csrf.ts index ecedde4883..f33af1f814 100644 --- a/pages/api/csrf.ts +++ b/pages/api/csrf.ts @@ -4,7 +4,7 @@ import buildUrl from 'nextjs/utils/buildUrl'; import fetchFactory from 'nextjs/utils/fetchProxy'; import { httpLogger } from 'nextjs/utils/logger'; -import isNeedProxy from 'lib/api/isNeedProxy'; +import isNeedProxy from 'client/api/is-need-proxy'; export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) { if (!isNeedProxy()) { diff --git a/pages/api/monitoring/invalid-api-schema.ts b/pages/api/monitoring/invalid-api-schema.ts index dfb3942b85..db2fb2d5c7 100644 --- a/pages/api/monitoring/invalid-api-schema.ts +++ b/pages/api/monitoring/invalid-api-schema.ts @@ -1,11 +1,13 @@ import type { NextApiRequest, NextApiResponse } from 'next'; -import type { ApiName } from 'lib/api/types'; +import type { ApiName } from 'client/api/types'; import { httpLogger } from 'nextjs/utils/logger'; -import { RESOURCES } from 'lib/api/resources'; -import getErrorMessage from 'lib/errors/getErrorMessage'; +import { RESOURCES } from 'client/api/resources'; + +import getErrorMessage from 'client/shared/errors/get-error-message'; + import metrics from 'lib/monitoring/metrics'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { diff --git a/pages/api/proxy.ts b/pages/api/proxy.ts index 5d2e0d05d2..d50c66bfce 100644 --- a/pages/api/proxy.ts +++ b/pages/api/proxy.ts @@ -3,8 +3,9 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import fetchFactory from 'nextjs/utils/fetchProxy'; +import isNeedProxy from 'client/api/is-need-proxy'; + import appConfig from 'configs/app'; -import isNeedProxy from 'lib/api/isNeedProxy'; const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => { diff --git a/pages/api/tokens/[hash]/instances/[id]/media-type.ts b/pages/api/tokens/[hash]/instances/[id]/media-type.ts index 8aeb2734ec..1640e60f83 100644 --- a/pages/api/tokens/[hash]/instances/[id]/media-type.ts +++ b/pages/api/tokens/[hash]/instances/[id]/media-type.ts @@ -4,8 +4,9 @@ import nodeFetch from 'node-fetch'; import fetchApi from 'nextjs/utils/fetchApi'; import { httpLogger } from 'nextjs/utils/logger'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import metrics from 'lib/monitoring/metrics'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { SECOND } from 'toolkit/utils/consts'; export default async function tokenInstanceMediaTypeHandler(req: NextApiRequest, res: NextApiResponse) { diff --git a/pages/apps/[id].tsx b/pages/apps/[id].tsx index 364792e5eb..16277830e1 100644 --- a/pages/apps/[id].tsx +++ b/pages/apps/[id].tsx @@ -13,8 +13,9 @@ import PageNextJs from 'nextjs/PageNextJs'; import detectBotRequest from 'nextjs/utils/detectBotRequest'; import fetchApi from 'nextjs/utils/fetchApi'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import getQueryParamString from 'lib/router/getQueryParamString'; import LayoutApp from 'ui/shared/layout/LayoutApp'; const MarketplaceAppPage = dynamic(() => import('ui/pages/MarketplaceApp'), { ssr: false }); @@ -69,7 +70,7 @@ export const getServerSideProps: GetServerSideProps> = as } else { return await fetchApi({ resource: 'admin:marketplace_dapp', - pathParams: { dappId: getQueryParamString(ctx.query.id), chainId: config.chain.id }, + pathParams: { dappId: getQueryParamString(ctx.query.id), instanceId: config.apis.admin?.instanceId }, timeout: 1_000, }); } diff --git a/pages/batches/[number].tsx b/pages/batches/[number].tsx index dae405c97c..29a628ffef 100644 --- a/pages/batches/[number].tsx +++ b/pages/batches/[number].tsx @@ -19,8 +19,6 @@ const Batch = dynamic(() => { return import('ui/pages/ArbitrumL2TxnBatch'); case 'optimistic': return import('ui/pages/OptimisticL2TxnBatch'); - case 'zkEvm': - return import('ui/pages/ZkEvmL2TxnBatch'); case 'zkSync': return import('ui/pages/ZkSyncL2TxnBatch'); case 'scroll': diff --git a/pages/batches/index.tsx b/pages/batches/index.tsx index b3be2eb23f..afae13b66b 100644 --- a/pages/batches/index.tsx +++ b/pages/batches/index.tsx @@ -13,8 +13,6 @@ const Batches = dynamic(() => { } switch (rollupFeature.type) { - case 'zkEvm': - return import('ui/pages/ZkEvmL2TxnBatches'); case 'zkSync': return import('ui/pages/ZkSyncL2TxnBatches'); case 'optimistic': diff --git a/pages/block/[height_or_hash].tsx b/pages/block/[height_or_hash].tsx index 514cb542b4..81de82c519 100644 --- a/pages/block/[height_or_hash].tsx +++ b/pages/block/[height_or_hash].tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const Block = dynamic(() => import('ui/pages/Block'), { ssr: false }); +const Block = dynamic(() => import('client/slices/block/pages/details/Block'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/block/countdown/[height].tsx b/pages/block/countdown/[height].tsx index 3c44c2a63f..332fb24117 100644 --- a/pages/block/countdown/[height].tsx +++ b/pages/block/countdown/[height].tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const BlockCountdown = dynamic(() => import('ui/pages/BlockCountdown'), { ssr: false }); +const BlockCountdown = dynamic(() => import('client/slices/block/pages/countdown-details/BlockCountdown'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/block/countdown/index.tsx b/pages/block/countdown/index.tsx index 0213e439ab..b8bf31fe5a 100644 --- a/pages/block/countdown/index.tsx +++ b/pages/block/countdown/index.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; -const BlockCountdownIndex = dynamic(() => import('ui/pages/BlockCountdownIndex'), { ssr: false }); +const BlockCountdownIndex = dynamic(() => import('client/slices/block/pages/countdown-index/BlockCountdownIndex'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( diff --git a/pages/blocks.tsx b/pages/blocks.tsx index fa168a6a89..457fb888e8 100644 --- a/pages/blocks.tsx +++ b/pages/blocks.tsx @@ -11,7 +11,7 @@ const Blocks = dynamic(() => { return import('ui/multichain/blocks/MultichainBlocks'); } - return import('ui/pages/Blocks'); + return import('client/slices/block/pages/index/Blocks'); }, { ssr: false }); const Page: NextPage = () => { diff --git a/pages/chain/[chain_slug]/csv-export/index.tsx b/pages/chain/[chain_slug]/csv-export/index.tsx deleted file mode 100644 index c3fd451e03..0000000000 --- a/pages/chain/[chain_slug]/csv-export/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { NextPage } from 'next'; -import React from 'react'; - -import PageNextJs from 'nextjs/PageNextJs'; - -import { MultichainProvider } from 'lib/contexts/multichain'; -import CsvExport from 'ui/pages/CsvExport'; - -const Page: NextPage = () => { - return ( - - - - - - ); -}; - -export default Page; - -export { csvExport as getServerSideProps } from 'nextjs/getServerSideProps/multichain'; diff --git a/pages/chain/[chain_slug]/accounts/label/[slug].tsx b/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx similarity index 88% rename from pages/chain/[chain_slug]/accounts/label/[slug].tsx rename to pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx index 6b0e14244a..fa84edddb3 100644 --- a/pages/chain/[chain_slug]/accounts/label/[slug].tsx +++ b/pages/chain/[chain_slug_or_id]/accounts/label/[slug].tsx @@ -10,7 +10,7 @@ const AccountsLabelSearch = dynamic(() => import('ui/pages/AccountsLabelSearch') const Page: NextPage = () => { return ( - + diff --git a/pages/chain/[chain_slug]/advanced-filter/index.tsx b/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx similarity index 87% rename from pages/chain/[chain_slug]/advanced-filter/index.tsx rename to pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx index 4f95f5dff3..31e5f6dcd8 100644 --- a/pages/chain/[chain_slug]/advanced-filter/index.tsx +++ b/pages/chain/[chain_slug_or_id]/advanced-filter/index.tsx @@ -8,7 +8,7 @@ import AdvancedFilter from 'ui/pages/AdvancedFilter'; const Page: NextPage = () => { return ( - + diff --git a/pages/chain/[chain_slug]/block/[height_or_hash].tsx b/pages/chain/[chain_slug_or_id]/block/[height_or_hash].tsx similarity index 84% rename from pages/chain/[chain_slug]/block/[height_or_hash].tsx rename to pages/chain/[chain_slug_or_id]/block/[height_or_hash].tsx index 9d7ddb4299..107fb06274 100644 --- a/pages/chain/[chain_slug]/block/[height_or_hash].tsx +++ b/pages/chain/[chain_slug_or_id]/block/[height_or_hash].tsx @@ -9,7 +9,7 @@ const MultichainBlock = dynamic(() => import('ui/multichain/block/MultichainBloc const Page: NextPage = (props: Props) => { return ( - + ); diff --git a/pages/chain/[chain_slug]/block/countdown/[height].tsx b/pages/chain/[chain_slug_or_id]/block/countdown/[height].tsx similarity index 70% rename from pages/chain/[chain_slug]/block/countdown/[height].tsx rename to pages/chain/[chain_slug_or_id]/block/countdown/[height].tsx index bffdedd5d6..c2e2f6fe78 100644 --- a/pages/chain/[chain_slug]/block/countdown/[height].tsx +++ b/pages/chain/[chain_slug_or_id]/block/countdown/[height].tsx @@ -7,11 +7,11 @@ import PageNextJs from 'nextjs/PageNextJs'; import { MultichainProvider } from 'lib/contexts/multichain'; -const BlockCountdown = dynamic(() => import('ui/pages/BlockCountdown'), { ssr: false }); +const BlockCountdown = dynamic(() => import('client/slices/block/pages/countdown-details/BlockCountdown'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( - + diff --git a/pages/chain/[chain_slug]/block/countdown/index.tsx b/pages/chain/[chain_slug_or_id]/block/countdown/index.tsx similarity index 71% rename from pages/chain/[chain_slug]/block/countdown/index.tsx rename to pages/chain/[chain_slug_or_id]/block/countdown/index.tsx index 1a7714a103..d2035d753e 100644 --- a/pages/chain/[chain_slug]/block/countdown/index.tsx +++ b/pages/chain/[chain_slug_or_id]/block/countdown/index.tsx @@ -7,11 +7,11 @@ import PageNextJs from 'nextjs/PageNextJs'; import { MultichainProvider } from 'lib/contexts/multichain'; -const BlockCountdownIndex = dynamic(() => import('ui/pages/BlockCountdownIndex'), { ssr: false }); +const BlockCountdownIndex = dynamic(() => import('client/slices/block/pages/countdown-index/BlockCountdownIndex'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( - + diff --git a/pages/chain/[chain_slug]/op/[hash].tsx b/pages/chain/[chain_slug_or_id]/op/[hash].tsx similarity index 87% rename from pages/chain/[chain_slug]/op/[hash].tsx rename to pages/chain/[chain_slug_or_id]/op/[hash].tsx index 89e806bcc3..b4765ba8c0 100644 --- a/pages/chain/[chain_slug]/op/[hash].tsx +++ b/pages/chain/[chain_slug_or_id]/op/[hash].tsx @@ -11,7 +11,7 @@ const UserOp = dynamic(() => import('ui/pages/UserOp'), { ssr: false }); const Page: NextPage = (props: Props) => { return ( - + diff --git a/pages/chain/[chain_slug]/token/[hash]/index.tsx b/pages/chain/[chain_slug_or_id]/token/[hash]/index.tsx similarity index 100% rename from pages/chain/[chain_slug]/token/[hash]/index.tsx rename to pages/chain/[chain_slug_or_id]/token/[hash]/index.tsx diff --git a/pages/chain/[chain_slug]/token/[hash]/instance/[id].tsx b/pages/chain/[chain_slug_or_id]/token/[hash]/instance/[id].tsx similarity index 100% rename from pages/chain/[chain_slug]/token/[hash]/instance/[id].tsx rename to pages/chain/[chain_slug_or_id]/token/[hash]/instance/[id].tsx diff --git a/pages/chain/[chain_slug]/tx/[hash]/index.tsx b/pages/chain/[chain_slug_or_id]/tx/[hash]/index.tsx similarity index 85% rename from pages/chain/[chain_slug]/tx/[hash]/index.tsx rename to pages/chain/[chain_slug_or_id]/tx/[hash]/index.tsx index 2903fc429b..daf25596da 100644 --- a/pages/chain/[chain_slug]/tx/[hash]/index.tsx +++ b/pages/chain/[chain_slug_or_id]/tx/[hash]/index.tsx @@ -9,7 +9,7 @@ const MultichainTx = dynamic(() => import('ui/multichain/tx/MultichainTx'), { ss const Page: NextPage = (props: Props) => { return ( - + ); diff --git a/pages/chain/[chain_slug]/visualize/sol2uml.tsx b/pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx similarity index 100% rename from pages/chain/[chain_slug]/visualize/sol2uml.tsx rename to pages/chain/[chain_slug_or_id]/visualize/sol2uml.tsx diff --git a/pages/csv-export.tsx b/pages/csv-export.tsx deleted file mode 100644 index 58ad87f1fa..0000000000 --- a/pages/csv-export.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { NextPage } from 'next'; -import React from 'react'; - -import PageNextJs from 'nextjs/PageNextJs'; - -import CsvExport from 'ui/pages/CsvExport'; - -const Page: NextPage = () => { - return ( - - - - ); -}; - -export default Page; - -export { csvExport as getServerSideProps } from 'nextjs/getServerSideProps/main'; diff --git a/pages/deposits/index.tsx b/pages/deposits/index.tsx index 643737c982..2d34fbb110 100644 --- a/pages/deposits/index.tsx +++ b/pages/deposits/index.tsx @@ -21,10 +21,6 @@ const Deposits = dynamic(() => { return import('ui/pages/ShibariumDeposits'); } - if (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm') { - return import('ui/pages/ZkEvmL2Deposits'); - } - if (rollupFeature.isEnabled && rollupFeature.type === 'scroll') { return import('ui/pages/ScrollL2Deposits'); } diff --git a/pages/ictt-users.tsx b/pages/ictt-users.tsx new file mode 100644 index 0000000000..c731b54872 --- /dev/null +++ b/pages/ictt-users.tsx @@ -0,0 +1,18 @@ +import type { NextPage } from 'next'; +import React from 'react'; + +import PageNextJs from 'nextjs/PageNextJs'; + +import IcttUsers from 'client/features/cross-chain-txs/pages/ictt-users/IcttUsers'; + +const Page: NextPage = () => { + return ( + + + + ); +}; + +export default Page; + +export { crossChainTxs as getServerSideProps } from 'nextjs/getServerSideProps/main'; diff --git a/pages/stats/[id].tsx b/pages/stats/[id].tsx index 6edc3ef03d..2b017f6339 100644 --- a/pages/stats/[id].tsx +++ b/pages/stats/[id].tsx @@ -9,12 +9,13 @@ import PageNextJs from 'nextjs/PageNextJs'; import detectBotRequest from 'nextjs/utils/detectBotRequest'; import fetchApi from 'nextjs/utils/fetchApi'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; import { MultichainProvider } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; -import getQueryParamString from 'lib/router/getQueryParamString'; -const Chart = dynamic(() => import('ui/pages/Chart'), { ssr: false }); +const ChainStatsDetails = dynamic(() => import('client/features/chain-stats/pages/details/ChainStatsDetails'), { ssr: false }); const pathname: Route['pathname'] = '/stats/[id]'; @@ -23,10 +24,10 @@ const Page: NextPage> = (props: Props) = { config.features.multichain.isEnabled ? ( - + ) : ( - + ) } ); diff --git a/pages/stats/index.tsx b/pages/stats/index.tsx index 5cfcf4ec9d..1457131b60 100644 --- a/pages/stats/index.tsx +++ b/pages/stats/index.tsx @@ -3,14 +3,15 @@ import React from 'react'; import PageNextJs from 'nextjs/PageNextJs'; +import ChainStatsIndex from 'client/features/chain-stats/pages/index/ChainStatsIndex'; + import config from 'configs/app'; import MultichainStats from 'ui/multichain/stats/MultichainStats'; -import Stats from 'ui/pages/Stats'; const Page: NextPage = () => { return ( - { config.features.multichain.isEnabled ? : } + { config.features.multichain.isEnabled ? : } ); }; diff --git a/pages/token/[hash]/index.tsx b/pages/token/[hash]/index.tsx index cd2f380582..df633e7c26 100644 --- a/pages/token/[hash]/index.tsx +++ b/pages/token/[hash]/index.tsx @@ -8,8 +8,9 @@ import PageNextJs from 'nextjs/PageNextJs'; import detectBotRequest from 'nextjs/utils/detectBotRequest'; import fetchApi from 'nextjs/utils/fetchApi'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import getQueryParamString from 'lib/router/getQueryParamString'; import Token from 'ui/pages/Token'; const pathname: Route['pathname'] = '/token/[hash]'; diff --git a/pages/token/[hash]/instance/[id].tsx b/pages/token/[hash]/instance/[id].tsx index 5d88d2ab3d..b6ad57802a 100644 --- a/pages/token/[hash]/instance/[id].tsx +++ b/pages/token/[hash]/instance/[id].tsx @@ -8,8 +8,9 @@ import PageNextJs from 'nextjs/PageNextJs'; import detectBotRequest from 'nextjs/utils/detectBotRequest'; import fetchApi from 'nextjs/utils/fetchApi'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import getQueryParamString from 'lib/router/getQueryParamString'; import TokenInstance from 'ui/pages/TokenInstance'; const pathname: Route['pathname'] = '/token/[hash]/instance/[id]'; diff --git a/pages/tx/[hash].tsx b/pages/tx/[hash].tsx index 2c1eed9a1c..049355c83c 100644 --- a/pages/tx/[hash].tsx +++ b/pages/tx/[hash].tsx @@ -6,7 +6,7 @@ import type { Props } from 'nextjs/getServerSideProps/handlers'; import PageNextJs from 'nextjs/PageNextJs'; const Transaction = dynamic(() => { - return import('ui/pages/Transaction'); + return import('client/slices/tx/pages/details/Transaction'); }, { ssr: false }); const Page: NextPage = (props: Props) => { diff --git a/pages/txs/index.tsx b/pages/txs/index.tsx index 887199497d..bcd5121779 100644 --- a/pages/txs/index.tsx +++ b/pages/txs/index.tsx @@ -19,7 +19,7 @@ const Transactions = dynamic(() => { return import('ui/crossChain/txs/Transactions'); } - return import('ui/pages/Transactions'); + return import('client/slices/tx/pages/index/TxIndex'); }, { ssr: false }); const Page: NextPage = () => { diff --git a/pages/withdrawals/index.tsx b/pages/withdrawals/index.tsx index a50541a11e..aec39272b7 100644 --- a/pages/withdrawals/index.tsx +++ b/pages/withdrawals/index.tsx @@ -21,10 +21,6 @@ const Withdrawals = dynamic(() => { return import('ui/pages/ShibariumWithdrawals'); } - if (rollupFeature.isEnabled && rollupFeature.type === 'zkEvm') { - return import('ui/pages/ZkEvmL2Withdrawals'); - } - if (rollupFeature.isEnabled && rollupFeature.type === 'scroll') { return import('ui/pages/ScrollL2Withdrawals'); } diff --git a/playwright-ct.config.ts b/playwright-ct.config.ts index b2b0a52128..0892f99eb9 100644 --- a/playwright-ct.config.ts +++ b/playwright-ct.config.ts @@ -95,7 +95,7 @@ const config: PlaywrightTestConfig = defineConfig({ { find: '@metamask/providers', replacement: './playwright/mocks/modules/@metamask/providers.js' }, // Mock for growthbook to test feature flags - { find: 'lib/growthbook/useFeatureValue', replacement: './playwright/mocks/lib/growthbook/useFeatureValue.js' }, + { find: 'client/shared/feature-flags/useFeatureValue', replacement: './playwright/mocks/lib/growthbook/useFeatureValue.js' }, // Mock for reCaptcha hook { find: 'ui/shared/reCaptcha/useReCaptcha', replacement: './playwright/mocks/ui/shared/recaptcha/useReCaptcha.js' }, diff --git a/playwright/TestApp.tsx b/playwright/TestApp.tsx index e20c030599..0c487fd053 100644 --- a/playwright/TestApp.tsx +++ b/playwright/TestApp.tsx @@ -7,13 +7,16 @@ import { mainnet } from 'wagmi/chains'; import type { Props as PageProps } from 'nextjs/getServerSideProps/handlers'; +import { SocketProvider } from 'client/api/socket/context'; + +import { currentChain } from 'client/features/connect-wallet/utils/chains'; +import { CsvExportContextProvider } from 'client/features/csv-export/utils/context'; + import config from 'configs/app'; import { AppContextProvider } from 'lib/contexts/app'; import { MarketplaceContext } from 'lib/contexts/marketplace'; import { RewardsContextProvider } from 'lib/contexts/rewards'; import { SettingsContextProvider } from 'lib/contexts/settings'; -import { SocketProvider } from 'lib/socket/context'; -import { currentChain } from 'lib/web3/chains'; import { Provider as ChakraProvider } from 'toolkit/chakra/provider'; import { port as socketPort } from './utils/socket'; @@ -81,7 +84,9 @@ const TestApp = ({ children, withSocket, appContext = defaultAppContext, marketp - { children } + + { children } + diff --git a/playwright/fixtures/auth.ts b/playwright/fixtures/auth.ts index 0bda117392..17ecee26ad 100644 --- a/playwright/fixtures/auth.ts +++ b/playwright/fixtures/auth.ts @@ -1,7 +1,8 @@ import type { BrowserContext, TestFixture } from '@playwright/test'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; export function authenticateUser(context: BrowserContext) { context.addCookies([ { name: cookies.NAMES.API_TOKEN, value: 'foo', domain: config.app.host, path: '/' } ]); diff --git a/playwright/fixtures/mockApiResponse.ts b/playwright/fixtures/mockApiResponse.ts index 7ad6c9aff8..884ea1ede9 100644 --- a/playwright/fixtures/mockApiResponse.ts +++ b/playwright/fixtures/mockApiResponse.ts @@ -1,7 +1,7 @@ import type { TestFixture, Page } from '@playwright/test'; -import buildUrl from 'lib/api/buildUrl'; -import type { ResourceName, ResourcePayload } from 'lib/api/resources'; +import buildUrl from 'client/api/build-url'; +import type { ResourceName, ResourcePayload } from 'client/api/resources'; interface Options { pathParams?: Parameters>[1]; diff --git a/playwright/fixtures/mockEnvs.ts b/playwright/fixtures/mockEnvs.ts index d6365efc6a..356b038a9e 100644 --- a/playwright/fixtures/mockEnvs.ts +++ b/playwright/fixtures/mockEnvs.ts @@ -33,10 +33,6 @@ export const ENVS_MAP: Record> = { [ 'NEXT_PUBLIC_ROLLUP_TYPE', 'shibarium' ], [ 'NEXT_PUBLIC_ROLLUP_PARENT_CHAIN', '{"baseUrl":"https://localhost:3101"}' ], ], - zkEvmRollup: [ - [ 'NEXT_PUBLIC_ROLLUP_TYPE', 'zkEvm' ], - [ 'NEXT_PUBLIC_ROLLUP_PARENT_CHAIN', '{"baseUrl":"https://localhost:3101"}' ], - ], zkSyncRollup: [ [ 'NEXT_PUBLIC_ROLLUP_TYPE', 'zkSync' ], [ 'NEXT_PUBLIC_ROLLUP_PARENT_CHAIN', '{"baseUrl":"https://localhost:3101"}' ], diff --git a/playwright/fixtures/rewards.ts b/playwright/fixtures/rewards.ts index fc6052f477..897c963138 100644 --- a/playwright/fixtures/rewards.ts +++ b/playwright/fixtures/rewards.ts @@ -1,7 +1,8 @@ import type { BrowserContext, TestFixture } from '@playwright/test'; +import * as cookies from 'client/shared/storage/cookies'; + import config from 'configs/app'; -import * as cookies from 'lib/cookies'; // This JWT token contains 0xd789a607CEac2f0E14867de4EB15b15C9FFB5859 address const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIweGQ3ODlhNjA3Q0VhYzJmMEUxNDg2N2RlNEVCMTViMTVDOUZGQjU4NTkiLCJpYXQiOjE3MzA0NzAyNTIsImV4cCI6MTczMDQ3MDU1Mn0.uhWH59mJQhpWcK8RHaLQ-X_nieXZsYE-VdcPrjYNvp4'; // eslint-disable-line max-len diff --git a/playwright/fixtures/socketServer.ts b/playwright/fixtures/socketServer.ts index f49cd93191..034e156dc0 100644 --- a/playwright/fixtures/socketServer.ts +++ b/playwright/fixtures/socketServer.ts @@ -2,12 +2,12 @@ import type { TestFixture, Page } from '@playwright/test'; import type { WebSocket } from 'ws'; import { WebSocketServer } from 'ws'; -import type { AddressCoinBalanceHistoryItem, AddressTokensBalancesSocketMessage } from 'types/api/address'; -import type { NewBlockSocketResponse } from 'types/api/block'; +import type { AddressCoinBalanceHistoryItem, AddressTokensBalancesSocketMessage } from 'client/slices/address/types/api'; +import type { NewBlockSocketResponse } from 'client/slices/block/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; import type { SmartContractVerificationResponse } from 'types/api/contract'; import type { TokenInstanceMetadataSocketMessage } from 'types/api/token'; import type { TokenTransfer } from 'types/api/tokenTransfer'; -import type { Transaction } from 'types/api/transaction'; import { port as socketPort } from '../utils/socket'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c710add706..ae06e0a71f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,7 @@ overrides: '@noble/hashes': 1.8.0 vite-plugin-dts>brace-expansion: 2.0.2 vite-plugin-svgr>js-yaml: 4.1.1 + protobufjs: 7.5.5 importers: @@ -24,8 +25,8 @@ importers: specifier: 1.4.1 version: 1.4.1 '@blockscout/interchain-indexer-types': - specifier: 0.0.10 - version: 0.0.10 + specifier: 1.3.0 + version: 1.3.0 '@blockscout/multichain-aggregator-types': specifier: 2.1.2 version: 2.1.2 @@ -48,14 +49,24 @@ importers: specifier: 3.33.0 version: 3.33.0(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@dynamic-labs/ethereum': +<<<<<<< HEAD specifier: 4.67.2 version: 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) +======= + specifier: 4.74.1 + version: 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) +>>>>>>> v2.8.0-alpha.1 '@dynamic-labs/sdk-react-core': - specifier: 4.67.2 - version: 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + specifier: 4.74.1 + version: 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) '@dynamic-labs/wagmi-connector': +<<<<<<< HEAD specifier: 4.67.2 version: 4.67.2(50f5641ee895bbb4bced80afc9d83bd9) +======= + specifier: 4.74.1 + version: 4.74.1(f2fb078b83c33aaece8b74f7b77c1992) +>>>>>>> v2.8.0-alpha.1 '@emotion/react': specifier: 11.14.0 version: 11.14.0(@types/react@19.1.7)(react@19.1.4) @@ -75,14 +86,14 @@ importers: specifier: ^10.2.1 version: 10.2.1 '@monaco-editor/react': - specifier: ^4.7.0 - version: 4.7.0(monaco-editor@0.34.1)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + specifier: 4.7.0 + version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@multisender.app/multisender-react-widget': specifier: 0.2.3 version: 0.2.3(75cca8bf91576fd15aab39089e230044) '@next/bundle-analyzer': - specifier: 16.1.7 - version: 16.1.7(bufferutil@4.1.0)(utf-8-validate@6.0.6) + specifier: 16.2.4 + version: 16.2.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@nouns/assets': specifier: ^0.10.0 version: 0.10.0 @@ -149,6 +160,9 @@ importers: '@types/d3-selection': specifier: 3.0.11 version: 3.0.11 + '@types/d3-transition': + specifier: 3.0.9 + version: 3.0.9 '@types/papaparse': specifier: ^5.3.5 version: 5.5.2 @@ -177,8 +191,8 @@ importers: specifier: ^4.2.0 version: 4.2.0 cspell: - specifier: 9.6.4 - version: 9.6.4 + specifier: 9.8.0 + version: 9.8.0 d3: specifier: ^7.6.1 version: 7.9.0 @@ -188,6 +202,9 @@ importers: d3-selection: specifier: 3.0.0 version: 3.0.0 + d3-transition: + specifier: 3.0.1 + version: 3.0.1(d3-selection@3.0.0) dappscout-iframe: specifier: 0.4.0 version: 0.4.0(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) @@ -231,17 +248,17 @@ importers: specifier: 2.67.0 version: 2.67.0 monaco-editor: - specifier: ^0.34.1 - version: 0.34.1 + specifier: 0.52.2 + version: 0.52.2 next: - specifier: 16.1.7 - version: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + specifier: 16.2.4 + version: 16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) next-themes: specifier: 0.4.4 version: 0.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) nextjs-routes: - specifier: ^1.0.8 - version: 1.0.9(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) + specifier: 2.2.5 + version: 2.2.5(next@16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) node-fetch: specifier: ^3.2.9 version: 3.3.2 @@ -249,8 +266,8 @@ importers: specifier: ^5.3.2 version: 5.5.3 path-to-regexp: - specifier: 8.1.0 - version: 8.1.0 + specifier: 8.4.2 + version: 8.4.2 phoenix: specifier: ^1.6.15 version: 1.8.5 @@ -331,11 +348,11 @@ importers: specifier: 9.14.0 version: 9.14.0 '@next/eslint-plugin-next': - specifier: 16.1.7 - version: 16.1.7 + specifier: 16.2.4 + version: 16.2.4 '@playwright/experimental-ct-react': specifier: 1.57.0 - version: 1.57.0(@types/node@20.16.7)(terser@5.46.1)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2))(yaml@2.8.2) + version: 1.57.0(@types/node@20.16.7)(terser@5.46.1)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3))(yaml@2.8.3) '@playwright/test': specifier: 1.57.0 version: 1.57.0 @@ -346,8 +363,8 @@ importers: specifier: ^6.5.1 version: 6.5.1 '@tanstack/eslint-plugin-query': - specifier: 5.91.2 - version: 5.91.2(eslint@9.39.2)(typescript@5.9.2) + specifier: 5.100.5 + version: 5.100.5(eslint@9.39.2)(typescript@5.9.2) '@testing-library/react': specifier: ^14.0.0 version: 14.3.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -404,7 +421,7 @@ importers: version: 5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2) '@vitejs/plugin-react': specifier: ^4.0.0 - version: 4.3.4(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + version: 4.3.4(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) css-loader: specifier: ^6.7.3 version: 6.11.0(webpack@5.105.4(esbuild@0.25.12)) @@ -415,8 +432,11 @@ importers: specifier: 9.39.2 version: 9.39.2 eslint-config-next: - specifier: 16.1.7 - version: 16.1.7(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2) + specifier: 16.2.4 + version: 16.2.4(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2) + eslint-plugin-boundaries: + specifier: 6.0.2 + version: 6.0.2(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2) eslint-plugin-consistent-default-export-name: specifier: ^0.0.15 version: 0.0.15 @@ -443,7 +463,7 @@ importers: version: 2.6.0(eslint@9.39.2) eslint-plugin-vitest: specifier: 0.5.4 - version: 0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2)(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2)) + version: 0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2)(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3)) globals: specifier: 15.12.0 version: 15.12.0 @@ -460,7 +480,7 @@ importers: specifier: 0.1.2 version: 0.1.2 lint-staged: - specifier: '>=10' + specifier: 16.4.0 version: 16.4.0 mockdate: specifier: ^3.0.5 @@ -485,16 +505,16 @@ importers: version: 8.41.0(eslint@9.39.2)(typescript@5.9.2) vite-plugin-svgr: specifier: ^2.2.2 - version: 2.4.0(rollup@4.59.0)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + version: 2.4.0(rollup@4.59.0)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + version: 5.1.4(typescript@5.9.2)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) vitest: specifier: 4.0.15 - version: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2) + version: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3) vitest-fetch-mock: specifier: 0.4.5 - version: 0.4.5(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2)) + version: 0.4.5(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3)) ws: specifier: 8.17.1 version: 8.17.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) @@ -514,8 +534,8 @@ importers: specifier: 5.4.2 version: 5.4.2 vite: - specifier: 6.4.1 - version: 6.4.1(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.2) + specifier: 6.4.2 + version: 6.4.2(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.3) yup: specifier: ^1.2.0 version: 1.7.1 @@ -539,8 +559,8 @@ importers: specifier: 5.4.2 version: 5.4.2 vite: - specifier: 6.4.1 - version: 6.4.1(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.2) + specifier: 6.4.2 + version: 6.4.2(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.3) deploy/tools/favicon-generator: dependencies: @@ -560,8 +580,8 @@ importers: specifier: 5.4.2 version: 5.4.2 vite: - specifier: 6.4.1 - version: 6.4.1(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.2) + specifier: 6.4.2 + version: 6.4.2(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.3) deploy/tools/llms-txt-generator: dependencies: @@ -582,8 +602,8 @@ importers: specifier: 5.4.2 version: 5.4.2 vite: - specifier: 6.4.1 - version: 6.4.1(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.2) + specifier: 6.4.2 + version: 6.4.2(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.3) deploy/tools/multichain-config-generator: devDependencies: @@ -594,14 +614,14 @@ importers: specifier: 5.4.2 version: 5.4.2 vite: - specifier: 6.4.1 - version: 6.4.1(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.2) + specifier: 6.4.2 + version: 6.4.2(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.3) deploy/tools/sitemap-generator: dependencies: next-sitemap: specifier: 4.2.3 - version: 4.2.3(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) + version: 4.2.3(next@16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) toolkit/package: dependencies: @@ -612,8 +632,8 @@ importers: specifier: '>=11.14.0' version: 11.14.0(@types/react@18.3.12)(react@19.1.4) next: - specifier: '>=15.2.3' - version: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + specifier: '>=16.2.4' + version: 16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) next-themes: specifier: '>=0.4.4' version: 0.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -641,22 +661,22 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: 4.3.4 - version: 4.3.4(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + version: 4.3.4(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) typescript: specifier: 5.9.2 version: 5.9.2 vite: - specifier: 6.4.1 - version: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + specifier: 6.4.2 + version: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) vite-plugin-dts: specifier: 4.5.4 - version: 4.5.4(@types/node@20.16.7)(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + version: 4.5.4(@types/node@20.16.7)(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) vite-plugin-svgr: specifier: 4.5.0 - version: 4.5.0(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + version: 4.5.0(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + version: 5.1.4(typescript@5.9.2)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) packages: @@ -674,6 +694,9 @@ packages: graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 typescript: ^5.0.0 + '@ably/msgpack-js@0.4.1': + resolution: {integrity: sha512-Sjxj6SOr17hExAVrsycN7u6oV4PhZcK7Z2S8dM71CH/butgO47cSo/TL6FJPCXUyDAzKkOWjMUpJGyZkEpyu4Q==} + '@acemir/cssom@0.9.31': resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} @@ -1360,8 +1383,8 @@ packages: '@blockscout/bens-types@1.4.1': resolution: {integrity: sha512-TlZ1HVdZ2Cswm/CcvNoxS+Ydiht/YGaLo//PJR/UmkmihlEFoY4HfVJvVcUnOQXi+Si7FwJ486DPii889nTJsQ==} - '@blockscout/interchain-indexer-types@0.0.10': - resolution: {integrity: sha512-VxdTHLZ+x+rNQqYZxqm+hSqIwFiOEB4lgWY9gXQKVb2aRrGESHPxM+E9XdpKu3SDAoXYTwL/y4QHR/A6PVV8ng==} + '@blockscout/interchain-indexer-types@1.3.0': + resolution: {integrity: sha512-S+nbsMJ4g10yjwf+BnJ5JL1jlvVHRGsCCpCEx57wQs5sw+odo22iVcZ2LmHNv8Vdh7Tn1+2tJonJQ0nKwadrig==} '@blockscout/multichain-aggregator-types@2.1.2': resolution: {integrity: sha512-Eo5iCIsrfzTbPgfJfZT4TzuIU5FXxB2/thbc+3lkfUCUuSPP/VZPxDEXai773iSra+ctX4LGobFF8osPKZGy5g==} @@ -1384,6 +1407,10 @@ packages: '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + '@boundaries/elements@2.0.1': + resolution: {integrity: sha512-sAWO3D8PFP6pBXdxxW93SQi/KQqqhE2AAHo3AgWfdtJXwO6bfK6/wUN81XnOZk0qRC6vHzUEKhjwVD9dtDWvxg==} + engines: {node: '>=18.18'} + '@chainsafe/as-chacha20poly1305@0.1.0': resolution: {integrity: sha512-BpNcL8/lji/GM3+vZ/bgRWqJ1q5kwvTFmGPk7pxm/QQZDbaMI98waOHjEymTjq2JmdD/INdNBFOVSyJofXg7ew==} @@ -1471,36 +1498,36 @@ packages: '@corex/deepmerge@4.0.43': resolution: {integrity: sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==} - '@cspell/cspell-bundled-dicts@9.6.4': - resolution: {integrity: sha512-OIiPQuB7XQ6rnUv4KaCwHr9vNwbh6VZ4GfgQjcThT0oz0hkL6E5Ar3tq54K9jyqE9ylcHqpRuXUgnKgio6Hlig==} + '@cspell/cspell-bundled-dicts@9.8.0': + resolution: {integrity: sha512-MpXFpVyBPfJQ1YuVotljqUaGf6lWuf+fuWBBgs0PHFYTSjRPWuIxviAaCDnup/CJLLH60xQL4IlcQe4TOjzljw==} engines: {node: '>=20'} - '@cspell/cspell-json-reporter@9.6.4': - resolution: {integrity: sha512-rGYSDnDWACrUyovfN8M/LM8CCFSKjYd2kehbNS7YMPk0Jk+rLk6sgt5WYu3ty45otXCkiO07bjUo/81wBLet7A==} + '@cspell/cspell-json-reporter@9.8.0': + resolution: {integrity: sha512-nqUaSo9T7l8KrE22gc7ZIs+zvP7ak1i7JqGdRs8sGvh2Ijqj43qYQLePgb1b/vm8a1bavnc51m+vf05hpd3g3Q==} engines: {node: '>=20'} - '@cspell/cspell-performance-monitor@9.6.4': - resolution: {integrity: sha512-exuqxV1IVfZkasg57ZjUbaHeZDd6Mdbsbe5FBT3+XaVnRij+wpY2oEW9+kIOL5MOQE3bgQKgu37iMtA1NlCrGA==} + '@cspell/cspell-performance-monitor@9.8.0': + resolution: {integrity: sha512-IsrXYzn23yJICIQ915ACdf+2lNEcFNTu5BIQt3khHOsGVvZ9/AZYpu9Dk825vUyZG7RHg2Oi6dYNiJtULG4ouQ==} engines: {node: '>=20.18'} - '@cspell/cspell-pipe@9.6.4': - resolution: {integrity: sha512-vVxajTG9Ko01oHk8HPsMLajcLrd9AfkOk6vdgFI4FD7ZPq1CY0hfTmfmJ8bzZ4/QkqXglTvePdSgHQVJeltwWw==} + '@cspell/cspell-pipe@9.8.0': + resolution: {integrity: sha512-ISEUD8PHYkd2Ktafc6hFfIXdGKYUvthA09NbwwZsWmOqYyk4wWKHZKqyyxD+BcrFwOyMOJcD8OEvIjkRQp2SJw==} engines: {node: '>=20'} - '@cspell/cspell-resolver@9.6.4': - resolution: {integrity: sha512-3xsgZEqqH9Uj8ZYLBnWbnsHz8wphgaeuWKcNDqgwoMjvwTMQLGoXjHht8Jx5yxd2e080lB7fJax8TaBdCzmFFA==} + '@cspell/cspell-resolver@9.8.0': + resolution: {integrity: sha512-PZJj56BZpKfMxOzWkyt7b+aIXObe+8Ku/zLI4xDXPSuQPENbHBFHfPIZx68CyGEkanKxZ1ewKVx/FT1FUy+wDA==} engines: {node: '>=20'} - '@cspell/cspell-service-bus@9.6.4': - resolution: {integrity: sha512-oGNEzP1gJ43rLklJQjOk5PsfX0mZkLjV19djGptb9xZQeC2qAUxnaAbZtWt5CE8ni2iiTaRmgNRbUqAhRCnjew==} + '@cspell/cspell-service-bus@9.8.0': + resolution: {integrity: sha512-P45sd2nqwcqhulBBbQnZB/JNcobecTrP4Ky3vmEq0cprsvavc+ZoHF9U2Ql5ghMSUzjrF2n1aNzZ8cH4IlsnKg==} engines: {node: '>=20'} - '@cspell/cspell-types@9.6.4': - resolution: {integrity: sha512-lf6d+BdMkJIFCxx2FpajLpqVGGyaGUNFU6jhEM6QUPeGuoA5et2kJXrL0NSY2uWLOVyYYc/FPjzlbe8trA9tBQ==} + '@cspell/cspell-types@9.8.0': + resolution: {integrity: sha512-7Ge4UD6SCA49Tcc3+GTlz3Xn4cqVUAXtDO0u9IeHvJgkN3Me2Rw2GB/CtGmhKST3YeEeZMX7ww09TdHMUJlehw==} engines: {node: '>=20'} - '@cspell/cspell-worker@9.6.4': - resolution: {integrity: sha512-anacKDOZzDfPzuDeFOXGI2tFBYiRRCSnIZP/AOyJ9zTvEQcqq5p/ak18nJ5OQyDr2NG7ovJiCDT5YNiH2Vdg/g==} + '@cspell/cspell-worker@9.8.0': + resolution: {integrity: sha512-W8FLdE3MXPLbWtAXciILQhk9CHd6Mt+HRjZHM8m+dwE1Bc2TAjUai8kIxsdhHUq58p7gYY2ekr5sg1uYOUgTAA==} engines: {node: '>=20.18'} '@cspell/dict-ada@4.1.1': @@ -1542,8 +1569,8 @@ packages: '@cspell/dict-docker@1.1.17': resolution: {integrity: sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==} - '@cspell/dict-dotnet@5.0.12': - resolution: {integrity: sha512-FiV934kNieIjGTkiApu/WKvLYi/KBpvfWB2TSqpDQtmXZlt3uSa5blwblO1ZC8OvjH8RCq/31H5IdEYmTaZS7A==} + '@cspell/dict-dotnet@5.0.13': + resolution: {integrity: sha512-xPp7jMnFpOri7tzmqmm/dXMolXz1t2bhNqxYkOyMqXhvs08oc7BFs+EsbDY0X7hqiISgeFZGNqn0dOCr+ncPYw==} '@cspell/dict-elixir@4.0.8': resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} @@ -1551,14 +1578,14 @@ packages: '@cspell/dict-en-common-misspellings@2.1.12': resolution: {integrity: sha512-14Eu6QGqyksqOd4fYPuRb58lK1Va7FQK9XxFsRKnZU8LhL3N+kj7YKDW+7aIaAN/0WGEqslGP6lGbQzNti8Akw==} - '@cspell/dict-en-gb-mit@3.1.20': - resolution: {integrity: sha512-rcWEKb1mwgK13CSHu6GwwFA/sWLRkB0twTzSfPxUOULJkhcUrL93ixohk416SBa0BVxixub+lOpTXKcCTbTXsA==} + '@cspell/dict-en-gb-mit@3.1.22': + resolution: {integrity: sha512-xE5Vg6gGdMkZ1Ep6z9SJMMioGkkT1GbxS5Mm0U3Ey1/H68P0G7cJcyiVr1CARxFbLqKE4QUpoV1o6jz1Z5Yl9Q==} - '@cspell/dict-en_us@4.4.31': - resolution: {integrity: sha512-MdV6mbrSkyIqn9+6F5poCyHIPqWB3H/wlDL9LXfjgAxNFBdYRE767xJtIYunzUqhUiJHSJgZajEFdTtMDw441Q==} + '@cspell/dict-en_us@4.4.33': + resolution: {integrity: sha512-zWftVqfUStDA37wO1ZNDN1qMJOfcxELa8ucHW8W8wBAZY3TK5Nb6deLogCK/IJi/Qljf30dwwuqqv84Qqle9Tw==} - '@cspell/dict-filetypes@3.0.17': - resolution: {integrity: sha512-6f1gZf9o60fGQXGi/fZaryISUNDRmmJwGyr4QU1UvgUgOdpDHLVJtv/0wSL9q5F0wAkYhbXo/fNG8CcUTaf7Ww==} + '@cspell/dict-filetypes@3.0.18': + resolution: {integrity: sha512-yU7RKD/x1IWmDLzWeiItMwgV+6bUcU/af23uS0+uGiFUbsY1qWV/D4rxlAAO6Z7no3J2z8aZOkYIOvUrJq0Rcw==} '@cspell/dict-flutter@1.1.1': resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} @@ -1569,8 +1596,8 @@ packages: '@cspell/dict-fsharp@1.1.1': resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} - '@cspell/dict-fullstack@3.2.8': - resolution: {integrity: sha512-J6EeoeThvx/DFrcA2rJiCA6vfqwJMbkG0IcXhlsmRZmasIpanmxgt90OEaUazbZahFiuJT8wrhgQ1QgD1MsqBw==} + '@cspell/dict-fullstack@3.2.9': + resolution: {integrity: sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ==} '@cspell/dict-gaming-terms@1.1.2': resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} @@ -1631,8 +1658,8 @@ packages: '@cspell/dict-node@5.0.9': resolution: {integrity: sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==} - '@cspell/dict-npm@5.2.37': - resolution: {integrity: sha512-dYKrD0bI08YgebJcbjsIDpTMK6EH0wTkEyQLsaH0T4tmsLJE95coTYb3eE7giRRGJADvBqrjH4fz5+INd85QqQ==} + '@cspell/dict-npm@5.2.38': + resolution: {integrity: sha512-21ucGRPYYhr91C2cDBoMPTrcIOStQv33xOqJB0JLoC5LAs2Sfj9EoPGhGb+gIFVHz6Ia7JQWE2SJsOVFJD1wmg==} '@cspell/dict-php@4.1.1': resolution: {integrity: sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==} @@ -1643,8 +1670,8 @@ packages: '@cspell/dict-public-licenses@2.0.16': resolution: {integrity: sha512-EQRrPvEOmwhwWezV+W7LjXbIBjiy6y/shrET6Qcpnk3XANTzfvWflf9PnJ5kId/oKWvihFy0za0AV1JHd03pSQ==} - '@cspell/dict-python@4.2.25': - resolution: {integrity: sha512-hDdN0YhKgpbtZVRjQ2c8jk+n0wQdidAKj1Fk8w7KEHb3YlY5uPJ0mAKJk7AJKPNLOlILoUmN+HAVJz+cfSbWYg==} + '@cspell/dict-python@4.2.26': + resolution: {integrity: sha512-hbjN6BjlSgZOG2dA2DtvYNGBM5Aq0i0dHaZjMOI9K/9vRicVvKbcCiBSSrR3b+jwjhQL5ff7HwG5xFaaci0GQA==} '@cspell/dict-r@2.1.1': resolution: {integrity: sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==} @@ -1661,8 +1688,8 @@ packages: '@cspell/dict-shell@1.1.2': resolution: {integrity: sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==} - '@cspell/dict-software-terms@5.2.0': - resolution: {integrity: sha512-jyucc8KKxH5ClC4FPICISgcKAZU7UwgFdPkuuuK5nYIw0+l1umnUSn9IKCcOaurvXujvVP6mBfclXpUtmT6Vrw==} + '@cspell/dict-software-terms@5.2.2': + resolution: {integrity: sha512-0CaYd6TAsKtEoA7tNswm1iptEblTzEe3UG8beG2cpSTHk7afWIVMtJLgXDv0f/Li67Lf3Z1Jf3JeXR7GsJ2TRw==} '@cspell/dict-sql@2.2.1': resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==} @@ -1685,24 +1712,24 @@ packages: '@cspell/dict-zig@1.0.0': resolution: {integrity: sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==} - '@cspell/dynamic-import@9.6.4': - resolution: {integrity: sha512-1VnL9ahT3s17DLWl4MeO1pYg7zcVT3X9cKynI2/U86zNK5xMGS5icvjp7X65tsCAVNcWOtkqVFfrxi7kWxn67g==} + '@cspell/dynamic-import@9.8.0': + resolution: {integrity: sha512-wMgb32lqG9g6lCipUQsY9Bk5idXPDz7wvzOqEsU1M2HmNYmdE1wfPoRpfQfsVL965iG3+6h8QLr2+8FKpweFEQ==} engines: {node: '>=20'} - '@cspell/filetypes@9.6.4': - resolution: {integrity: sha512-a1aZ/8vGnhTknxTukjzo3m8CISyHW2MWnbedywg5SDEl5RMJitmzX90QZiQdSvEcqzqmtoAgSEZNBT2LX2gIKg==} + '@cspell/filetypes@9.8.0': + resolution: {integrity: sha512-yHvtYn9qt6zykua77sNzTcf7HrG/dpo/+2pCMGSrfSrQypSNT6FUFvMS04W7kwhP86U1GkCjppNykXuoH3cqug==} engines: {node: '>=20'} - '@cspell/rpc@9.6.4': - resolution: {integrity: sha512-vGI1788Rx5Yml9N1/pD4zGd8Vrchi2Y01ADf9NiiOaNVVdf4PU1GCssLCsiIzhYQneErpQ8pJi/mS2F/QMZbRA==} + '@cspell/rpc@9.8.0': + resolution: {integrity: sha512-t4lHEa254W+PePXNQ1noW7QhQxz/mhsJ9X8LEt0ILzBbPWCJzN+JuaM7EiolIPiwxtfxpMwKx9482kt4eTja7A==} engines: {node: '>=20.18'} - '@cspell/strong-weak-map@9.6.4': - resolution: {integrity: sha512-AQrUbA0JUOEQgwItnfUQ6Ydk0hWY/uV3VhLwZWyrnT9eiQynmTnRTHtOCkkSl9+M4P0N4Raa2eGFRLcPAFksaw==} + '@cspell/strong-weak-map@9.8.0': + resolution: {integrity: sha512-HocksAqZ0JcWA5oWO7TIlOCftXVGkPGzbeFlCRRrjJpZmYQH+4NdeEXyQC6T89NGocp45td/CgyBcAaFMy1N9w==} engines: {node: '>=20'} - '@cspell/url@9.6.4': - resolution: {integrity: sha512-h6VMlb7bDyGJfwLtipxxtHlT+ojzUXZz14AqZ/NEzY3LfOhfJTGpRcWLYFsgG/L0Ma4qjsYbPJt/Sj1C14j0VA==} + '@cspell/url@9.8.0': + resolution: {integrity: sha512-LY1lFiZLTQF/ma1ilfKmRmFmEOw0RfYhyl0UMhY7/d93b+kiDMhxP/9Qir4+5LyiRncaE3++ZcWno9Hya+ssRg==} engines: {node: '>=20'} '@cspotcode/source-map-support@0.8.1': @@ -1767,17 +1794,17 @@ packages: '@dynamic-labs/wallet-connector-core': ^4.11.1 viem: ^2.21.55 - '@dynamic-labs-sdk/assert-package-version@0.14.0': - resolution: {integrity: sha512-t9rfVlBjCZu2ZlluS80U7/N/Fckv11pBSRZPoFa1gM1vTbCVcob7ThLeNvzlGnTnfqEHaU+Lhz6RLGDvrr0HxA==} + '@dynamic-labs-sdk/assert-package-version@0.19.0': + resolution: {integrity: sha512-kIVwfrY0RxKQGD7QDJPsT7rlDZTH1KXjowZxFbe3UCWedmQQ9Swz+KDxeCryxiDTlvDG5ilrSZ39Ay9Hqg3lcA==} - '@dynamic-labs-sdk/client@0.14.0': - resolution: {integrity: sha512-9HcJ9GMRWXEGZRxmexEkTTaAJBXbSohKBuIer5ETP3Lr/NrhrnCgQPZ3CSclMUMdQ4GlZWo66676+heTWglXKA==} + '@dynamic-labs-sdk/client@0.19.0': + resolution: {integrity: sha512-zQROniFPeLAjkpm0/miBYJ5HL1Yh6TdUqoNrH5GzDhFkTvUY+vhspcZd9NSuHZoktTN2tOQuzJtbicr4jQqFKQ==} '@dynamic-labs-wallet/browser-wallet-client@0.0.286': resolution: {integrity: sha512-jmDW0lOSNYrPg/krdqaigt5F2Ib/y/7eAc2ZV/ELY5Wd982gl2JoG1RZhy6ut0ovZL9J+qeCfQhDLCgHDhz3Ww==} - '@dynamic-labs-wallet/browser-wallet-client@0.0.289': - resolution: {integrity: sha512-3h0XtxS82qeIv+AObMS+KC7wdEyUmgwngGgBcc5wSiYFFm4XgLY5AMBwxHdSiqBzf8ZYFBPq9h4BuVJH1X0l/A==} + '@dynamic-labs-wallet/browser-wallet-client@0.0.314': + resolution: {integrity: sha512-cPon6dW11SuoyWAnJX5BZvLZLucEAHvYBC55a09BEpx5c/iIRzkbqqEsW6EzKIfNKH/0Zbb1j48kXAK1jS/C0w==} '@dynamic-labs-wallet/browser@0.0.167': resolution: {integrity: sha512-HDmUetnJ1iz6kGd5PB1kJzeLI7ZJmwxlJ1QGtUqSQHDdBkhLwaDPlccB2IviC5iPfU5PR/IQ1BYEqpoTWx2sBA==} @@ -1800,10 +1827,10 @@ packages: '@dynamic-labs-wallet/core@0.0.286': resolution: {integrity: sha512-DgMDLWvPmYlJoRfqTVgcQIRJtwv9T7wPa88wMJAg11fd/Wmb7MKJedPQ9BMlHOUdnvFkG9qAxw52SKh3MzGp6w==} - '@dynamic-labs-wallet/core@0.0.289': - resolution: {integrity: sha512-wkSeF2Wxj1NfSwMEfMThY+YA7m2HKOr2+dDnt6fEGti0c4fvTGzuE+88sUpChdEFwtGDAQMsJ4Q74MrLCXjBEw==} + '@dynamic-labs-wallet/core@0.0.314': + resolution: {integrity: sha512-1F8BMB1e1D7D1Vwls5NzW1JGaWdEvbJRH7OjDnh19w1bFw13nFqfJbmWry4kY8BjV663kCTmK22sjY3sJXQ3KA==} peerDependencies: - '@dynamic-labs-wallet/forward-mpc-client': 0.4.0 + '@dynamic-labs-wallet/forward-mpc-client': 0.5.4 '@dynamic-labs-wallet/forward-mpc-client@0.1.3': resolution: {integrity: sha512-riZesfU41fMvetaxJ3bO48/9P8ikRPgoVJgWh8m8i0oRyYN7uUz+Iesp+52U12DCtcvSTXljxrKtrV3yqNAYRw==} @@ -1814,8 +1841,8 @@ packages: '@dynamic-labs-wallet/forward-mpc-client@0.3.0': resolution: {integrity: sha512-bpuaN4iuKKIi/SPF7xs+InYbfVO/DTLZw9gJeU8cCRh0h91j+GNJZOX8n+1T9sRj3IIvHv3Seq6Zsx/uEWAtVA==} - '@dynamic-labs-wallet/forward-mpc-client@0.4.0': - resolution: {integrity: sha512-Ev+ObL3Pb1xv9hfEjnEg+iGQh7uaV4LfkIu7Ri5AvtL0MgIHJAjBmz4N+h58s8bN3l3Nr/NvUPiPwXwV407e+Q==} + '@dynamic-labs-wallet/forward-mpc-client@0.5.4': + resolution: {integrity: sha512-35mWTIyHJKBR29mbErx7dIUHhBU5iRZ0Q4+SChPyU+yefclLLybDVFcI3J+fcx11YdgFeEZXLprhJb7D4asQzw==} '@dynamic-labs-wallet/forward-mpc-shared@0.1.0': resolution: {integrity: sha512-xRpMri4+ZuClonwf04RcnT/BCG8oA36ononD7s0MA5wSqd8kOuHjzNTSoM6lWnPiCmlpECyPARJ1CEO02Sfq9Q==} @@ -1826,50 +1853,56 @@ packages: '@dynamic-labs-wallet/forward-mpc-shared@0.3.0': resolution: {integrity: sha512-S0oO+vbDZZdUFH+LOAIolvgKWgthyvJj6R4wam+v4Hb5FXFxvdBkzCAMq63UNH+wLP4oyctZK6BOjJ3oTzwQLA==} - '@dynamic-labs-wallet/forward-mpc-shared@0.4.0': - resolution: {integrity: sha512-bBpjnybxwCWGiMAAuRFwkas8gonkUf3NAf+LKpGzDjyn6qQ5bY8VrWmVjIgOJSCKlxlGZeBpRuFW1qRtL9Z9Yg==} + '@dynamic-labs-wallet/forward-mpc-shared@0.5.1': + resolution: {integrity: sha512-ekfuJV7Q861ElqeeOCKO1kNGE+32+xM9azka7BxOLr3XeBPQz3H/wjcEX8YWqOUqeAcYMRc4QbBKR7ouTgsFSQ==} '@dynamic-labs/assert-package-version@4.67.2': resolution: {integrity: sha512-8wkpF0IlTP5hCUFkRZXhprCBDqDVbZnoyVp8lFJCXY4RcF8ksexnmrUgVq42Q5i64OiqhhSNrrUqeyS9Gt2zIw==} - '@dynamic-labs/embedded-wallet-evm@4.67.2': - resolution: {integrity: sha512-oYtOsC1aUR6yfBJ0rbl2PdgAzn+mhbUyRKy1dgsROWNlsraogJYR3M9FEnsGZlaIO4aEzBvII6h3OYFOyA5vDA==} + '@dynamic-labs/assert-package-version@4.74.1': + resolution: {integrity: sha512-ql+2c2C+Df3UXmLQKB7Syxgola2GlAg2qDCpP0qg7oWyNBivaMyHON7Ty1M/GRnfhWICKID11bTZXRpzsxZw7g==} + + '@dynamic-labs/embedded-wallet-evm@4.74.1': + resolution: {integrity: sha512-MBisGU35t0HxM/1eZg7jHDUecuECEA3wK0BzHGO4m930j47n2Q0PUTW8NhIEIpTKK99MLAYUWjoZIP194ePAAg==} peerDependencies: - viem: ^2.28.4 + viem: ^2.45.3 - '@dynamic-labs/embedded-wallet@4.67.2': - resolution: {integrity: sha512-JB6v+huZD3m9PHBCplyZec6k4QU9Bkq0lrGjTkxiPO+6Gsw+P6Ys0Xf0YgFHV4iHNSL/Rg8a342RvHMJYKR8WQ==} + '@dynamic-labs/embedded-wallet@4.74.1': + resolution: {integrity: sha512-fxITdB6aJghwv84pDhJhAlv+xHR9z+tQVjBMCcAj58g3hkIbTsw+xKg6HH8Lo0hry6SXv6apn9r7uiedEALUSQ==} - '@dynamic-labs/ethereum-core@4.67.2': - resolution: {integrity: sha512-eD4Qv0UQjeEyqTUEwOGPu//5upoShsxmgvgq+zjYXSH4T5kECqJEZd9I3hyAlXv8CgZwz1aLiJMQLXMyuVm80w==} + '@dynamic-labs/ethereum-core@4.74.1': + resolution: {integrity: sha512-XgVEbSPEEHC+r5PvQwF41jtu8jn+RW/eSEPch7aXsSJFP6aoH/5wzNOUFPHS1AZtseG6njxnaPX3Us3ekJ2l6Q==} peerDependencies: - viem: ^2.28.4 + viem: ^2.45.3 - '@dynamic-labs/ethereum@4.67.2': - resolution: {integrity: sha512-8PQkxHKL7tQJlh3AxtRwmhoEuI6Jl+g92JH26sdXJqSEVDpopyBakZJeeRo7XBXfqJbuFTaw9n7FRaIKegCPRw==} + '@dynamic-labs/ethereum@4.74.1': + resolution: {integrity: sha512-mwjlYEs2tniOdl94MxUDBKdcl6PlWWGALqkjd4B3VqbGUGm0jGeVtM23m9uPeF/YFmvOK+dGLAoR7Q1HsJ36rg==} peerDependencies: - viem: ^2.28.4 + viem: ^2.45.3 - '@dynamic-labs/iconic@4.67.2': - resolution: {integrity: sha512-R7C9ddoIRwczPUNGJs8jb9m6R1GhBS40cW+XZYhuuRu34SVEtPowwSuai2gpKxqF/368ic69t1ugv04bXVwTFg==} + '@dynamic-labs/iconic@4.74.1': + resolution: {integrity: sha512-JRw4prg5pwns8YAL1vyfvuY/FZJHdMghuQ6xBt9DUc8LWYd0ewmIOXhQNhzrBX93ezyagghRTZ/vWfgAHTYkYw==} peerDependencies: react: '>=18.0.0 <20.0.0' react-dom: '>=18.0.0 <20.0.0' - '@dynamic-labs/locale@4.67.2': - resolution: {integrity: sha512-yrFXKdfkngxEjFzPItyfl7D1K2YhBUQB9lTgg28Gpr6/Q80dydA13fFzily5ffV3YYWhLAl6qpf61RbkaszR3g==} + '@dynamic-labs/locale@4.74.1': + resolution: {integrity: sha512-bWNlJQZ1paoSj8uFytln+fPEKY0bO65ZITzB2H/sZRXNMtDdg1eS6jUy+MzTvcvppsVipkYE5mqQ5vlSt5PuBQ==} '@dynamic-labs/logger@4.67.2': resolution: {integrity: sha512-TbE53aAi3y5wuo53pgjzhF68oOTEZhKBBDXkm21NYukFxdfVtgbiqPMOSBRfq6X7DmFlhl1jjhPgwukkYTs8fA==} + '@dynamic-labs/logger@4.74.1': + resolution: {integrity: sha512-Gg85XkrrQ5WZn0gSR5VNFX+beoicD8LRuMcbJdKtgIZi5EV1C0QRZsv2XgGp5d0mW/vBj049kfNpwFxZ0Ze1og==} + '@dynamic-labs/message-transport@4.67.2': resolution: {integrity: sha512-Do+xr7F0gHqsm/awhXuQIloOC31uCuMqf/hfCUSzd1St6HG+B9IyWbMVo4w2KcX2pDk7sd/caeiPNPsx7MQ0OQ==} - '@dynamic-labs/multi-wallet@4.67.2': - resolution: {integrity: sha512-I3bo5h7Ikmo3NIHwnxRm5s6afpsK2YU+qJ6eaegV1cfGn6+ESvE2FqumSVyGdeqWkcy2N/dX8FwCXyhdCTakfQ==} + '@dynamic-labs/multi-wallet@4.74.1': + resolution: {integrity: sha512-UyFQTJPxsvJN/PRj79otHXeicZzqJqgK1aHcmYLSCmxxNuBz0W94de6p9fAs/btG/hOtvq2CNL0WJB0iWQ6tpQ==} - '@dynamic-labs/rpc-providers@4.67.2': - resolution: {integrity: sha512-iiOlIBY6BVvNiCHsByfl11bs0JK2gdfQDuWZ1SqEf2Iy2ZtvQlW2MYaST7sH2HX6+H0egsFjQBqbDJHgWiH/Hg==} + '@dynamic-labs/rpc-providers@4.74.1': + resolution: {integrity: sha512-zW0rkMdId38YLgTsY03kWRqnC0HHeYes1xNLWOCFS/Hs6rYnHS252BRa8/Xg+hxSbPCBDOki3EDTiBJq484C1Q==} '@dynamic-labs/sdk-api-core@0.0.764': resolution: {integrity: sha512-79JptJTTClLc9qhioThtwMuzTHJ+mrj8sTEglb7Mcx3lJub9YbXqNdzS9mLRxZsr2et3aqqpzymXdUBzSEaMng==} @@ -1886,60 +1919,75 @@ packages: '@dynamic-labs/sdk-api-core@0.0.881': resolution: {integrity: sha512-Prc/gJZOTHGlGAw4VCoR8qTxrSRu2Ivl5+dYUsTjWDWTrJYJIzPhNOdXiReDitkXwr5Zu5wrsvRnrDf9/Rn0AQ==} - '@dynamic-labs/sdk-react-core@4.67.2': - resolution: {integrity: sha512-YhWlAnKJHAbAKYAzAYagY8LpR1SquzA/D2CpwkLqkgNRHZ6X3F6MiIsYpFE8gAASruqlGNaKrSDUL/F/BNraLA==} + '@dynamic-labs/sdk-api-core@0.0.900': + resolution: {integrity: sha512-4kb6IY75fFbTLwW24hN8ziVuxEeGAtsQpvXlKXA/XYrPKFFtJU6VQnuiKrKAerekIltdfMXEIqBJpcdPsmbszg==} + + '@dynamic-labs/sdk-api-core@0.0.909': + resolution: {integrity: sha512-xXeYrBxQveJLDxEE1DPUlhoGlEB4CAyd4dMMBbUbXqmN3gC9dM9bAjOzaBqRzzA55fzqu6DGdegj0efbPOGJXA==} + + '@dynamic-labs/sdk-api-core@0.0.914': + resolution: {integrity: sha512-EWCUCtgq9lZwfanfC6X6PAHHm/g0V146s6tk00I88Bj7+nSo7VR++0ckiXOEUZKF13L/p3k5tf5QhC7dCdLn1A==} + + '@dynamic-labs/sdk-react-core@4.74.1': + resolution: {integrity: sha512-kfC6VbvX9b/keuw0ltbv6r8qzb+YWBpSxI8IS2Z2gTHbIpGlEbRwRdUauHdaAUGLlvgijfOHYohyYcM0mQcSZA==} peerDependencies: react: '>=18.0.0 <20.0.0' react-dom: '>=18.0.0 <20.0.0' - '@dynamic-labs/solana-core@4.67.2': - resolution: {integrity: sha512-xLInw2DN/6tynjuN1SZNrTadSs4LJOc2t2cBNj9Wygj6ZgLmB83SycSgScz3ggOAsfWPiwQ1gHProZENwF0spw==} + '@dynamic-labs/solana-core@4.74.1': + resolution: {integrity: sha512-2wo10zI3xBJTKWaRgzuDtkCozG341EmqoPXEagXEBJ5yXsIrLcjc1RStV0mpEz5YZ0nK/CByQDJbs99M/blueA==} - '@dynamic-labs/store@4.67.2': - resolution: {integrity: sha512-KMh782VoUfnFR0sCEwm8W68wCgTNO40pROma105gBkn8py0UOpaU6e0imb2BUydvY2x+TPgiyIUNQAIK1dZQiQ==} + '@dynamic-labs/store@4.74.1': + resolution: {integrity: sha512-/tNBOJkcMAlFfJStS7erfOiQjBkkqGiD+51a/5XQBaffq5xnK4DNECw6AyQG7aFR+o+eCGLF+iZR0Zi2GTy5SQ==} - '@dynamic-labs/sui-core@4.67.2': - resolution: {integrity: sha512-56EojPjzHKdURQiEMDUDmOMbfFEkQZ+jjkA3sA4oHHJg3LKVHiADieNxtPw5fyCgUFTNYlNeaN7dKQELoQMoGA==} + '@dynamic-labs/sui-core@4.74.1': + resolution: {integrity: sha512-gN4GfLzup7w93e+XILvbt4E3bAvVQJdcHTkhzeODi3kUtnnQuIYDYzEc6NZWZTka6T5+SQ08SYI8EmUUg4kmSQ==} '@dynamic-labs/types@4.67.2': resolution: {integrity: sha512-BYM/AaE7IwS4oP0AO1bcywF/35NUw+ajStpG6hiOc7JWBJE9Hvrf0DM98Ly+9mCdWLBRRwR4NtMvfzHYOKUzjg==} + '@dynamic-labs/types@4.74.1': + resolution: {integrity: sha512-yuyaasX6049G28cTeyF/kr1HJo6m3RwSbZD3T9KNTOQZTZp0yBIjAWi6rYvGthtZdax4NeT1I3HLgHPt2bFdRw==} + '@dynamic-labs/utils@4.67.2': resolution: {integrity: sha512-8vpl1dDInwC7yBpp+QQ6uNgzxIKhzfj+ePL2bLQ6gCBTcAczpw2b31WmwatOmlUe7UQJ1rVqVxjugPkv216Drw==} - '@dynamic-labs/waas-evm@4.67.2': - resolution: {integrity: sha512-bh56eUmGGZqPXtbu5OCwJlUwfgjGVMBGnPQNFFr/KNFO9FpWGV4082iBV0QY51XZFJ+drZX0PabRaX0mgDEZgw==} + '@dynamic-labs/utils@4.74.1': + resolution: {integrity: sha512-H2/vDk9m19Wte08W3xSy3CxcYoUbK6ZPTEUIwY+gsklY1lyfPgSX+wGiKFp/3h6DmxgrAA+Pf4NouHBn4NgPlg==} + + '@dynamic-labs/waas-evm@4.74.1': + resolution: {integrity: sha512-9ECKeAIbwKtMFJFu7hx3UFOpaGGcsgb9ngDmdIjK3twPuuhKEwrvWVm6J3K9vfVyeNSBxB6gDIi73ylkii6J7g==} - '@dynamic-labs/waas@4.67.2': - resolution: {integrity: sha512-n0LjQkDtHeZAd9LEiCvvlqg42dN0I3BfvQ05IOScqO8y3rA3fahNVc2HR4t8gdCqz2Ru5zKLVM0YNL5IU1X5ww==} + '@dynamic-labs/waas@4.74.1': + resolution: {integrity: sha512-ibYJVRdIZNcwcate0jnNLxTvTS3fxuJsMR4VtGGl/x8ysPYS26FWL92pS3zwWhhWJH8mpa9YDQVZDrSPZIOgnA==} - '@dynamic-labs/wagmi-connector@4.67.2': - resolution: {integrity: sha512-iXUwWJYW2nFT94DxZZlMGc8tLhS9l1eZxneMJz/+FHL29r4gmudt8WXrRf1cPfqLWtePJFk7NN5rcShFE9SpSQ==} + '@dynamic-labs/wagmi-connector@4.74.1': + resolution: {integrity: sha512-3zdfroURHm3FRD74Q4QPwy/joywCNoifEtaR1b5gMVPCoFtY5hTaj2bGcZU7Sg3SBpMPByyv9rlqJZfpD7Ze8A==} peerDependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/ethereum-core': 4.67.2 - '@dynamic-labs/logger': 4.67.2 - '@dynamic-labs/rpc-providers': 4.67.2 - '@dynamic-labs/sdk-react-core': 4.67.2 - '@dynamic-labs/types': 4.67.2 - '@dynamic-labs/wallet-connector-core': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/ethereum-core': 4.74.1 + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-react-core': 4.74.1 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/wallet-connector-core': 4.74.1 '@wagmi/core': ^2.6.4 eventemitter3: 5.0.1 react: '>=18.0.0 <20.0.0' - viem: ^2.28.4 + viem: ^2.45.3 wagmi: ^2.14.11 - '@dynamic-labs/wallet-book@4.67.2': - resolution: {integrity: sha512-rSiqGUV07kA2Nc0YLrUS+P+w8Kr1wObxkTlxbQWM0DltWZj9O4b3v8bgleCIiixZugDK3eb3bod+T/n+n6QGQQ==} + '@dynamic-labs/wallet-book@4.74.1': + resolution: {integrity: sha512-hXd5kGfNslEPbPVd04fPwJn9r1zgoP+3otaVEGodSApaeg5uz/VSVjj77+5t933j84g8Jv/Q+lgtO7KLorKC9Q==} peerDependencies: react: '>=18.0.0 <20.0.0' react-dom: '>=18.0.0 <20.0.0' - '@dynamic-labs/wallet-connector-core@4.67.2': - resolution: {integrity: sha512-6DrlAENmi4t4oewd1ut1zToEJYFXAUbQwTDdkM4zdUxpSgagBmtbqWY8bFTjwJY/35Dm16qWybvHbL14po/ysA==} + '@dynamic-labs/wallet-connector-core@4.74.1': + resolution: {integrity: sha512-l6qhtvBWIWGl8VieZuwu9zGpNEdgYsv0lgXwxIhp/CTqdHoEbFZ0sU+w2kthrcApFF+LqHod3hzPYuV6dhb1YQ==} - '@dynamic-labs/webauthn@4.67.2': - resolution: {integrity: sha512-NcifT0RNxXhxfDBoEjMLUZ74CUiVcGJc4muj7hXhgnZ0s+wyIG0y5ZDSy25X/NKtolBORtENbbVigcWlUIWqFQ==} + '@dynamic-labs/webauthn@4.74.1': + resolution: {integrity: sha512-JZQwGaCl0ADxVyX06d9UXD/3KLUMQVL7Y6g39D+Cw3ndgjbdUKrIcxoBcV3rFYZvrGEojNFXWDjfn9vYcQilug==} '@ecies/ciphers@0.2.5': resolution: {integrity: sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A==} @@ -3185,66 +3233,66 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/bundle-analyzer@16.1.7': - resolution: {integrity: sha512-RlMe5kKnTBtn7l1svgz5BgMZZue+Bymvbvo7aTlzd2un4EMV94fSOvf26Z3NgzhZYv7xf3K2TV7SGPQ5NYHocA==} + '@next/bundle-analyzer@16.2.4': + resolution: {integrity: sha512-EAA9xTDv0P1IiUcZaASJJPkNDjRvL7OMTha6XN8MBzALYGfn7I52Mn7vRiyjVfNrtUPi2qiacY+eFVXe3lKCXA==} '@next/env@13.5.11': resolution: {integrity: sha512-fbb2C7HChgM7CemdCY+y3N1n8pcTKdqtQLbC7/EQtPdLvlMUT9JX/dBYl8MMZAtYG4uVMyPFHXckb68q/NRwqg==} - '@next/env@16.1.7': - resolution: {integrity: sha512-rJJbIdJB/RQr2F1nylZr/PJzamvNNhfr3brdKP6s/GW850jbtR70QlSfFselvIBbcPUOlQwBakexjFzqLzF6pg==} + '@next/env@16.2.4': + resolution: {integrity: sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw==} - '@next/eslint-plugin-next@16.1.7': - resolution: {integrity: sha512-v/bRGOJlfRCO+NDKt0bZlIIWjhMKU8xbgEQBo+rV9C8S6czZvs96LZ/v24/GvpEnovZlL4QDpku/RzWHVbmPpA==} + '@next/eslint-plugin-next@16.2.4': + resolution: {integrity: sha512-tOX826JJ96gYK/go18sPUgMq9FK1tqxBFfUCEufJb5XIkWFFmpgU7mahJANKGkHs7F41ir3tReJ3Lv5La0RvhA==} - '@next/swc-darwin-arm64@16.1.7': - resolution: {integrity: sha512-b2wWIE8sABdyafc4IM8r5Y/dS6kD80JRtOGrUiKTsACFQfWWgUQ2NwoUX1yjFMXVsAwcQeNpnucF2ZrujsBBPg==} + '@next/swc-darwin-arm64@16.2.4': + resolution: {integrity: sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.1.7': - resolution: {integrity: sha512-zcnVaaZulS1WL0Ss38R5Q6D2gz7MtBu8GZLPfK+73D/hp4GFMrC2sudLky1QibfV7h6RJBJs/gOFvYP0X7UVlQ==} + '@next/swc-darwin-x64@16.2.4': + resolution: {integrity: sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.1.7': - resolution: {integrity: sha512-2ant89Lux/Q3VyC8vNVg7uBaFVP9SwoK2jJOOR0L8TQnX8CAYnh4uctAScy2Hwj2dgjVHqHLORQZJ2wH6VxhSQ==} + '@next/swc-linux-arm64-gnu@16.2.4': + resolution: {integrity: sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.1.7': - resolution: {integrity: sha512-uufcze7LYv0FQg9GnNeZ3/whYfo+1Q3HnQpm16o6Uyi0OVzLlk2ZWoY7j07KADZFY8qwDbsmFnMQP3p3+Ftprw==} + '@next/swc-linux-arm64-musl@16.2.4': + resolution: {integrity: sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.1.7': - resolution: {integrity: sha512-KWVf2gxYvHtvuT+c4MBOGxuse5TD7DsMFYSxVxRBnOzok/xryNeQSjXgxSv9QpIVlaGzEn/pIuI6Koosx8CGWA==} + '@next/swc-linux-x64-gnu@16.2.4': + resolution: {integrity: sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.1.7': - resolution: {integrity: sha512-HguhaGwsGr1YAGs68uRKc4aGWxLET+NevJskOcCAwXbwj0fYX0RgZW2gsOCzr9S11CSQPIkxmoSbuVaBp4Z3dA==} + '@next/swc-linux-x64-musl@16.2.4': + resolution: {integrity: sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.1.7': - resolution: {integrity: sha512-S0n3KrDJokKTeFyM/vGGGR8+pCmXYrjNTk2ZozOL1C/JFdfUIL9O1ATaJOl5r2POe56iRChbsszrjMAdWSv7kQ==} + '@next/swc-win32-arm64-msvc@16.2.4': + resolution: {integrity: sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.1.7': - resolution: {integrity: sha512-mwgtg8CNZGYm06LeEd+bNnOUfwOyNem/rOiP14Lsz+AnUY92Zq/LXwtebtUiaeVkhbroRCQ0c8GlR4UT1U+0yg==} + '@next/swc-win32-x64-msvc@16.2.4': + resolution: {integrity: sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -4733,6 +4781,10 @@ packages: resolution: {integrity: sha512-KV321z5m/0nuAg83W1dPLy85HpHDk7Sdi4fJbwvacWsEhAh+rZUW4ZfGcXmUIvjZg4ss2bcwNlRhJ7GBEUG08w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + '@sindresorhus/is@7.2.0': resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} @@ -5478,6 +5530,10 @@ packages: '@swc/helpers@0.5.19': resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + '@tabler/icons-react@3.40.0': resolution: {integrity: sha512-oO5+6QCnna4a//mYubx4euZfECtzQZFDGsDMIdzZUhbdyBCT+3bRVFBPueGIcemWld4Vb/0UQ39C/cmGfGylAg==} peerDependencies: @@ -5486,10 +5542,14 @@ packages: '@tabler/icons@3.40.0': resolution: {integrity: sha512-V/Q4VgNPKubRTiLdmWjV/zscYcj5IIk+euicUtaVVqF6luSC9rDngYWgST5/yh3Mrg/mYUwRv1YVTk71Jp0twQ==} - '@tanstack/eslint-plugin-query@5.91.2': - resolution: {integrity: sha512-UPeWKl/Acu1IuuHJlsN+eITUHqAaa9/04geHHPedY8siVarSaWprY0SVMKrkpKfk5ehRT7+/MZ5QwWuEtkWrFw==} + '@tanstack/eslint-plugin-query@5.100.5': + resolution: {integrity: sha512-WKt+xyxvMQkUL4sqMQ8l3gzCplNi9HedVQN32WmBJYKITJ9a5r3H5cpICp8y96V8ZL5rZH0EZRgpO6sy8fAgrQ==} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ^5.4.0 || ^6.0.0 + peerDependenciesMeta: + typescript: + optional: true '@tanstack/query-core@5.55.4': resolution: {integrity: sha512-uoRqNnRfzOH4OMIoxj8E2+Us89UIGXfau981qYJWsNMkFS1GXR4UIyzUTVGq4N7SDLHgFPpo6IOazqUV5gkMZA==} @@ -5644,6 +5704,9 @@ packages: '@types/bunyan@1.8.9': resolution: {integrity: sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==} + '@types/cacheable-request@6.0.3': + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -5877,6 +5940,9 @@ packages: '@types/keygrip@1.0.6': resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + '@types/keyv@3.1.4': + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/koa-compose@3.2.9': resolution: {integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==} @@ -5976,6 +6042,9 @@ packages: '@types/react@19.1.7': resolution: {integrity: sha512-BnsPLV43ddr05N71gaGzyZ5hzkCmGwhMvYc8zmvI8Ci1bRkkDSzDDVfAXfN2tk748OwI7ediiPX6PfT9p0QGVg==} + '@types/responselike@1.0.3': + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@types/retry@0.12.2': resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} @@ -6089,6 +6158,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.59.0': + resolution: {integrity: sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@5.62.0': resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6105,6 +6180,10 @@ packages: resolution: {integrity: sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.59.0': + resolution: {integrity: sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.41.0': resolution: {integrity: sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6117,6 +6196,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/tsconfig-utils@8.59.0': + resolution: {integrity: sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@5.62.0': resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6157,6 +6242,10 @@ packages: resolution: {integrity: sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.59.0': + resolution: {integrity: sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@5.62.0': resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6187,6 +6276,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/typescript-estree@8.59.0': + resolution: {integrity: sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@5.62.0': resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6213,6 +6308,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.59.0': + resolution: {integrity: sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/visitor-keys@5.62.0': resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6229,6 +6331,10 @@ packages: resolution: {integrity: sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.59.0': + resolution: {integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@uidotdev/usehooks@2.4.1': resolution: {integrity: sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==} engines: {node: '>=16'} @@ -6971,6 +7077,18 @@ packages: zod: optional: true + ably@2.17.1: + resolution: {integrity: sha512-70yfXHoM7JtJD/8FCtPD1gkWW0f+AJqbJp0PsqDAqiyxFB8cPFY+FuKHgNTYb8eRHKXq8hT1xiDphUcY0+GHnA==} + engines: {node: '>=16'} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -7299,6 +7417,10 @@ packages: resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} engines: {node: '>= 0.6.0'} + base64-js@1.0.2: + resolution: {integrity: sha512-ZXBDPMt/v/8fsIqn+Z5VwrhdR6jVka0bYobHdGia0Nxi7BJ9i/Uvml3AocHIBtIIBhZjBw5MR0aR4ROs/8+SNg==} + engines: {node: '>= 0.4'} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -7320,10 +7442,6 @@ packages: bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} @@ -7352,6 +7470,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bops@1.0.1: + resolution: {integrity: sha512-qCMBuZKP36tELrrgXpAfM+gHzqa0nLsWZ+L37ncsb8txYlnAoxOPpVp+g7fK0sGkMXfA0wl8uQkESqw3v4HNag==} + borsh@0.7.0: resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} @@ -7418,6 +7539,10 @@ packages: resolution: {integrity: sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==} engines: {node: '>=20'} + cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + cacheable-lookup@7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} @@ -7426,6 +7551,10 @@ packages: resolution: {integrity: sha512-rFWadDRKJs3s2eYdXlGggnBZKG7MTblkFBB0YllFds+UYnfogDp2wcR6JN97FhRkHTvq59n2vhNoHNZn29dh/Q==} engines: {node: '>=18'} + cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -7488,9 +7617,9 @@ packages: charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} @@ -7554,6 +7683,9 @@ packages: resolution: {integrity: sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==} engines: {node: '>=12'} + clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -7735,44 +7867,44 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - cspell-config-lib@9.6.4: - resolution: {integrity: sha512-MecJNR9bIlcPBhyZFsXP6Q2n8qQ2IR9N9HiIz0yh0gBNVydp3LR5JITP5Ji8m7hexmZzVeoXms/dVN74XbS95g==} + cspell-config-lib@9.8.0: + resolution: {integrity: sha512-gMJBAgYPvvO+uDFLUcGWaTu6/e+r8mm4GD4rQfWa/yV4F9fj+yOYLIMZqLWRvT1moHZX1FxyVvUbJcmZ1gfebg==} engines: {node: '>=20'} - cspell-dictionary@9.6.4: - resolution: {integrity: sha512-Ik9ZQVqV/fJfMt5X6IkC7yHGVH46/qjcqCNWwrMSwvROLM3SemNxxZoLvh0wi0GXz9WF1lHcxLJVdeKUk6QB8g==} + cspell-dictionary@9.8.0: + resolution: {integrity: sha512-QW4hdkWcrxZA1QNqi26U0S/U3/V+tKCm7JaaesEJW2F6Ao+23AbHVwidyAVtXaEhGkn6PxB+epKrrAa6nE69qA==} engines: {node: '>=20'} - cspell-gitignore@9.6.4: - resolution: {integrity: sha512-a8asE9BsjJgJ506WUGh5VHrTdVEE8hWELjCJB2atPrW6iY5e4aCIugy0gkRC1ZH9/TseadlmMLrFzHUkJUjzsg==} + cspell-gitignore@9.8.0: + resolution: {integrity: sha512-SDUa1DmSfT20+JH7XtyzcEL9KfurneoR/XbmlrtPQZP/LUHXh3yz4x/0vFIkEFXNWdSckY0QdWTz8DaxClCf4Q==} engines: {node: '>=20'} hasBin: true - cspell-glob@9.6.4: - resolution: {integrity: sha512-253VrjbR8QU15h8GtpDQLX5Ti9uNSuNod2T7f8YEElQOb9I/kUXoCj3Cq4P390IC99klqSHIDxHsxd77ex19lA==} + cspell-glob@9.8.0: + resolution: {integrity: sha512-Uvj/iHXs+jpsJyIEnhEoJTWXb1GVyZ9T05L5JFtZfsQNXrh8SRDQPscjxbg4okKr63N7WevfioQum/snHNYvmw==} engines: {node: '>=20'} - cspell-grammar@9.6.4: - resolution: {integrity: sha512-rvZyTB45/XSRWx7eAsrvTTAZvBTREr/2G2JWVMdqrptFyq1XReAKHhw/x1HJkNgWC9LKAK3bVQJpjLsNG37U9A==} + cspell-grammar@9.8.0: + resolution: {integrity: sha512-01XMq2vhPS0Gvxnfed9uvOwH+3cXddHYxW0PwCE+SZdcC6TN8yM6glByuLt1qFustAmQVE5GSr7uAY9o4pZQRg==} engines: {node: '>=20'} hasBin: true - cspell-io@9.6.4: - resolution: {integrity: sha512-bmvJ4yn5QK2FZWTkZA4sx2qJqIi8BrUUUV7W209drSwkYjhJtXqP0RyF6Qx4Xuu2D1s0UilEtO5Jd+E9UJkQ6w==} + cspell-io@9.8.0: + resolution: {integrity: sha512-JINaEWQEzR4f2upwdZOFcft+nBvQgizJfrOLszxG3p+BIzljnGklqE/nUtLFZpBu0oMJvuM/Fd+GsWor0yP7Xw==} engines: {node: '>=20'} - cspell-lib@9.6.4: - resolution: {integrity: sha512-fUodKcIHTwvokuowB25XyFzBxlk73yj1QRw2por3BxDz9fAim1zAIohAPAnGuzj3LowYnTMjHLYE7RFDUSxy5A==} + cspell-lib@9.8.0: + resolution: {integrity: sha512-G2TtPcye5QE5ev3YgWq42UOJLpTZ6naO/47oIm+jmeSYbgnbcOSThnEE7uMycx+TTNOz/vJVFpZmQyt0bWCftw==} engines: {node: '>=20'} - cspell-trie-lib@9.6.4: - resolution: {integrity: sha512-JKwyRtyybbaTrixwI1OgU5Hvva2Z5zHVWl92WBa9U7KijAyiD/Ehp3T3DCYuBwGks7egw7MgWPySkXXnpme6mw==} + cspell-trie-lib@9.8.0: + resolution: {integrity: sha512-GXIyqxya8QLp6SjKsAN9w3apvt1Ww7GKcZvTBaP76OfLoyb1QC6unwmObY2cZs1manCntGwHrgU6vFNuXnTzpw==} engines: {node: '>=20'} peerDependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.8.0 - cspell@9.6.4: - resolution: {integrity: sha512-rZJmgcyBGKX3KcJ3KC9JYVHeKhDEVbmCheSp8eRGMYw6MCG9o7FHqQjGA/u4lEu4A0psr7ACP/5ym/QHyntRbA==} + cspell@9.8.0: + resolution: {integrity: sha512-qL0VErMSn8BDxaPxcV+9uenffgjPS+5Jfz+m4rCsvYjzLwr7AaaJBWWSV2UiAe/4cturae8n8qzxiGnbbazkRw==} engines: {node: '>=20.18'} hasBin: true @@ -8120,6 +8252,10 @@ packages: defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -8155,6 +8291,10 @@ packages: engines: {node: '>=14'} hasBin: true + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + derive-valtio@0.1.0: resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} peerDependencies: @@ -8490,8 +8630,8 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-next@16.1.7: - resolution: {integrity: sha512-FTq1i/QDltzq+zf9aB/cKWAiZ77baG0V7h8dRQh3thVx7I4dwr6ZXQrWKAaTB7x5VwVXlzoUTyMLIVQPLj2gJg==} + eslint-config-next@16.2.4: + resolution: {integrity: sha512-A6ekXYFj/YQxBPMl45g3e+U8zJo+X2+ZQwcz34pPKjpc/3S4roBA2Rd9xWB4FKuSxhofo1/95WjzmUY+wHrOhg==} peerDependencies: eslint: '>=9.0.0' typescript: '>=3.3.1' @@ -8536,6 +8676,12 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-plugin-boundaries@6.0.2: + resolution: {integrity: sha512-wSHgiYeMEbziP91lH0UQ9oslgF2djG1x+LV9z/qO19ggMKZaCB8pKIGePHAY91eLF4EAgpsxQk8MRSFGRPfPzw==} + engines: {node: '>=18.18'} + peerDependencies: + eslint: '>=6.0.0' + eslint-plugin-consistent-default-export-name@0.0.15: resolution: {integrity: sha512-gqW7dnJbWMxI5H6/Pyz6Sl/vBMwOktePMI2iuuKPb4N82uvemUkfaWhsRZCKndSzpIVaCZ9wdspCVO1tm0wXJQ==} engines: {node: '>=0.10.0'} @@ -9057,6 +9203,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + get-stream@9.0.1: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} @@ -9100,9 +9250,9 @@ packages: engines: {node: '>=12'} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} + global-directory@5.0.0: + resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} + engines: {node: '>=20'} globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -9148,6 +9298,10 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + got@14.6.6: resolution: {integrity: sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==} engines: {node: '>=20'} @@ -9202,6 +9356,11 @@ packages: hamt-sharding@3.0.6: resolution: {integrity: sha512-nZeamxfymIWLpVcAN0CRrb7uVq3hCOGj9IcL6NMA6VVCVWqj+h9Jo/SmaWuS92AEDf1thmHsM5D5c70hM3j2Tg==} + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + engines: {node: '>=0.4.7'} + hasBin: true + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -9337,6 +9496,10 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + http2-wrapper@2.2.1: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} @@ -9426,14 +9589,14 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ini@5.0.0: resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} engines: {node: ^18.17.0 || >=20.5.0} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + interface-blockstore@5.3.2: resolution: {integrity: sha512-oA9Pjkxun/JHAsZrYEyKX+EoPjLciTzidE7wipLc/3YoHDjzsnXRJzAzFJXNUvogtY4g7hIwxArx8+WKJs2RIg==} @@ -9515,10 +9678,6 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -10134,6 +10293,10 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + lowercase-keys@3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10326,6 +10489,10 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -10408,8 +10575,8 @@ packages: engines: {node: '>=14'} hasBin: true - monaco-editor@0.34.1: - resolution: {integrity: sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==} + monaco-editor@0.52.2: + resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} mortice@3.3.1: resolution: {integrity: sha512-t3oESfijIPGsmsdLEKjF+grHfrbnKSXflJtgb1wY14cjxZpS6GnhHRXTxxzCAoCCnq1YYfpEPwY3gjiCPhOufQ==} @@ -10505,8 +10672,8 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@16.1.7: - resolution: {integrity: sha512-WM0L7WrSvKwoLegLYr6V+mz+RIofqQgVAfHhMp9a88ms0cFX8iX9ew+snpWlSBwpkURJOUdvCEt3uLl3NNzvWg==} + next@16.2.4: + resolution: {integrity: sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -10526,8 +10693,9 @@ packages: sass: optional: true - nextjs-routes@1.0.9: - resolution: {integrity: sha512-vITLm5SzpoYGGgDUmWXjf2GU5oHyc4Oh/b7VdDOTqKudEOWuSk1pluZFIlZ9ogVqVxI28c+U+EQ6KyjZOuZR5g==} + nextjs-routes@2.2.5: + resolution: {integrity: sha512-F8IwFj6JRm00lz0iIo7U7B1aOEClI8flcJVZFQIvwwxIL0eQtaroTi1IAKhxJVvETNfT5pYxVnF3pF0pn16npQ==} + hasBin: true peerDependencies: next: '*' @@ -10609,6 +10777,10 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + normalize-url@8.1.1: resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==} engines: {node: '>=14.16'} @@ -10724,6 +10896,14 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + ox@0.14.7: + resolution: {integrity: sha512-zSQ/cfBdolj7U4++NAvH7sI+VG0T3pEohITCgcQj8KlawvTDY4vGVhDT64Atsm0d6adWfIYHDpu88iUBMMp+AQ==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + ox@0.6.7: resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} peerDependencies: @@ -10764,6 +10944,10 @@ packages: typescript: optional: true + p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + p-cancelable@4.0.1: resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==} engines: {node: '>=14.16'} @@ -10873,9 +11057,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@8.1.0: - resolution: {integrity: sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==} - engines: {node: '>=16'} + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -10916,6 +11099,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pify@3.0.0: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} @@ -11160,8 +11347,8 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - protobufjs@7.5.4: - resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + protobufjs@7.5.5: + resolution: {integrity: sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==} engines: {node: '>=12.0.0'} protons-runtime@5.6.0: @@ -11533,9 +11720,9 @@ packages: resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} readdirp@5.0.0: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} @@ -11674,6 +11861,9 @@ packages: engines: {node: '>= 0.4'} hasBin: true + responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + responselike@4.0.2: resolution: {integrity: sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==} engines: {node: '>=20'} @@ -11945,8 +12135,8 @@ packages: resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} engines: {node: '>=20'} - smol-toml@1.6.0: - resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} snake-case@3.0.4: @@ -12367,6 +12557,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-utf8@0.0.1: + resolution: {integrity: sha512-zks18/TWT1iHO3v0vFp5qLKOG27m67ycq/Y7a7cTiRuUNlc4gf3HGnkRgMv0NyhnfTamtkYBJl+YeD1/j07gBQ==} + toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} @@ -12576,6 +12769,11 @@ packages: ufo@1.6.3: resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + uint8-varint@2.0.4: resolution: {integrity: sha512-FwpTa7ZGA/f/EssWAb5/YV6pHgVF1fViKdW8cWaEarjB8t7NyofSWBdOTyFPaGuUG4gx3v1O3PQ8etsiOs3lcw==} @@ -12595,6 +12793,10 @@ packages: uint8arrays@5.1.0: resolution: {integrity: sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==} + ulid@2.4.0: + resolution: {integrity: sha512-fIRiVTJNcSRmXKPZtGzFQv9WRrZ3M9eoptl/teFJvjOzmpU+/K/JH6HZ8deBfb5vMEpicJcLn7JmvdknlMq7Zg==} + hasBin: true + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -12902,6 +13104,14 @@ packages: typescript: optional: true + viem@2.47.10: + resolution: {integrity: sha512-D+l6SDDZWB5bh8u9hgICzMX2/egMrgEQ+Pef/QkZgmOl6bOTyCQMSgWAH8jZTWJ/218J9QNv7s/9BH6Wu5oPDg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + visit-values@2.0.0: resolution: {integrity: sha512-vLFU70y3D915d611GnHYeHkEmq6ZZETzTH4P1hM6I9E3lBwH2VeBBEESe/bGCY+gAyK0qqLFn5bNFpui/GKmww==} @@ -12932,8 +13142,8 @@ packages: vite: optional: true - vite@6.4.1: - resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + vite@6.4.2: + resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -13171,6 +13381,9 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wordwrapjs@5.1.1: resolution: {integrity: sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==} engines: {node: '>=12.17'} @@ -13273,6 +13486,11 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -13391,6 +13609,10 @@ snapshots: graphql: 16.13.1 typescript: 5.9.2 + '@ably/msgpack-js@0.4.1': + dependencies: + bops: 1.0.1 + '@acemir/cssom@0.9.31': {} '@achingbrain/http-parser-js@0.5.9': @@ -14370,7 +14592,7 @@ snapshots: '@blockscout/bens-types@1.4.1': {} - '@blockscout/interchain-indexer-types@0.0.10': {} + '@blockscout/interchain-indexer-types@1.3.0': {} '@blockscout/multichain-aggregator-types@2.1.2': {} @@ -14386,6 +14608,20 @@ snapshots: '@borewit/text-codec@0.2.2': {} + '@boundaries/elements@2.0.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)': + dependencies: + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + handlebars: 4.7.9 + is-core-module: 2.16.1 + micromatch: 4.0.8 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + '@chainsafe/as-chacha20poly1305@0.1.0': {} '@chainsafe/as-sha256@1.2.0': {} @@ -14638,7 +14874,7 @@ snapshots: '@corex/deepmerge@4.0.43': {} - '@cspell/cspell-bundled-dicts@9.6.4': + '@cspell/cspell-bundled-dicts@9.8.0': dependencies: '@cspell/dict-ada': 4.1.1 '@cspell/dict-al': 1.1.1 @@ -14653,16 +14889,16 @@ snapshots: '@cspell/dict-data-science': 2.0.13 '@cspell/dict-django': 4.1.6 '@cspell/dict-docker': 1.1.17 - '@cspell/dict-dotnet': 5.0.12 + '@cspell/dict-dotnet': 5.0.13 '@cspell/dict-elixir': 4.0.8 '@cspell/dict-en-common-misspellings': 2.1.12 - '@cspell/dict-en-gb-mit': 3.1.20 - '@cspell/dict-en_us': 4.4.31 - '@cspell/dict-filetypes': 3.0.17 + '@cspell/dict-en-gb-mit': 3.1.22 + '@cspell/dict-en_us': 4.4.33 + '@cspell/dict-filetypes': 3.0.18 '@cspell/dict-flutter': 1.1.1 '@cspell/dict-fonts': 4.0.6 '@cspell/dict-fsharp': 1.1.1 - '@cspell/dict-fullstack': 3.2.8 + '@cspell/dict-fullstack': 3.2.9 '@cspell/dict-gaming-terms': 1.1.2 '@cspell/dict-git': 3.1.0 '@cspell/dict-golang': 6.0.26 @@ -14681,17 +14917,17 @@ snapshots: '@cspell/dict-markdown': 2.0.16(@cspell/dict-css@4.1.1)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.15)(@cspell/dict-typescript@3.2.3) '@cspell/dict-monkeyc': 1.0.12 '@cspell/dict-node': 5.0.9 - '@cspell/dict-npm': 5.2.37 + '@cspell/dict-npm': 5.2.38 '@cspell/dict-php': 4.1.1 '@cspell/dict-powershell': 5.0.15 '@cspell/dict-public-licenses': 2.0.16 - '@cspell/dict-python': 4.2.25 + '@cspell/dict-python': 4.2.26 '@cspell/dict-r': 2.1.1 '@cspell/dict-ruby': 5.1.1 '@cspell/dict-rust': 4.1.2 '@cspell/dict-scala': 5.0.9 '@cspell/dict-shell': 1.1.2 - '@cspell/dict-software-terms': 5.2.0 + '@cspell/dict-software-terms': 5.2.2 '@cspell/dict-sql': 2.2.1 '@cspell/dict-svelte': 1.0.7 '@cspell/dict-swift': 2.0.6 @@ -14700,25 +14936,25 @@ snapshots: '@cspell/dict-vue': 3.0.5 '@cspell/dict-zig': 1.0.0 - '@cspell/cspell-json-reporter@9.6.4': + '@cspell/cspell-json-reporter@9.8.0': dependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.8.0 - '@cspell/cspell-performance-monitor@9.6.4': {} + '@cspell/cspell-performance-monitor@9.8.0': {} - '@cspell/cspell-pipe@9.6.4': {} + '@cspell/cspell-pipe@9.8.0': {} - '@cspell/cspell-resolver@9.6.4': + '@cspell/cspell-resolver@9.8.0': dependencies: - global-directory: 4.0.1 + global-directory: 5.0.0 - '@cspell/cspell-service-bus@9.6.4': {} + '@cspell/cspell-service-bus@9.8.0': {} - '@cspell/cspell-types@9.6.4': {} + '@cspell/cspell-types@9.8.0': {} - '@cspell/cspell-worker@9.6.4': + '@cspell/cspell-worker@9.8.0': dependencies: - cspell-lib: 9.6.4 + cspell-lib: 9.8.0 '@cspell/dict-ada@4.1.1': {} @@ -14748,17 +14984,17 @@ snapshots: '@cspell/dict-docker@1.1.17': {} - '@cspell/dict-dotnet@5.0.12': {} + '@cspell/dict-dotnet@5.0.13': {} '@cspell/dict-elixir@4.0.8': {} '@cspell/dict-en-common-misspellings@2.1.12': {} - '@cspell/dict-en-gb-mit@3.1.20': {} + '@cspell/dict-en-gb-mit@3.1.22': {} - '@cspell/dict-en_us@4.4.31': {} + '@cspell/dict-en_us@4.4.33': {} - '@cspell/dict-filetypes@3.0.17': {} + '@cspell/dict-filetypes@3.0.18': {} '@cspell/dict-flutter@1.1.1': {} @@ -14766,7 +15002,7 @@ snapshots: '@cspell/dict-fsharp@1.1.1': {} - '@cspell/dict-fullstack@3.2.8': {} + '@cspell/dict-fullstack@3.2.9': {} '@cspell/dict-gaming-terms@1.1.2': {} @@ -14809,7 +15045,7 @@ snapshots: '@cspell/dict-node@5.0.9': {} - '@cspell/dict-npm@5.2.37': {} + '@cspell/dict-npm@5.2.38': {} '@cspell/dict-php@4.1.1': {} @@ -14817,7 +15053,7 @@ snapshots: '@cspell/dict-public-licenses@2.0.16': {} - '@cspell/dict-python@4.2.25': + '@cspell/dict-python@4.2.26': dependencies: '@cspell/dict-data-science': 2.0.13 @@ -14831,7 +15067,7 @@ snapshots: '@cspell/dict-shell@1.1.2': {} - '@cspell/dict-software-terms@5.2.0': {} + '@cspell/dict-software-terms@5.2.2': {} '@cspell/dict-sql@2.2.1': {} @@ -14847,18 +15083,18 @@ snapshots: '@cspell/dict-zig@1.0.0': {} - '@cspell/dynamic-import@9.6.4': + '@cspell/dynamic-import@9.8.0': dependencies: - '@cspell/url': 9.6.4 + '@cspell/url': 9.8.0 import-meta-resolve: 4.2.0 - '@cspell/filetypes@9.6.4': {} + '@cspell/filetypes@9.8.0': {} - '@cspell/rpc@9.6.4': {} + '@cspell/rpc@9.8.0': {} - '@cspell/strong-weak-map@9.6.4': {} + '@cspell/strong-weak-map@9.8.0': {} - '@cspell/url@9.6.4': {} + '@cspell/url@9.8.0': {} '@cspotcode/source-map-support@0.8.1': dependencies: @@ -14902,11 +15138,19 @@ snapshots: '@leichtgewicht/ip-codec': 2.0.5 utf8-codec: 1.0.0 +<<<<<<< HEAD '@dynamic-labs-connectors/base-account-evm@4.4.2(@dynamic-labs/ethereum-core@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)))(@dynamic-labs/wallet-connector-core@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6)': dependencies: '@base-org/account': 1.1.1(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.3.6) '@dynamic-labs/ethereum-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) +======= + '@dynamic-labs-connectors/base-account-evm@4.4.2(@dynamic-labs/ethereum-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)))(@dynamic-labs/wallet-connector-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6)': + dependencies: + '@base-org/account': 1.1.1(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.3.6) + '@dynamic-labs/ethereum-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) +>>>>>>> v2.8.0-alpha.1 viem: 2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) transitivePeerDependencies: - '@types/react' @@ -14918,27 +15162,30 @@ snapshots: - utf-8-validate - zod - '@dynamic-labs-sdk/assert-package-version@0.14.0': {} + '@dynamic-labs-sdk/assert-package-version@0.19.0': {} - '@dynamic-labs-sdk/client@0.14.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': + '@dynamic-labs-sdk/client@0.19.0(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs-sdk/assert-package-version': 0.14.0 + '@dynamic-labs-sdk/assert-package-version': 0.19.0 '@dynamic-labs-wallet/browser-wallet-client': 0.0.286(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) - '@dynamic-labs/sdk-api-core': 0.0.881 + '@dynamic-labs/sdk-api-core': 0.0.914 '@simplewebauthn/browser': 13.1.0 + ably: 2.17.1(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(utf-8-validate@6.0.6) buffer: 6.0.3 eventemitter3: 5.0.1 zod: 4.0.5 transitivePeerDependencies: - bufferutil - debug + - react + - react-dom - typescript - utf-8-validate '@dynamic-labs-wallet/browser-wallet-client@0.0.286(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/core': 0.0.286(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/message-transport': 4.67.2 uuid: 11.1.0 transitivePeerDependencies: @@ -14947,10 +15194,10 @@ snapshots: - typescript - utf-8-validate - '@dynamic-labs-wallet/browser-wallet-client@0.0.289(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))': + '@dynamic-labs-wallet/browser-wallet-client@0.0.314(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))': dependencies: - '@dynamic-labs-wallet/core': 0.0.289(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs-wallet/core': 0.0.314(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/message-transport': 4.67.2 uuid: 11.1.0 transitivePeerDependencies: @@ -14960,7 +15207,7 @@ snapshots: '@dynamic-labs-wallet/browser@0.0.167': dependencies: '@dynamic-labs-wallet/core': 0.0.167 - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/sdk-api-core': 0.0.764 '@noble/hashes': 1.8.0 argon2id: 1.0.1 @@ -14974,7 +15221,7 @@ snapshots: '@dynamic-labs-wallet/browser@0.0.203(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/core': 0.0.203(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/sdk-api-core': 0.0.818 '@noble/hashes': 1.8.0 argon2id: 1.0.1 @@ -14990,7 +15237,7 @@ snapshots: '@dynamic-labs-wallet/browser@0.0.259(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/core': 0.0.259(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/sdk-api-core': 0.0.864 '@noble/hashes': 1.8.0 argon2id: 1.0.1 @@ -15017,7 +15264,7 @@ snapshots: '@dynamic-labs-wallet/core@0.0.203(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/forward-mpc-client': 0.1.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/sdk-api-core': 0.0.818 axios: 1.15.0(debug@4.4.3) http-errors: 2.0.0 @@ -15030,7 +15277,7 @@ snapshots: '@dynamic-labs-wallet/core@0.0.259(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/forward-mpc-client': 0.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/sdk-api-core': 0.0.864 axios: 1.15.0(debug@4.4.3) http-errors: 2.0.0 @@ -15043,7 +15290,7 @@ snapshots: '@dynamic-labs-wallet/core@0.0.286(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/forward-mpc-client': 0.3.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/logger': 4.74.1 '@dynamic-labs/sdk-api-core': 0.0.875 axios: 1.15.0(debug@4.4.3) uuid: 11.1.0 @@ -15053,12 +15300,18 @@ snapshots: - typescript - utf-8-validate - '@dynamic-labs-wallet/core@0.0.289(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))': + '@dynamic-labs-wallet/core@0.0.314(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))': dependencies: +<<<<<<< HEAD '@dynamic-labs-wallet/forward-mpc-client': 0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) '@dynamic-labs/logger': 4.67.2 '@dynamic-labs/sdk-api-core': 0.0.875 axios: 1.15.0(debug@4.4.3) +======= + '@dynamic-labs-wallet/forward-mpc-client': 0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/sdk-api-core': 0.0.900 + axios: 1.12.0(debug@4.4.3) +>>>>>>> v2.8.0-alpha.1 uuid: 11.1.0 transitivePeerDependencies: - debug @@ -15109,10 +15362,10 @@ snapshots: - typescript - utf-8-validate - '@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': + '@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/core': 0.0.259(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@dynamic-labs-wallet/forward-mpc-shared': 0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs-wallet/forward-mpc-shared': 0.5.1(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) '@evervault/wasm-attestation-bindings': 0.3.1 '@noble/hashes': 1.8.0 '@noble/post-quantum': 0.5.4 @@ -15167,7 +15420,7 @@ snapshots: - typescript - utf-8-validate - '@dynamic-labs-wallet/forward-mpc-shared@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': + '@dynamic-labs-wallet/forward-mpc-shared@0.5.1(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-wallet/browser': 0.0.259(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) '@dynamic-labs-wallet/core': 0.0.259(bufferutil@4.1.0)(utf-8-validate@6.0.6) @@ -15186,6 +15439,7 @@ snapshots: dependencies: '@dynamic-labs/logger': 4.67.2 +<<<<<<< HEAD '@dynamic-labs/embedded-wallet-evm@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6)': dependencies: '@dynamic-labs/assert-package-version': 4.67.2 @@ -15197,6 +15451,23 @@ snapshots: '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@dynamic-labs/webauthn': 4.67.2 +======= + '@dynamic-labs/assert-package-version@4.74.1': + dependencies: + '@dynamic-labs/logger': 4.74.1 + + '@dynamic-labs/embedded-wallet-evm@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6)': + dependencies: + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/embedded-wallet': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/ethereum-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/webauthn': 4.74.1 +>>>>>>> v2.8.0-alpha.1 '@turnkey/api-key-stamper': 0.4.7 '@turnkey/iframe-stamper': 2.5.0 '@turnkey/viem': 0.13.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) @@ -15213,26 +15484,30 @@ snapshots: - utf-8-validate - zod - '@dynamic-labs/embedded-wallet@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@dynamic-labs/embedded-wallet@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/logger': 4.67.2 - '@dynamic-labs/sdk-api-core': 0.0.881 - '@dynamic-labs/utils': 4.67.2 - '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/webauthn': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/webauthn': 4.74.1 '@turnkey/api-key-stamper': 0.4.7 '@turnkey/http': 3.10.0 '@turnkey/iframe-stamper': 2.5.0 '@turnkey/webauthn-stamper': 0.5.1 transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' + - bufferutil - debug - encoding - react - react-dom + - typescript + - utf-8-validate +<<<<<<< HEAD '@dynamic-labs/ethereum-core@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))': dependencies: '@dynamic-labs/assert-package-version': 4.67.2 @@ -15243,13 +15518,29 @@ snapshots: '@dynamic-labs/utils': 4.67.2 '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) +======= + '@dynamic-labs/ethereum-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))': + dependencies: + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) +>>>>>>> v2.8.0-alpha.1 viem: 2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' + - bufferutil - debug - react - react-dom + - typescript + - utf-8-validate +<<<<<<< HEAD '@dynamic-labs/ethereum@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6)': dependencies: '@coinbase/wallet-sdk': 4.3.7(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) @@ -15264,6 +15555,42 @@ snapshots: '@dynamic-labs/waas-evm': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) +======= + '@dynamic-labs/ethereum-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))': + dependencies: + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + viem: 2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) + transitivePeerDependencies: + - '@dynamic-labs-wallet/forward-mpc-client' + - bufferutil + - debug + - react + - react-dom + - typescript + - utf-8-validate + + '@dynamic-labs/ethereum@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6)': + dependencies: + '@coinbase/wallet-sdk': 4.3.7(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) + '@dynamic-labs-connectors/base-account-evm': 4.4.2(@dynamic-labs/ethereum-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)))(@dynamic-labs/wallet-connector-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/embedded-wallet-evm': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) + '@dynamic-labs/ethereum-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/waas-evm': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) +>>>>>>> v2.8.0-alpha.1 '@metamask/sdk': 0.33.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/ethereum-provider': 2.21.5(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) buffer: 6.0.3 @@ -15306,18 +15633,18 @@ snapshots: - utf-8-validate - zod - '@dynamic-labs/iconic@4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@dynamic-labs/iconic@4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 react: 19.1.4 react-dom: 19.1.4(react@19.1.4) sharp: 0.33.5 url: 0.11.0 - '@dynamic-labs/locale@4.67.2(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)': + '@dynamic-labs/locale@4.74.1(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 i18next: 23.4.6 react-i18next: 13.5.0(i18next@23.4.6)(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4) transitivePeerDependencies: @@ -15329,6 +15656,10 @@ snapshots: dependencies: eventemitter3: 5.0.1 + '@dynamic-labs/logger@4.74.1': + dependencies: + eventemitter3: 5.0.1 + '@dynamic-labs/message-transport@4.67.2': dependencies: '@dynamic-labs/assert-package-version': 4.67.2 @@ -15337,26 +15668,29 @@ snapshots: '@vue/reactivity': 3.5.30 eventemitter3: 5.0.1 - '@dynamic-labs/multi-wallet@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@dynamic-labs/multi-wallet@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/rpc-providers': 4.67.2 - '@dynamic-labs/sdk-api-core': 0.0.881 - '@dynamic-labs/types': 4.67.2 - '@dynamic-labs/utils': 4.67.2 - '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) tslib: 2.4.1 transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' + - bufferutil - debug - react - react-dom + - typescript + - utf-8-validate - '@dynamic-labs/rpc-providers@4.67.2': + '@dynamic-labs/rpc-providers@4.74.1': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/types': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/types': 4.74.1 '@dynamic-labs/sdk-api-core@0.0.764': {} @@ -15368,22 +15702,28 @@ snapshots: '@dynamic-labs/sdk-api-core@0.0.881': {} - '@dynamic-labs/sdk-react-core@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': + '@dynamic-labs/sdk-api-core@0.0.900': {} + + '@dynamic-labs/sdk-api-core@0.0.909': {} + + '@dynamic-labs/sdk-api-core@0.0.914': {} + + '@dynamic-labs/sdk-react-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs-sdk/client': 0.14.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) - '@dynamic-labs-wallet/browser-wallet-client': 0.0.289(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/iconic': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/locale': 4.67.2(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4) - '@dynamic-labs/logger': 4.67.2 - '@dynamic-labs/multi-wallet': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/rpc-providers': 4.67.2 - '@dynamic-labs/sdk-api-core': 0.0.881 - '@dynamic-labs/store': 4.67.2 - '@dynamic-labs/types': 4.67.2 - '@dynamic-labs/utils': 4.67.2 - '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs-sdk/client': 0.19.0(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs-wallet/browser-wallet-client': 0.0.314(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/iconic': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/locale': 4.74.1(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4) + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/multi-wallet': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/store': 4.74.1 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) '@hcaptcha/react-hcaptcha': 1.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@thumbmarkjs/thumbmarkjs': 0.16.0 bs58: 5.0.0 @@ -15407,15 +15747,15 @@ snapshots: - typescript - utf-8-validate - '@dynamic-labs/solana-core@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': + '@dynamic-labs/solana-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/rpc-providers': 4.67.2 - '@dynamic-labs/sdk-api-core': 0.0.881 - '@dynamic-labs/types': 4.67.2 - '@dynamic-labs/utils': 4.67.2 - '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) '@solana/spl-token': 0.4.14(@solana/web3.js@1.98.1(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@6.0.6) '@solana/web3.js': 1.98.1(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6) eventemitter3: 5.0.1 @@ -15430,21 +15770,21 @@ snapshots: - typescript - utf-8-validate - '@dynamic-labs/store@4.67.2': + '@dynamic-labs/store@4.74.1': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 - '@dynamic-labs/sui-core@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)': + '@dynamic-labs/sui-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/logger': 4.67.2 - '@dynamic-labs/rpc-providers': 4.67.2 - '@dynamic-labs/sdk-api-core': 0.0.881 - '@dynamic-labs/types': 4.67.2 - '@dynamic-labs/utils': 4.67.2 - '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) '@mysten/sui': 1.45.2(typescript@5.9.2) '@mysten/wallet-standard': 0.19.9(typescript@5.9.2) text-encoding: 0.7.0 @@ -15452,16 +15792,23 @@ snapshots: - '@dynamic-labs-wallet/forward-mpc-client' - '@gql.tada/svelte-support' - '@gql.tada/vue-support' + - bufferutil - debug - react - react-dom - typescript + - utf-8-validate '@dynamic-labs/types@4.67.2': dependencies: '@dynamic-labs/assert-package-version': 4.67.2 '@dynamic-labs/sdk-api-core': 0.0.881 + '@dynamic-labs/types@4.74.1': + dependencies: + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/utils@4.67.2': dependencies: '@dynamic-labs/assert-package-version': 4.67.2 @@ -15472,6 +15819,7 @@ snapshots: eventemitter3: 5.0.1 tldts: 6.0.16 +<<<<<<< HEAD '@dynamic-labs/waas-evm@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@dynamic-labs/assert-package-version': 4.67.2 @@ -15483,6 +15831,29 @@ snapshots: '@dynamic-labs/waas': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) viem: 2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) +======= + '@dynamic-labs/utils@4.74.1': + dependencies: + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + buffer: 6.0.3 + eventemitter3: 5.0.1 + tldts: 6.0.16 + + '@dynamic-labs/waas-evm@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)': + dependencies: + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/ethereum-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/waas': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + viem: 2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) +>>>>>>> v2.8.0-alpha.1 transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' - '@gql.tada/svelte-support' @@ -15497,6 +15868,7 @@ snapshots: - utf-8-validate - zod +<<<<<<< HEAD '@dynamic-labs/waas@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))': dependencies: '@dynamic-labs-wallet/browser-wallet-client': 0.0.289(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) @@ -15509,6 +15881,21 @@ snapshots: '@dynamic-labs/utils': 4.67.2 '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) +======= + '@dynamic-labs/waas@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))': + dependencies: + '@dynamic-labs-sdk/client': 0.19.0(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs-wallet/browser-wallet-client': 0.0.314(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/ethereum-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/solana-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/sui-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) +>>>>>>> v2.8.0-alpha.1 transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' - '@gql.tada/svelte-support' @@ -15523,6 +15910,7 @@ snapshots: - utf-8-validate - viem +<<<<<<< HEAD '@dynamic-labs/wagmi-connector@4.67.2(50f5641ee895bbb4bced80afc9d83bd9)': dependencies: '@dynamic-labs/assert-package-version': 4.67.2 @@ -15532,45 +15920,60 @@ snapshots: '@dynamic-labs/sdk-react-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) '@dynamic-labs/types': 4.67.2 '@dynamic-labs/wallet-connector-core': 4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) +======= + '@dynamic-labs/wagmi-connector@4.74.1(f2fb078b83c33aaece8b74f7b77c1992)': + dependencies: + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/ethereum-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-react-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/wallet-connector-core': 4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) +>>>>>>> v2.8.0-alpha.1 '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(react@19.1.4)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6)) eventemitter3: 5.0.1 react: 19.1.4 viem: 2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6) wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)(viem@2.41.2(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) - '@dynamic-labs/wallet-book@4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@dynamic-labs/wallet-book@4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/iconic': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/logger': 4.67.2 - '@dynamic-labs/utils': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/iconic': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/utils': 4.74.1 eventemitter3: 5.0.1 react: 19.1.4 react-dom: 19.1.4(react@19.1.4) util: 0.12.5 zod: 4.0.5 - '@dynamic-labs/wallet-connector-core@4.67.2(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': - dependencies: - '@dynamic-labs-wallet/browser-wallet-client': 0.0.289(@dynamic-labs-wallet/forward-mpc-client@0.4.0(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/logger': 4.67.2 - '@dynamic-labs/rpc-providers': 4.67.2 - '@dynamic-labs/sdk-api-core': 0.0.881 - '@dynamic-labs/types': 4.67.2 - '@dynamic-labs/utils': 4.67.2 - '@dynamic-labs/wallet-book': 4.67.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@dynamic-labs/wallet-connector-core@4.74.1(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6)': + dependencies: + '@dynamic-labs-sdk/client': 0.19.0(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.2)(utf-8-validate@6.0.6) + '@dynamic-labs-wallet/browser-wallet-client': 0.0.314(@dynamic-labs-wallet/forward-mpc-client@0.5.4(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)) + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 + '@dynamic-labs/rpc-providers': 4.74.1 + '@dynamic-labs/sdk-api-core': 0.0.909 + '@dynamic-labs/types': 4.74.1 + '@dynamic-labs/utils': 4.74.1 + '@dynamic-labs/wallet-book': 4.74.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) eventemitter3: 5.0.1 transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' + - bufferutil - debug - react - react-dom + - typescript + - utf-8-validate - '@dynamic-labs/webauthn@4.67.2': + '@dynamic-labs/webauthn@4.74.1': dependencies: - '@dynamic-labs/assert-package-version': 4.67.2 - '@dynamic-labs/logger': 4.67.2 + '@dynamic-labs/assert-package-version': 4.74.1 + '@dynamic-labs/logger': 4.74.1 '@simplewebauthn/browser': 13.1.0 '@simplewebauthn/types': 12.0.0 @@ -16030,7 +16433,7 @@ snapshots: dependencies: lodash.camelcase: 4.3.0 long: 5.3.2 - protobufjs: 7.5.4 + protobufjs: 7.5.5 yargs: 17.7.2 '@hapi/b64@5.0.0': @@ -17476,10 +17879,10 @@ snapshots: dependencies: state-local: 1.0.7 - '@monaco-editor/react@4.7.0(monaco-editor@0.34.1)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: '@monaco-editor/loader': 1.7.0 - monaco-editor: 0.34.1 + monaco-editor: 0.52.2 react: 19.1.4 react-dom: 19.1.4(react@19.1.4) @@ -17626,7 +18029,7 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/bundle-analyzer@16.1.7(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + '@next/bundle-analyzer@16.2.4(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: webpack-bundle-analyzer: 4.10.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: @@ -17635,34 +18038,34 @@ snapshots: '@next/env@13.5.11': {} - '@next/env@16.1.7': {} + '@next/env@16.2.4': {} - '@next/eslint-plugin-next@16.1.7': + '@next/eslint-plugin-next@16.2.4': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.1.7': + '@next/swc-darwin-arm64@16.2.4': optional: true - '@next/swc-darwin-x64@16.1.7': + '@next/swc-darwin-x64@16.2.4': optional: true - '@next/swc-linux-arm64-gnu@16.1.7': + '@next/swc-linux-arm64-gnu@16.2.4': optional: true - '@next/swc-linux-arm64-musl@16.1.7': + '@next/swc-linux-arm64-musl@16.2.4': optional: true - '@next/swc-linux-x64-gnu@16.1.7': + '@next/swc-linux-x64-gnu@16.2.4': optional: true - '@next/swc-linux-x64-musl@16.1.7': + '@next/swc-linux-x64-musl@16.2.4': optional: true - '@next/swc-win32-arm64-msvc@16.1.7': + '@next/swc-win32-arm64-msvc@16.2.4': optional: true - '@next/swc-win32-x64-msvc@16.1.7': + '@next/swc-win32-x64-msvc@16.2.4': optional: true '@noble/ciphers@0.4.1': {} @@ -18258,14 +18661,14 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-exporter-base': 0.49.1(@opentelemetry/api@1.9.0) - protobufjs: 7.5.4 + protobufjs: 7.5.5 '@opentelemetry/otlp-proto-exporter-base@0.49.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-exporter-base': 0.49.1(@opentelemetry/api@1.9.0) - protobufjs: 7.5.4 + protobufjs: 7.5.5 '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)': dependencies: @@ -18276,7 +18679,7 @@ snapshots: '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - protobufjs: 7.5.4 + protobufjs: 7.5.5 '@opentelemetry/otlp-transformer@0.49.1(@opentelemetry/api@1.9.0)': dependencies: @@ -18567,11 +18970,11 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/experimental-ct-core@1.57.0(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)': + '@playwright/experimental-ct-core@1.57.0(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)': dependencies: playwright: 1.57.0 playwright-core: 1.57.0 - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -18585,10 +18988,10 @@ snapshots: - tsx - yaml - '@playwright/experimental-ct-react@1.57.0(@types/node@20.16.7)(terser@5.46.1)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2))(yaml@2.8.2)': + '@playwright/experimental-ct-react@1.57.0(@types/node@20.16.7)(terser@5.46.1)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3))(yaml@2.8.3)': dependencies: - '@playwright/experimental-ct-core': 1.57.0(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) - '@vitejs/plugin-react': 4.3.4(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + '@playwright/experimental-ct-core': 1.57.0(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) + '@vitejs/plugin-react': 4.3.4(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) transitivePeerDependencies: - '@types/node' - jiti @@ -19875,6 +20278,8 @@ snapshots: '@sindresorhus/fnv1a@3.1.0': {} + '@sindresorhus/is@4.6.0': {} + '@sindresorhus/is@7.2.0': {} '@sindresorhus/merge-streams@2.3.0': {} @@ -21101,6 +21506,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + '@tabler/icons-react@3.40.0(react@19.1.4)': dependencies: '@tabler/icons': 3.40.0 @@ -21108,13 +21517,14 @@ snapshots: '@tabler/icons@3.40.0': {} - '@tanstack/eslint-plugin-query@5.91.2(eslint@9.39.2)(typescript@5.9.2)': + '@tanstack/eslint-plugin-query@5.100.5(eslint@9.39.2)(typescript@5.9.2)': dependencies: - '@typescript-eslint/utils': 8.57.1(eslint@9.39.2)(typescript@5.9.2) + '@typescript-eslint/utils': 8.59.0(eslint@9.39.2)(typescript@5.9.2) eslint: 9.39.2 + optionalDependencies: + typescript: 5.9.2 transitivePeerDependencies: - supports-color - - typescript '@tanstack/query-core@5.55.4': {} @@ -21337,6 +21747,13 @@ snapshots: dependencies: '@types/node': 22.12.0 + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.2.0 + '@types/keyv': 3.1.4 + '@types/node': 22.12.0 + '@types/responselike': 1.0.3 + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -21615,6 +22032,10 @@ snapshots: '@types/keygrip@1.0.6': {} + '@types/keyv@3.1.4': + dependencies: + '@types/node': 22.12.0 + '@types/koa-compose@3.2.9': dependencies: '@types/koa': 2.14.0 @@ -21730,6 +22151,10 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/responselike@1.0.3': + dependencies: + '@types/node': 22.12.0 + '@types/retry@0.12.2': {} '@types/semver@7.7.1': {} @@ -21883,6 +22308,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.59.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.2) + '@typescript-eslint/types': 8.59.0 + debug: 4.4.3 + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 @@ -21903,6 +22337,11 @@ snapshots: '@typescript-eslint/types': 8.57.1 '@typescript-eslint/visitor-keys': 8.57.1 + '@typescript-eslint/scope-manager@8.59.0': + dependencies: + '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/visitor-keys': 8.59.0 + '@typescript-eslint/tsconfig-utils@8.41.0(typescript@5.9.2)': dependencies: typescript: 5.9.2 @@ -21911,6 +22350,10 @@ snapshots: dependencies: typescript: 5.9.2 + '@typescript-eslint/tsconfig-utils@8.59.0(typescript@5.9.2)': + dependencies: + typescript: 5.9.2 + '@typescript-eslint/type-utils@5.62.0(eslint@9.39.2)(typescript@5.9.2)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.9.2) @@ -21955,6 +22398,8 @@ snapshots: '@typescript-eslint/types@8.57.1': {} + '@typescript-eslint/types@8.59.0': {} + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.9.2)': dependencies: '@typescript-eslint/types': 5.62.0 @@ -22015,6 +22460,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.59.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/project-service': 8.59.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.2) + '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/visitor-keys': 8.59.0 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@5.62.0(eslint@9.39.2)(typescript@5.9.2)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) @@ -22063,6 +22523,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.59.0(eslint@9.39.2)(typescript@5.9.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.59.0 + '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.2) + eslint: 9.39.2 + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 @@ -22083,6 +22554,11 @@ snapshots: '@typescript-eslint/types': 8.57.1 eslint-visitor-keys: 5.0.1 + '@typescript-eslint/visitor-keys@8.59.0': + dependencies: + '@typescript-eslint/types': 8.59.0 + eslint-visitor-keys: 5.0.1 + '@uidotdev/usehooks@2.4.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: react: 19.1.4 @@ -22176,14 +22652,14 @@ snapshots: '@visulima/boxen@2.0.10': {} - '@vitejs/plugin-react@4.3.4(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2))': + '@vitejs/plugin-react@4.3.4(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - supports-color @@ -22196,13 +22672,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.0.15(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2))': + '@vitest/mocker@4.0.15(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.0.15 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) '@vitest/pretty-format@4.0.15': dependencies: @@ -24042,6 +24518,21 @@ snapshots: typescript: 5.9.2 zod: 4.3.6 + ably@2.17.1(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(utf-8-validate@6.0.6): + dependencies: + '@ably/msgpack-js': 0.4.1 + dequal: 2.0.3 + fastestsmallesttextencoderdecoder: 1.0.22 + got: 11.8.6 + ulid: 2.4.0 + ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -24415,6 +24906,8 @@ snapshots: base64-arraybuffer@1.0.2: {} + base64-js@1.0.2: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.10.8: {} @@ -24431,8 +24924,6 @@ snapshots: bignumber.js@9.3.1: {} - binary-extensions@2.3.0: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -24468,6 +24959,11 @@ snapshots: boolbase@1.0.0: {} + bops@1.0.1: + dependencies: + base64-js: 1.0.2 + to-utf8: 0.0.1 + borsh@0.7.0: dependencies: bn.js: 5.2.3 @@ -24553,6 +25049,8 @@ snapshots: byte-counter@0.1.0: {} + cacheable-lookup@5.0.4: {} + cacheable-lookup@7.0.0: {} cacheable-request@13.0.18: @@ -24565,6 +25063,16 @@ snapshots: normalize-url: 8.1.1 responselike: 4.0.2 + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -24615,17 +25123,9 @@ snapshots: charenc@0.0.2: {} - chokidar@3.6.0: + chokidar@4.0.3: dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + readdirp: 4.1.2 chokidar@5.0.0: dependencies: @@ -24699,6 +25199,10 @@ snapshots: dependencies: is-regexp: 3.1.0 + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + clone@1.0.4: optional: true @@ -24861,61 +25365,61 @@ snapshots: crypto-js@4.2.0: {} - cspell-config-lib@9.6.4: + cspell-config-lib@9.8.0: dependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.8.0 comment-json: 4.6.2 - smol-toml: 1.6.0 - yaml: 2.8.2 + smol-toml: 1.6.1 + yaml: 2.8.3 - cspell-dictionary@9.6.4: + cspell-dictionary@9.8.0: dependencies: - '@cspell/cspell-performance-monitor': 9.6.4 - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-types': 9.6.4 - cspell-trie-lib: 9.6.4(@cspell/cspell-types@9.6.4) + '@cspell/cspell-performance-monitor': 9.8.0 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-types': 9.8.0 + cspell-trie-lib: 9.8.0(@cspell/cspell-types@9.8.0) fast-equals: 6.0.0 - cspell-gitignore@9.6.4: + cspell-gitignore@9.8.0: dependencies: - '@cspell/url': 9.6.4 - cspell-glob: 9.6.4 - cspell-io: 9.6.4 + '@cspell/url': 9.8.0 + cspell-glob: 9.8.0 + cspell-io: 9.8.0 - cspell-glob@9.6.4: + cspell-glob@9.8.0: dependencies: - '@cspell/url': 9.6.4 - picomatch: 4.0.3 + '@cspell/url': 9.8.0 + picomatch: 4.0.4 - cspell-grammar@9.6.4: + cspell-grammar@9.8.0: dependencies: - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-types': 9.8.0 - cspell-io@9.6.4: + cspell-io@9.8.0: dependencies: - '@cspell/cspell-service-bus': 9.6.4 - '@cspell/url': 9.6.4 + '@cspell/cspell-service-bus': 9.8.0 + '@cspell/url': 9.8.0 - cspell-lib@9.6.4: + cspell-lib@9.8.0: dependencies: - '@cspell/cspell-bundled-dicts': 9.6.4 - '@cspell/cspell-performance-monitor': 9.6.4 - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-resolver': 9.6.4 - '@cspell/cspell-types': 9.6.4 - '@cspell/dynamic-import': 9.6.4 - '@cspell/filetypes': 9.6.4 - '@cspell/rpc': 9.6.4 - '@cspell/strong-weak-map': 9.6.4 - '@cspell/url': 9.6.4 + '@cspell/cspell-bundled-dicts': 9.8.0 + '@cspell/cspell-performance-monitor': 9.8.0 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-resolver': 9.8.0 + '@cspell/cspell-types': 9.8.0 + '@cspell/dynamic-import': 9.8.0 + '@cspell/filetypes': 9.8.0 + '@cspell/rpc': 9.8.0 + '@cspell/strong-weak-map': 9.8.0 + '@cspell/url': 9.8.0 clear-module: 4.1.2 - cspell-config-lib: 9.6.4 - cspell-dictionary: 9.6.4 - cspell-glob: 9.6.4 - cspell-grammar: 9.6.4 - cspell-io: 9.6.4 - cspell-trie-lib: 9.6.4(@cspell/cspell-types@9.6.4) + cspell-config-lib: 9.8.0 + cspell-dictionary: 9.8.0 + cspell-glob: 9.8.0 + cspell-grammar: 9.8.0 + cspell-io: 9.8.0 + cspell-trie-lib: 9.8.0(@cspell/cspell-types@9.8.0) env-paths: 4.0.0 gensequence: 8.0.8 import-fresh: 3.3.1 @@ -24924,29 +25428,29 @@ snapshots: vscode-uri: 3.1.0 xdg-basedir: 5.1.0 - cspell-trie-lib@9.6.4(@cspell/cspell-types@9.6.4): + cspell-trie-lib@9.8.0(@cspell/cspell-types@9.8.0): dependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.8.0 - cspell@9.6.4: + cspell@9.8.0: dependencies: - '@cspell/cspell-json-reporter': 9.6.4 - '@cspell/cspell-performance-monitor': 9.6.4 - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-types': 9.6.4 - '@cspell/cspell-worker': 9.6.4 - '@cspell/dynamic-import': 9.6.4 - '@cspell/url': 9.6.4 + '@cspell/cspell-json-reporter': 9.8.0 + '@cspell/cspell-performance-monitor': 9.8.0 + '@cspell/cspell-pipe': 9.8.0 + '@cspell/cspell-types': 9.8.0 + '@cspell/cspell-worker': 9.8.0 + '@cspell/dynamic-import': 9.8.0 + '@cspell/url': 9.8.0 ansi-regex: 6.2.2 chalk: 5.6.2 chalk-template: 1.1.2 commander: 14.0.3 - cspell-config-lib: 9.6.4 - cspell-dictionary: 9.6.4 - cspell-gitignore: 9.6.4 - cspell-glob: 9.6.4 - cspell-io: 9.6.4 - cspell-lib: 9.6.4 + cspell-config-lib: 9.8.0 + cspell-dictionary: 9.8.0 + cspell-gitignore: 9.8.0 + cspell-glob: 9.8.0 + cspell-io: 9.8.0 + cspell-lib: 9.8.0 fast-json-stable-stringify: 2.1.0 flatted: 3.4.2 semver: 7.7.4 @@ -25327,6 +25831,8 @@ snapshots: clone: 1.0.4 optional: true + defer-to-connect@2.0.1: {} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -25362,6 +25868,8 @@ snapshots: transitivePeerDependencies: - supports-color + dequal@2.0.3: {} + derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4)): dependencies: valtio: 1.13.2(@types/react@19.1.7)(react@19.1.4) @@ -25800,9 +26308,9 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@16.1.7(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2): + eslint-config-next@16.2.4(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2): dependencies: - '@next/eslint-plugin-next': 16.1.7 + '@next/eslint-plugin-next': 16.2.4 eslint: 9.39.2 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) @@ -25854,6 +26362,21 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-plugin-boundaries@6.0.2(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2): + dependencies: + '@boundaries/elements': 2.0.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2) + chalk: 4.1.2 + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + handlebars: 4.7.9 + micromatch: 4.0.8 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + eslint-plugin-consistent-default-export-name@0.0.15: dependencies: lodash: 4.17.23 @@ -25993,13 +26516,13 @@ snapshots: regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2)(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2)): + eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2)(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3)): dependencies: '@typescript-eslint/utils': 7.18.0(eslint@9.39.2)(typescript@5.9.2) eslint: 9.39.2 optionalDependencies: '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2)(typescript@5.9.2))(eslint@9.39.2)(typescript@5.9.2) - vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2) + vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - supports-color - typescript @@ -26462,6 +26985,10 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + get-stream@9.0.1: dependencies: '@sec-ant/readable-stream': 0.4.1 @@ -26519,9 +27046,9 @@ snapshots: minimatch: 5.1.9 once: 1.4.0 - global-directory@4.0.1: + global-directory@5.0.0: dependencies: - ini: 4.1.1 + ini: 6.0.0 globals@13.24.0: dependencies: @@ -26566,6 +27093,20 @@ snapshots: gopd@1.2.0: {} + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + got@14.6.6: dependencies: '@sindresorhus/is': 7.2.0 @@ -26656,6 +27197,15 @@ snapshots: sparse-array: 1.3.2 uint8arrays: 5.1.0 + handlebars@4.7.9: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -26835,6 +27385,11 @@ snapshots: transitivePeerDependencies: - supports-color + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 @@ -26912,10 +27467,10 @@ snapshots: ini@1.3.8: {} - ini@4.1.1: {} - ini@5.0.0: {} + ini@6.0.0: {} + interface-blockstore@5.3.2: dependencies: interface-store: 6.0.3 @@ -27049,10 +27604,6 @@ snapshots: dependencies: has-bigints: 1.1.0 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -27799,6 +28350,8 @@ snapshots: dependencies: tslib: 2.8.1 + lowercase-keys@2.0.0: {} + lowercase-keys@3.0.0: {} lowlight@1.20.0: @@ -28080,6 +28633,8 @@ snapshots: mimic-function@5.0.1: {} + mimic-response@1.0.1: {} + mimic-response@3.1.0: {} mimic-response@4.0.0: {} @@ -28151,7 +28706,7 @@ snapshots: requirejs: 2.3.8 requirejs-config-file: 4.0.0 - monaco-editor@0.34.1: {} + monaco-editor@0.52.2: {} mortice@3.3.1: dependencies: @@ -28208,22 +28763,22 @@ snapshots: netmask@2.0.2: {} - next-sitemap@4.2.3(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): + next-sitemap@4.2.3(next@16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.11 fast-glob: 3.3.3 minimist: 1.2.8 - next: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + next: 16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) next-themes@0.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4): dependencies: react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): + next@16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): dependencies: - '@next/env': 16.1.7 + '@next/env': 16.2.4 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.8 caniuse-lite: 1.0.30001780 @@ -28232,14 +28787,14 @@ snapshots: react-dom: 19.1.4(react@19.1.4) styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.1.4) optionalDependencies: - '@next/swc-darwin-arm64': 16.1.7 - '@next/swc-darwin-x64': 16.1.7 - '@next/swc-linux-arm64-gnu': 16.1.7 - '@next/swc-linux-arm64-musl': 16.1.7 - '@next/swc-linux-x64-gnu': 16.1.7 - '@next/swc-linux-x64-musl': 16.1.7 - '@next/swc-win32-arm64-msvc': 16.1.7 - '@next/swc-win32-x64-msvc': 16.1.7 + '@next/swc-darwin-arm64': 16.2.4 + '@next/swc-darwin-x64': 16.2.4 + '@next/swc-linux-arm64-gnu': 16.2.4 + '@next/swc-linux-arm64-musl': 16.2.4 + '@next/swc-linux-x64-gnu': 16.2.4 + '@next/swc-linux-x64-musl': 16.2.4 + '@next/swc-win32-arm64-msvc': 16.2.4 + '@next/swc-win32-x64-msvc': 16.2.4 '@opentelemetry/api': 1.9.0 '@playwright/test': 1.57.0 sharp: 0.34.5 @@ -28247,10 +28802,10 @@ snapshots: - '@babel/core' - babel-plugin-macros - nextjs-routes@1.0.9(next@16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): + nextjs-routes@2.2.5(next@16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): dependencies: - chokidar: 3.6.0 - next: 16.1.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + chokidar: 4.0.3 + next: 16.2.4(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) no-case@3.0.4: dependencies: @@ -28319,6 +28874,8 @@ snapshots: normalize-path@3.0.0: {} + normalize-url@6.1.0: {} + normalize-url@8.1.1: {} nth-check@2.1.1: @@ -28450,7 +29007,30 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 +<<<<<<< HEAD ox@0.6.7(typescript@5.9.2)(zod@4.3.6): +======= + ox@0.14.7(typescript@5.9.2)(zod@4.3.6): +>>>>>>> v2.8.0-alpha.1 + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.2)(zod@4.3.6) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - zod + +<<<<<<< HEAD + ox@0.6.9(typescript@5.9.2)(zod@4.3.6): +======= + ox@0.6.7(typescript@5.9.2)(zod@4.3.6): +>>>>>>> v2.8.0-alpha.1 dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 @@ -28464,6 +29044,8 @@ snapshots: transitivePeerDependencies: - zod +<<<<<<< HEAD +======= ox@0.6.9(typescript@5.9.2)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -28478,6 +29060,7 @@ snapshots: transitivePeerDependencies: - zod +>>>>>>> v2.8.0-alpha.1 ox@0.7.1(typescript@5.9.2)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -28568,6 +29151,11 @@ snapshots: transitivePeerDependencies: - zod +<<<<<<< HEAD +======= + p-cancelable@2.1.1: {} + +>>>>>>> v2.8.0-alpha.1 p-cancelable@4.0.1: {} p-defer@4.0.1: {} @@ -28670,7 +29258,7 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.3 - path-to-regexp@8.1.0: {} + path-to-regexp@8.4.2: {} path-type@4.0.0: {} @@ -28700,6 +29288,8 @@ snapshots: picomatch@4.0.3: {} + picomatch@4.0.4: {} + pify@3.0.0: {} pify@5.0.0: {} @@ -28983,7 +29573,7 @@ snapshots: property-information@7.1.0: {} - protobufjs@7.5.4: + protobufjs@7.5.5: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -29412,9 +30002,7 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 + readdirp@4.1.2: {} readdirp@5.0.0: {} @@ -29556,6 +30144,10 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + responselike@4.0.2: dependencies: lowercase-keys: 3.0.0 @@ -29942,7 +30534,7 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - smol-toml@1.6.0: {} + smol-toml@1.6.1: {} snake-case@3.0.4: dependencies: @@ -30427,6 +31019,8 @@ snapshots: dependencies: is-number: 7.0.0 + to-utf8@0.0.1: {} + toggle-selection@1.0.6: {} toidentifier@1.0.1: {} @@ -30634,6 +31228,9 @@ snapshots: ufo@1.6.3: {} + uglify-js@3.19.3: + optional: true + uint8-varint@2.0.4: dependencies: uint8arraylist: 2.4.8 @@ -30657,6 +31254,8 @@ snapshots: dependencies: multiformats: 13.4.2 + ulid@2.4.0: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -30960,9 +31559,29 @@ snapshots: - utf-8-validate - zod +<<<<<<< HEAD +======= + viem@2.47.10(bufferutil@4.1.0)(typescript@5.9.2)(utf-8-validate@6.0.6)(zod@4.3.6): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.2)(zod@4.3.6) + isows: 1.0.7(ws@8.17.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + ox: 0.14.7(typescript@5.9.2)(zod@4.3.6) + ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + +>>>>>>> v2.8.0-alpha.1 visit-values@2.0.0: {} - vite-plugin-dts@4.5.4(@types/node@20.16.7)(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-dts@4.5.4(@types/node@20.16.7)(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)): dependencies: '@microsoft/api-extractor': 7.57.7(@types/node@20.16.7) '@rollup/pluginutils': 5.3.0(rollup@4.59.0) @@ -30975,44 +31594,44 @@ snapshots: magic-string: 0.30.21 typescript: 5.9.2 optionalDependencies: - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite-plugin-svgr@2.4.0(rollup@4.59.0)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-svgr@2.4.0(rollup@4.59.0)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.59.0) '@svgr/core': 6.5.1 - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - rollup - supports-color - vite-plugin-svgr@4.5.0(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-svgr@4.5.0(rollup@4.59.0)(typescript@5.9.2)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.59.0) '@svgr/core': 8.1.0(typescript@5.9.2) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - rollup - supports-color - typescript - vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)): + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.2) optionalDependencies: - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) transitivePeerDependencies: - supports-color - typescript - vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2): + vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -31024,9 +31643,9 @@ snapshots: '@types/node': 20.16.7 fsevents: 2.3.3 terser: 5.46.1 - yaml: 2.8.2 + yaml: 2.8.3 - vite@6.4.1(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.2): + vite@6.4.2(@types/node@22.12.0)(terser@5.46.1)(yaml@2.8.3): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -31038,16 +31657,16 @@ snapshots: '@types/node': 22.12.0 fsevents: 2.3.3 terser: 5.46.1 - yaml: 2.8.2 + yaml: 2.8.3 - vitest-fetch-mock@0.4.5(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2)): + vitest-fetch-mock@0.4.5(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3)): dependencies: - vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2) + vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3) - vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.2): + vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(terser@5.46.1)(yaml@2.8.3): dependencies: '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2)) + '@vitest/mocker': 4.0.15(vite@6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3)) '@vitest/pretty-format': 4.0.15 '@vitest/runner': 4.0.15 '@vitest/snapshot': 4.0.15 @@ -31064,7 +31683,7 @@ snapshots: tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 6.4.1(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.2) + vite: 6.4.2(@types/node@20.16.7)(terser@5.46.1)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -31323,6 +31942,8 @@ snapshots: word-wrap@1.2.5: {} + wordwrap@1.0.0: {} + wordwrapjs@5.1.1: {} wrap-ansi@6.2.0: @@ -31403,6 +32024,8 @@ snapshots: yaml@2.8.2: {} + yaml@2.8.3: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 diff --git a/public/icons/name.d.ts b/public/icons/name.d.ts index efb940d2d7..51a6be6d20 100644 --- a/public/icons/name.d.ts +++ b/public/icons/name.d.ts @@ -50,6 +50,7 @@ | "delete" | "docs" | "dots" + | "download" | "edit" | "email" | "ENS" @@ -119,6 +120,7 @@ | "navigation/gas_tracker" | "navigation/hot_contracts" | "navigation/hourglass" + | "navigation/ictt_users" | "navigation/internal_txns" | "navigation/merits_with_dot" | "navigation/merits" diff --git a/stubs/ENS.ts b/stubs/ENS.ts index a35773f077..a70f881d64 100644 --- a/stubs/ENS.ts +++ b/stubs/ENS.ts @@ -1,7 +1,7 @@ import * as bens from '@blockscout/bens-types'; -import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS, ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const ENS_DOMAIN: bens.DetailedDomain = { id: '0x126d74db13895f8d3a1d362410212731d1e1d9be8add83e388385f93d84c8c84', diff --git a/stubs/L2.ts b/stubs/L2.ts index a117450fb0..51d301facc 100644 --- a/stubs/L2.ts +++ b/stubs/L2.ts @@ -7,8 +7,8 @@ import type { OptimisticL2WithdrawalsItem, } from 'types/api/optimisticL2'; -import { ADDRESS_HASH, ADDRESS_PARAMS } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_HASH, ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const L2_DEPOSIT_ITEM: OptimisticL2DepositsItem = { l1_block_number: 9045233, diff --git a/stubs/RPC.ts b/stubs/RPC.ts index 0da0947c7c..3db42894fc 100644 --- a/stubs/RPC.ts +++ b/stubs/RPC.ts @@ -1,8 +1,8 @@ import type { Chain, GetBlockReturnType, GetTransactionReturnType, TransactionReceipt, Withdrawal } from 'viem'; -import { ADDRESS_HASH } from './addressParams'; -import { BLOCK_HASH } from './block'; -import { TX_HASH } from './tx'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { BLOCK_HASH } from 'client/slices/block/stubs/block'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const WITHDRAWAL: Withdrawal = { index: '0x1af95d9', diff --git a/stubs/account.ts b/stubs/account.ts index f46a3a064f..a7dd53836f 100644 --- a/stubs/account.ts +++ b/stubs/account.ts @@ -1,7 +1,7 @@ import type { AddressTag, TransactionTag, ApiKey, CustomAbi, VerifiedAddress, TokenInfoApplication, WatchlistAddress } from 'types/api/account'; -import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS, ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const PRIVATE_TAG_ADDRESS: AddressTag = { address: ADDRESS_PARAMS, diff --git a/stubs/address3rdPartyWidgets.ts b/stubs/address3rdPartyWidgets.ts index 8f69e95cc2..b6dc73af5e 100644 --- a/stubs/address3rdPartyWidgets.ts +++ b/stubs/address3rdPartyWidgets.ts @@ -1,4 +1,4 @@ -import type { Address3rdPartyWidget } from 'types/views/address'; +import type { Address3rdPartyWidget } from 'client/features/address-3rd-party-widgets/types/view'; export const WIDGET_CONFIG: Address3rdPartyWidget = { name: 'name', diff --git a/stubs/advancedFilter.ts b/stubs/advancedFilter.ts index 341280220b..2f58c0b7a4 100644 --- a/stubs/advancedFilter.ts +++ b/stubs/advancedFilter.ts @@ -1,7 +1,7 @@ import type { AdvancedFilterResponseItem } from 'types/api/advancedFilter'; -import { ADDRESS_PARAMS } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const ADVANCED_FILTER_ITEM: AdvancedFilterResponseItem = { fee: '215504444616317', diff --git a/stubs/arbitrumL2.ts b/stubs/arbitrumL2.ts index f612838e5e..637d4390aa 100644 --- a/stubs/arbitrumL2.ts +++ b/stubs/arbitrumL2.ts @@ -1,7 +1,7 @@ import type { ArbitrumL2TxnBatchesItem, ArbitrumL2TxnBatch, ArbitrumL2MessagesItem, ArbitrumL2TxnWithdrawalsItem } from 'types/api/arbitrumL2'; -import { ADDRESS_HASH } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const ARBITRUM_MESSAGES_ITEM: ArbitrumL2MessagesItem = { completion_transaction_hash: TX_HASH, diff --git a/stubs/blobs.ts b/stubs/blobs.ts index 89ca5c4cb4..eebe1bad44 100644 --- a/stubs/blobs.ts +++ b/stubs/blobs.ts @@ -1,6 +1,6 @@ import type { Blob, TxBlob } from 'types/api/blobs'; -import { TX_HASH } from './tx'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; const BLOB_HASH = '0x0137cd898a9aaa92bbe94999d2a98241f5eabc829d9354160061789963f85995'; const BLOB_PROOF = '0x82683d5d6e58a76f2a607b8712cad113621d46cb86a6bcfcffb1e274a70c7308b3243c6075ee22d904fecf8d4c147c6f'; diff --git a/stubs/contract.ts b/stubs/contract.ts index d309160a96..1e22b3ecae 100644 --- a/stubs/contract.ts +++ b/stubs/contract.ts @@ -2,10 +2,11 @@ import type * as stats from '@blockscout/stats-types'; import type { SmartContract, SmartContractMudSystemsResponse } from 'types/api/contract'; import type { HotContract, VerifiedContract, VerifiedContractsCounters } from 'types/api/contracts'; -import type { SolidityScanReport } from 'lib/solidityScan/schema'; +import { ADDRESS_PARAMS, ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; + +import { CHAIN_STATS_COUNTER } from 'client/features/chain-stats/stubs/counters'; -import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams'; -import { STATS_COUNTER } from './stats'; +import type { SolidityScanReport } from 'lib/solidityScan/schema'; export const CONTRACT_CODE_UNVERIFIED = { creation_bytecode: '0x60806040526e', @@ -84,10 +85,10 @@ export const VERIFIED_CONTRACTS_COUNTERS: VerifiedContractsCounters = { }; export const VERIFIED_CONTRACTS_COUNTERS_MICROSERVICE: stats.ContractsPageStats = { - total_contracts: STATS_COUNTER, - new_contracts_24h: STATS_COUNTER, - total_verified_contracts: STATS_COUNTER, - new_verified_contracts_24h: STATS_COUNTER, + total_contracts: CHAIN_STATS_COUNTER, + new_contracts_24h: CHAIN_STATS_COUNTER, + total_verified_contracts: CHAIN_STATS_COUNTER, + new_verified_contracts_24h: CHAIN_STATS_COUNTER, }; export const SOLIDITY_SCAN_REPORT: SolidityScanReport = { diff --git a/stubs/deposits.ts b/stubs/deposits.ts index 91b14505d0..d5a14b300f 100644 --- a/stubs/deposits.ts +++ b/stubs/deposits.ts @@ -1,7 +1,7 @@ import type { DepositsItem } from 'types/api/deposits'; -import { ADDRESS_PARAMS } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const DEPOSIT: DepositsItem = { amount: '12565723', diff --git a/stubs/epoch.ts b/stubs/epoch.ts index 23b0c96923..67c89e5573 100644 --- a/stubs/epoch.ts +++ b/stubs/epoch.ts @@ -1,6 +1,7 @@ import type { CeloEpochListItem, CeloEpochDetails, CeloEpochElectionReward } from 'types/api/epochs'; -import { BLOCK_HASH } from './block'; +import { BLOCK_HASH } from 'client/slices/block/stubs/block'; + import { TOKEN_INFO_ERC_20, TOKEN_TRANSFER_ERC_20, TOKEN_TRANSFER_ERC_20_TOTAL } from './token'; export const CELO_EPOCH_ITEM: CeloEpochListItem = { diff --git a/stubs/fheOperations.ts b/stubs/fheOperations.ts index 66523daaf2..1d70927534 100644 --- a/stubs/fheOperations.ts +++ b/stubs/fheOperations.ts @@ -1,6 +1,6 @@ import type { FheOperation, FheOperationsResponse } from 'types/api/fheOperations'; -import { ADDRESS_PARAMS } from './addressParams'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; export const FHE_OPERATION: FheOperation = { log_index: 0, diff --git a/stubs/interchainIndexer.ts b/stubs/interchainIndexer.ts index 5b8b1370c6..fde54deb3c 100644 --- a/stubs/interchainIndexer.ts +++ b/stubs/interchainIndexer.ts @@ -4,14 +4,23 @@ import type { GetDailyStatisticsResponse, InterchainMessage, InterchainTransfer, + StatsBridgedTokenRow, + StatsChainRow, } from '@blockscout/interchain-indexer-types'; import { MessageStatus } from '@blockscout/interchain-indexer-types'; -import { ADDRESS_HASH } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; const MESSAGE_ID = '0xde5c33c6b3424cec51ea6f5d081f64719d531eec74f2a2408141274c117c5f44'; +const TOKEN = { + address_hash: ADDRESS_HASH, + name: 'Wrapped AVAX', + symbol: 'WAVAX', + decimals: '18', +}; + const CHAIN = { id: '8021', name: 'Avalanche', @@ -25,12 +34,7 @@ export const INTERCHAIN_TRANSFER = { }, message_id: MESSAGE_ID, status: MessageStatus.MESSAGE_STATUS_COMPLETED, - source_token: { - address_hash: ADDRESS_HASH, - name: 'Wrapped AVAX', - symbol: 'WAVAX', - decimals: '18', - }, + source_token: TOKEN, source_amount: '80800000000000000', source_transaction_hash: TX_HASH, source_chain: CHAIN, @@ -38,12 +42,7 @@ export const INTERCHAIN_TRANSFER = { hash: ADDRESS_HASH, }, send_timestamp: '2026-01-07T18:41:50.000Z', - destination_token: { - address_hash: ADDRESS_HASH, - name: 'Wrapped AVAX', - symbol: 'WAVAX', - decimals: '18', - }, + destination_token: TOKEN, destination_amount: '80800000000000000', destination_transaction_hash: TX_HASH, destination_chain: CHAIN, @@ -89,3 +88,31 @@ export const INTERCHAIN_STATS_COMMON = { total_messages: 10823, total_transfers: 10822, } satisfies GetCommonStatisticsResponse; + +export const INTERCHAIN_STATS_CHAINS_ITEM = { + id: '8021', + name: 'NUMINE Mainnet', + logo: undefined, + explorer_url: 'https://subnets.avax.network/numi', + unique_transfer_users_count: 2544, +} satisfies StatsChainRow; + +export const INTERCHAIN_BRIDGED_TOKEN_ITEM = { + stats_asset_id: '1', + name: 'Wrapped AVAX', + symbol: 'WAVAX', + icon_url: undefined, + input_transfers_count: 4906, + output_transfers_count: 6908, + total_transfers_count: 11814, + tokens: [ + { + chain_id: CHAIN.id, + token_address: ADDRESS_HASH, + name: 'Wrapped AVAX', + symbol: 'WAVAX', + icon_url: undefined, + decimals: 18, + }, + ], +} satisfies StatsBridgedTokenRow; diff --git a/stubs/internalTx.ts b/stubs/internalTx.ts index f0e24bd1f1..ad67e0347a 100644 --- a/stubs/internalTx.ts +++ b/stubs/internalTx.ts @@ -1,7 +1,7 @@ import type { InternalTransaction } from 'types/api/internalTransaction'; -import { ADDRESS_PARAMS } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const INTERNAL_TX: InternalTransaction = { block_number: 9006105, diff --git a/stubs/interop.ts b/stubs/interop.ts index 6130e75942..ec93131c07 100644 --- a/stubs/interop.ts +++ b/stubs/interop.ts @@ -1,7 +1,7 @@ import type { InteropMessage } from 'types/api/interop'; -import { ADDRESS_HASH } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const INTEROP_MESSAGE: InteropMessage = { init_transaction_hash: TX_HASH, diff --git a/stubs/mud.ts b/stubs/mud.ts index dbbb51c898..6241e968a7 100644 --- a/stubs/mud.ts +++ b/stubs/mud.ts @@ -1,6 +1,6 @@ import type { MudWorldItem, MudWorldSchema, MudWorldTable } from 'types/api/mudWorlds'; -import { ADDRESS_PARAMS } from './addressParams'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; export const MUD_TABLE: MudWorldTable = { table_full_name: 'ot.Match', diff --git a/stubs/multichain.ts b/stubs/multichain.ts index 7e4196fb54..a7732c040e 100644 --- a/stubs/multichain.ts +++ b/stubs/multichain.ts @@ -2,7 +2,7 @@ import type * as multichain from '@blockscout/multichain-aggregator-types'; import type * as stats from '@blockscout/stats-types'; import type { AddressTokenItem } from 'types/client/multichainAggregator'; -import { ADDRESS_HASH } from './addressParams'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; export const ADDRESS: multichain.GetAddressResponse = { hash: ADDRESS_HASH, diff --git a/stubs/operations.ts b/stubs/operations.ts index 4b53e7fa5e..ee20e5c400 100644 --- a/stubs/operations.ts +++ b/stubs/operations.ts @@ -1,6 +1,6 @@ import * as tac from '@blockscout/tac-operation-lifecycle-types'; -import { ADDRESS_HASH } from './addressParams'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; export const TAC_OPERATION: tac.OperationBriefDetails = { operation_id: '0x4d3d36b7fcab0a2f93f24bf313ebfe9cc0b2c7157d2aef7e7f7d5835528428c6', diff --git a/stubs/scrollL2.ts b/stubs/scrollL2.ts index df919e3e3a..3912148f32 100644 --- a/stubs/scrollL2.ts +++ b/stubs/scrollL2.ts @@ -1,6 +1,6 @@ import type { ScrollL2MessageItem, ScrollL2TxnBatch } from 'types/api/scrollL2'; -import { TX_HASH } from './tx'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const SCROLL_L2_TXN_BATCH: ScrollL2TxnBatch = { commitment_transaction: { diff --git a/stubs/search.ts b/stubs/search.ts index df961d3524..0bcb43fa46 100644 --- a/stubs/search.ts +++ b/stubs/search.ts @@ -1,6 +1,6 @@ import type { SearchResult, SearchResultItem } from 'types/api/search'; -import { ADDRESS_HASH } from './addressParams'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; export const SEARCH_RESULT_ITEM: SearchResultItem = { address_hash: ADDRESS_HASH, diff --git a/stubs/shibarium.ts b/stubs/shibarium.ts index 3003be7110..d9e7cf2828 100644 --- a/stubs/shibarium.ts +++ b/stubs/shibarium.ts @@ -1,7 +1,7 @@ import type { ShibariumDepositsItem, ShibariumWithdrawalsItem } from 'types/api/shibarium'; -import { ADDRESS_PARAMS } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const SHIBARIUM_DEPOSIT_ITEM: ShibariumDepositsItem = { l1_block_number: 9045233, diff --git a/stubs/stats.ts b/stubs/stats.ts index 91e0eed0fe..cadeb937d8 100644 --- a/stubs/stats.ts +++ b/stubs/stats.ts @@ -1,6 +1,9 @@ import type * as stats from '@blockscout/stats-types'; import type { HomeStats } from 'types/api/stats'; +import { CHAIN_STATS_CHART_INFO } from 'client/features/chain-stats/stubs/charts'; +import { CHAIN_STATS_COUNTER } from 'client/features/chain-stats/stubs/counters'; + export const HOMEPAGE_STATS: HomeStats = { average_block_time: 14346, coin_price: '1807.68', @@ -42,81 +45,20 @@ export const HOMEPAGE_STATS: HomeStats = { tvl: '1767425.102766552', }; -const STATS_CHART_INFO: stats.LineChartInfo = { - id: 'chart_0', - title: 'Average transaction fee', - description: 'The average amount in ETH spent per transaction', - units: 'ETH', - resolutions: [ 'DAY', 'MONTH' ], -}; - -export const STATS_CHARTS_SECTION: stats.LineChartSection = { - id: 'placeholder', - title: 'Placeholder', - charts: [ - STATS_CHART_INFO, - { - id: 'chart_1', - title: 'Transactions fees', - description: 'Amount of tokens paid as fees', - units: 'ETH', - resolutions: [ 'DAY', 'MONTH' ], - }, - { - id: 'chart_2', - title: 'New transactions', - description: 'New transactions number', - units: undefined, - resolutions: [ 'DAY', 'MONTH' ], - }, - { - id: 'chart_3', - title: 'Transactions growth', - description: 'Cumulative transactions number', - units: undefined, - resolutions: [ 'DAY', 'MONTH' ], - }, - ], -}; - -export const STATS_CHARTS_SECTION_GAS: stats.LineChartSection = { - id: 'gas', - title: 'Gas', - charts: [ { - id: 'averageGasPrice', - title: 'Average gas price', - description: 'Average gas price', - units: 'ETH', - resolutions: [ 'DAY', 'MONTH' ], - } ], -}; - -export const STATS_CHARTS = { - sections: [ STATS_CHARTS_SECTION ], -}; - -export const STATS_COUNTER: stats.Counter = { - id: 'stub', - value: '9074405', - title: 'Placeholder Counter', - description: 'Placeholder description', - units: '', -}; - export const HOMEPAGE_STATS_MICROSERVICE: stats.MainPageStats = { - average_block_time: STATS_COUNTER, - total_addresses: STATS_COUNTER, - total_blocks: STATS_COUNTER, - total_transactions: STATS_COUNTER, - yesterday_transactions: STATS_COUNTER, - total_operational_transactions: STATS_COUNTER, - yesterday_operational_transactions: STATS_COUNTER, + average_block_time: CHAIN_STATS_COUNTER, + total_addresses: CHAIN_STATS_COUNTER, + total_blocks: CHAIN_STATS_COUNTER, + total_transactions: CHAIN_STATS_COUNTER, + yesterday_transactions: CHAIN_STATS_COUNTER, + total_operational_transactions: CHAIN_STATS_COUNTER, + yesterday_operational_transactions: CHAIN_STATS_COUNTER, daily_new_transactions: { chart: [], - info: { ...STATS_CHART_INFO, title: 'Daily transactions' }, + info: { ...CHAIN_STATS_CHART_INFO, title: 'Daily transactions' }, }, daily_new_operational_transactions: { chart: [], - info: { ...STATS_CHART_INFO, title: 'Daily op txns' }, + info: { ...CHAIN_STATS_CHART_INFO, title: 'Daily op txns' }, }, }; diff --git a/stubs/token.ts b/stubs/token.ts index 0a16297547..a3ecb3ba18 100644 --- a/stubs/token.ts +++ b/stubs/token.ts @@ -10,8 +10,9 @@ import type { import type { TokenInstanceTransferPagination, TokenInstanceTransferResponse } from 'types/api/tokens'; import type { Erc20TotalPayload, TokenTransfer, TokenTransferPagination, TokenTransferResponse } from 'types/api/tokenTransfer'; -import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams'; -import { TX_HASH } from './tx'; +import { ADDRESS_PARAMS, ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; + import { generateListStub } from './utils'; export const BLOCK_HASH = '0x8fa7b9e5e5e79deeb62d608db22ba9a5cb45388c7ebb9223ae77331c6080dc70'; diff --git a/stubs/userOps.ts b/stubs/userOps.ts index 283e43c4fd..79f6179ef2 100644 --- a/stubs/userOps.ts +++ b/stubs/userOps.ts @@ -1,8 +1,8 @@ import type { UserOpsItem, UserOp, UserOpsAccount } from 'types/api/userOps'; -import { ADDRESS_HASH } from './addressParams'; -import { BLOCK_HASH } from './block'; -import { TX_HASH } from './tx'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { BLOCK_HASH } from 'client/slices/block/stubs/block'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; const USER_OP_HASH = '0xb94fab8f31f83001a23e20b2ce3cdcfb284c57a64b9a073e0e09c018bc701978'; diff --git a/stubs/utils.ts b/stubs/utils.ts index 929a41d72e..bbbbd35a43 100644 --- a/stubs/utils.ts +++ b/stubs/utils.ts @@ -1,6 +1,6 @@ import type { ArrayElement } from 'types/utils'; -import type { PaginatedResourceName, PaginatedResourceResponse, PaginatedResourceResponseItems } from 'lib/api/resources'; +import type { PaginatedResourceName, PaginatedResourceResponse, PaginatedResourceResponseItems } from 'client/api/resources'; export function generateListStub( stub: ArrayElement>, diff --git a/stubs/validators.ts b/stubs/validators.ts index 6a0c1a22c3..0720177434 100644 --- a/stubs/validators.ts +++ b/stubs/validators.ts @@ -7,7 +7,7 @@ import type { ValidatorZilliqa, } from 'types/api/validators'; -import { ADDRESS_PARAMS } from './addressParams'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; export const VALIDATOR_STABILITY: ValidatorStability = { address: ADDRESS_PARAMS, diff --git a/stubs/withdrawals.ts b/stubs/withdrawals.ts index 25fbb4a826..8910e82adf 100644 --- a/stubs/withdrawals.ts +++ b/stubs/withdrawals.ts @@ -1,6 +1,6 @@ import type { WithdrawalsItem } from 'types/api/withdrawals'; -import { ADDRESS_PARAMS } from './addressParams'; +import { ADDRESS_PARAMS } from 'client/slices/address/stubs/address-params'; export const WITHDRAWAL: WithdrawalsItem = { amount: '12565723', diff --git a/stubs/zetaChainCCTX.ts b/stubs/zetaChainCCTX.ts index ef24628068..5ba09c9e5a 100644 --- a/stubs/zetaChainCCTX.ts +++ b/stubs/zetaChainCCTX.ts @@ -1,8 +1,8 @@ import * as zetaChainCCTXType from '@blockscout/zetachain-cctx-types'; -import { ADDRESS_HASH } from './addressParams'; -import { BLOCK_HASH } from './block'; -import { TX_HASH } from './tx'; +import { ADDRESS_HASH } from 'client/slices/address/stubs/address-params'; +import { BLOCK_HASH } from 'client/slices/block/stubs/block'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const ZETA_CHAIN_CCTX: zetaChainCCTXType.CrossChainTx = { creator: ADDRESS_HASH, diff --git a/stubs/zkEvmL2.ts b/stubs/zkEvmL2.ts deleted file mode 100644 index 85f7f3e310..0000000000 --- a/stubs/zkEvmL2.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { ZkEvmL2DepositsItem, ZkEvmL2TxnBatch, ZkEvmL2TxnBatchesItem, ZkEvmL2WithdrawalsItem } from 'types/api/zkEvmL2'; - -import { TX_HASH } from './tx'; - -export const ZKEVM_DEPOSITS_ITEM: ZkEvmL2DepositsItem = { - block_number: 19674901, - index: 181920, - l1_transaction_hash: '0xa74edfa5824a07a5f95ca1145140ed589df7f05bb17796bf18090b14c4566b5d', - l2_transaction_hash: '0x436d1c7ada270466ca0facdb96ecc22934d68d13b8a08f541b8df11b222967b5', - symbol: 'ETH', - timestamp: '2023-06-01T14:46:48.000000Z', - value: '0.13040262', -}; - -export const ZKEVM_WITHDRAWALS_ITEM: ZkEvmL2WithdrawalsItem = { - block_number: 11692968, - index: 47003, - l1_transaction_hash: '0x230cf46dabea287ac7d0ba83b8ea120bb83c1de58a81d34f44788f0459096c52', - l2_transaction_hash: '0x519d9f025ec47f08a48d708964d177189d2246ddf988686c481f5debcf097e34', - symbol: 'ETH', - timestamp: '2024-04-17T08:51:58.000000Z', - value: '110.35', -}; - -export const ZKEVM_L2_TXN_BATCHES_ITEM: ZkEvmL2TxnBatchesItem = { - timestamp: '2023-06-01T14:46:48.000000Z', - status: 'Finalized', - verify_transaction_hash: TX_HASH, - sequence_transaction_hash: TX_HASH, - number: 5218590, - transactions_count: 9, -}; - -export const ZKEVM_L2_TXN_BATCH: ZkEvmL2TxnBatch = { - acc_input_hash: '0xb815fe2832977f1324ad0124a019b938f189f7b470292f40a21284f15774b3b3', - global_exit_root: '0x0000000000000000000000000000000000000000000000000000000000000000', - number: 1, - sequence_transaction_hash: '0x57b9b95db5f94f125710bdc8fbb3fabaac10125b44b0cb61dbc69daddf06d0cd', - state_root: '0xb9a589d6b3ae44d3b250a9993caa5e3721568197f56e4743989ecb2285d80ec4', - status: 'Finalized', - timestamp: '2023-09-15T06:22:48.000000Z', - transactions: [ '0xff99dd67646b8f3d657cc6f19eb33abc346de2dbaccd03e45e7726cc28e3e186' ], - verify_transaction_hash: '0x093276fa65c67d7b12dd96f4fefafba9d9ad2f1c23c6e53f96583971ce75352d', -}; diff --git a/stubs/zkSyncL2.ts b/stubs/zkSyncL2.ts index 041bf29e38..2230f1118b 100644 --- a/stubs/zkSyncL2.ts +++ b/stubs/zkSyncL2.ts @@ -1,6 +1,6 @@ import type { ZkSyncBatch, ZkSyncBatchesItem } from 'types/api/zkSyncL2'; -import { TX_HASH } from './tx'; +import { TX_HASH } from 'client/slices/tx/stubs/tx'; export const ZKSYNC_L2_TXN_BATCHES_ITEM: ZkSyncBatchesItem = { commit_transaction_hash: TX_HASH, diff --git a/toolkit/chakra/avatar.tsx b/toolkit/chakra/avatar.tsx index 9d323b2e8d..2b98e0a417 100644 --- a/toolkit/chakra/avatar.tsx +++ b/toolkit/chakra/avatar.tsx @@ -42,8 +42,8 @@ const AvatarFallback = React.forwardRef( return ( { children } - { name != null && children == null && <>{ getInitials(name) } } - { name == null && children == null && ( + { name !== undefined && children === undefined && <>{ getInitials(name) } } + { name === undefined && children === undefined && ( { icon } ) } @@ -53,7 +53,7 @@ const AvatarFallback = React.forwardRef( function getInitials(name: string) { const names = name.trim().split(' '); - const firstName = names[0] != null ? names[0] : ''; + const firstName = names[0] !== undefined ? names[0] : ''; const lastName = names.length > 1 ? names[names.length - 1] : ''; return firstName && lastName ? `${ firstName.charAt(0) }${ lastName.charAt(0) }` : diff --git a/toolkit/chakra/checkbox.tsx b/toolkit/chakra/checkbox.tsx index ac62594e7e..775dc34688 100644 --- a/toolkit/chakra/checkbox.tsx +++ b/toolkit/chakra/checkbox.tsx @@ -17,7 +17,7 @@ export const Checkbox = React.forwardRef( { icon || } - { children != null && ( + { children !== undefined && children !== null && ( { children } ) } diff --git a/toolkit/chakra/empty-state.tsx b/toolkit/chakra/empty-state.tsx index 9b50d2eb83..94bd4fc71a 100644 --- a/toolkit/chakra/empty-state.tsx +++ b/toolkit/chakra/empty-state.tsx @@ -21,11 +21,12 @@ export interface EmptyStateProps extends ChakraEmptyState.RootProps { term?: string; type?: EmptyStateType; icon?: React.ReactNode; + noIcon?: boolean; } export const EmptyState = React.forwardRef( function EmptyState(props, ref) { - const { title, description, term, type = 'query', icon, children, ...rest } = props; + const { title, description, term, type = 'query', icon, children, noIcon, ...rest } = props; const titleContent = (() => { if (title) { @@ -62,6 +63,10 @@ export const EmptyState = React.forwardRef( })(); const iconContent = (() => { + if (noIcon) { + return null; + } + const Icon = ICONS[type]; if (Icon) { return ; diff --git a/toolkit/chakra/field.tsx b/toolkit/chakra/field.tsx index 4b6c66d0d4..cbbed0f511 100644 --- a/toolkit/chakra/field.tsx +++ b/toolkit/chakra/field.tsx @@ -1,4 +1,4 @@ -import { Field as ChakraField } from '@chakra-ui/react'; +import { Field as ChakraField, chakra } from '@chakra-ui/react'; import * as React from 'react'; import getComponentDisplayName from '../utils/getComponentDisplayName'; @@ -33,7 +33,9 @@ export const Field = React.forwardRef( const labelElement = ( - { label } + + { label } + { errorText && ( -{ space }{ errorText } diff --git a/toolkit/chakra/input-group.tsx b/toolkit/chakra/input-group.tsx index 96a45a9fd4..c0e7b6bb3a 100644 --- a/toolkit/chakra/input-group.tsx +++ b/toolkit/chakra/input-group.tsx @@ -97,7 +97,20 @@ export const InputGroup = React.forwardRef( }, [ ref ]); return ( - + { startElement && ( { startElement } diff --git a/toolkit/chakra/link.tsx b/toolkit/chakra/link.tsx index 31a9f77dd1..936cf4f454 100644 --- a/toolkit/chakra/link.tsx +++ b/toolkit/chakra/link.tsx @@ -4,8 +4,9 @@ import NextLink from 'next/link'; import type { LinkProps as NextLinkProps } from 'next/link'; import React from 'react'; +import stripUtmParams from 'client/shared/links/utils/strip-utm-params'; + import ArrowIcon from 'icons/link_external.svg'; -import stripUtmParams from 'lib/utils/stripUtmParams'; import { Skeleton } from './skeleton'; diff --git a/toolkit/chakra/select.tsx b/toolkit/chakra/select.tsx index 113c859718..f0a908f6d9 100644 --- a/toolkit/chakra/select.tsx +++ b/toolkit/chakra/select.tsx @@ -14,6 +14,8 @@ import { Skeleton } from './skeleton'; export type ViewMode = 'default' | 'compact'; +export type OnValueChangeHandler = (details: { value: Array }) => void; + export interface SelectOption { label: string; renderLabel?: (place: 'item' | 'value-text') => React.ReactNode; @@ -122,19 +124,32 @@ interface SelectValueTextProps extends Omit(function SelectValueText(props, ref) { - const { children, size, required, invalid, errorText, mode, ...rest } = props; + const { children, size, required, invalid, errorText, mode, multipleConfig, ...rest } = props; const context = useSelectContext(); const content = (() => { const items = context.selectedItems; const placeholder = `${ props.placeholder }${ required ? '*' : '' }${ invalid && errorText ? ` - ${ errorText }` : '' }`; + const label = size === 'lg' ? ( + + { placeholder } + + ) : null; if (items.length === 0) return placeholder; @@ -145,16 +160,6 @@ export const SelectValueText = React.forwardRef< if (!item) return placeholder; - const label = size === 'lg' ? ( - - { placeholder } - - ) : null; - return ( <> { label } @@ -174,8 +179,21 @@ export const SelectValueText = React.forwardRef< ); } - // FIXME: we don't have multiple selection in the select yet - return `${ items.length } selected`; + return ( + <> + { label } + + { multipleConfig?.icon } + + { `${ items.length } ${ multipleConfig?.term ?? 'selected' }` } + + + + ); })(); return ( @@ -242,20 +260,35 @@ export interface SelectProps extends SelectRootProps { loading?: boolean; errorText?: string; contentProps?: SelectContentProps; + controlProps?: SelectControlProps; contentHeader?: React.ReactNode; + multipleConfig?: SelectValueTextProps['multipleConfig']; itemFilter?: (item: SelectOption) => boolean; mode?: ViewMode; } export const Select = React.forwardRef((props, ref) => { - const { collection, placeholder, portalled = true, loading, errorText, contentProps, contentHeader, itemFilter, mode, ...rest } = props; + const { + collection, + placeholder, + portalled = true, + loading, + errorText, + contentProps, + controlProps, + contentHeader, + itemFilter, + mode, + multipleConfig, + ...rest + } = props; return ( - + ((props, ref) invalid={ props.invalid } errorText={ errorText } mode={ mode } + multipleConfig={ multipleConfig } /> diff --git a/toolkit/chakra/status.tsx b/toolkit/chakra/status.tsx new file mode 100644 index 0000000000..d560a90f20 --- /dev/null +++ b/toolkit/chakra/status.tsx @@ -0,0 +1,12 @@ +import { Status as ChakraStatus } from '@chakra-ui/react'; +import * as React from 'react'; + +export interface StatusProps extends ChakraStatus.RootProps {} + +export const Status = React.forwardRef( + function Status(props, ref) { + return ( + + ); + }, +); diff --git a/toolkit/chakra/switch.tsx b/toolkit/chakra/switch.tsx index ea48c54bb5..c500ed9be2 100644 --- a/toolkit/chakra/switch.tsx +++ b/toolkit/chakra/switch.tsx @@ -31,7 +31,7 @@ export const Switch = React.forwardRef( ) } - { children != null && ( + { children !== undefined && children !== null && ( { children } ) } diff --git a/toolkit/chakra/tooltip.tsx b/toolkit/chakra/tooltip.tsx index 1eb8711524..0d39c99722 100644 --- a/toolkit/chakra/tooltip.tsx +++ b/toolkit/chakra/tooltip.tsx @@ -2,8 +2,9 @@ import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react'; import { useClickAway } from '@uidotdev/usehooks'; import * as React from 'react'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; export interface TooltipProps extends ChakraTooltip.RootProps { selected?: boolean; diff --git a/toolkit/components/AdaptiveTabs/AdaptiveTabs.tsx b/toolkit/components/AdaptiveTabs/AdaptiveTabs.tsx index 62815f1a04..2715dd97ab 100644 --- a/toolkit/components/AdaptiveTabs/AdaptiveTabs.tsx +++ b/toolkit/components/AdaptiveTabs/AdaptiveTabs.tsx @@ -15,6 +15,7 @@ const AdaptiveTabs = (props: Props) => { defaultValue, isLoading, listProps, + listItemProps, rightSlot, rightSlotProps, leftSlot, @@ -60,6 +61,7 @@ const AdaptiveTabs = (props: Props) => { key={ isLoading + '_' + viewportSize.width + '_' + tabs.map((tab) => tab.id).join(':') } tabs={ tabs } listProps={ listProps } + listItemProps={ listItemProps } leftSlot={ leftSlot } leftSlotProps={ leftSlotProps } rightSlot={ rightSlot } diff --git a/toolkit/components/AdaptiveTabs/AdaptiveTabsList.tsx b/toolkit/components/AdaptiveTabs/AdaptiveTabsList.tsx index 583f5458c2..5d855ceb9a 100644 --- a/toolkit/components/AdaptiveTabs/AdaptiveTabsList.tsx +++ b/toolkit/components/AdaptiveTabs/AdaptiveTabsList.tsx @@ -4,10 +4,10 @@ import React from 'react'; import type { TabItemRegular } from './types'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; import { Skeleton } from '../../chakra/skeleton'; -import type { TabsProps } from '../../chakra/tabs'; +import type { TabsProps, TabsTriggerProps } from '../../chakra/tabs'; import { TabsCounter, TabsList, TabsTrigger } from '../../chakra/tabs'; import { useIsSticky } from '../../hooks/useIsSticky'; import AdaptiveTabsMenu from './AdaptiveTabsMenu'; @@ -22,6 +22,8 @@ export interface SlotProps extends HTMLChakraProps<'div'> { export interface BaseProps { tabs: Array; listProps?: HTMLChakraProps<'div'> | (({ isSticky, activeTab }: { isSticky: boolean; activeTab: string }) => HTMLChakraProps<'div'>); + listItemProps?: Partial | + (({ index, isActive, value }: { index: number; isActive: boolean; value: string }) => Partial | undefined); rightSlot?: React.ReactNode; rightSlotProps?: SlotProps; leftSlot?: React.ReactNode; @@ -64,6 +66,7 @@ const AdaptiveTabsList = (props: Props) => { tabs, activeTab, listProps, + listItemProps, rightSlot, rightSlotProps, leftSlot, @@ -161,6 +164,7 @@ const AdaptiveTabsList = (props: Props) => { scrollSnapAlign="start" flexShrink={ 0 } { ...getItemStyles(index, tabsCut, isLoading) } + { ...(typeof listItemProps === 'function' ? listItemProps({ index, isActive: activeTabIndex === index, value }) : listItemProps) } > { typeof tab.title === 'function' ? tab.title() : tab.title } diff --git a/toolkit/components/charts/ChartFullscreenDialog.tsx b/toolkit/components/charts/ChartFullscreenDialog.tsx deleted file mode 100644 index 8a2444ad2c..0000000000 --- a/toolkit/components/charts/ChartFullscreenDialog.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Grid, Icon, Text } from '@chakra-ui/react'; -import React from 'react'; - -import type { Resolution, TimeChartData } from './types'; - -import RepeatIcon from 'icons/repeat.svg'; -import { Heading } from 'toolkit/chakra/heading'; - -import { Button } from '../../chakra/button'; -import { DialogBody, DialogContent, DialogHeader, DialogRoot } from '../../chakra/dialog'; -import { ChartWidgetContent } from './ChartWidgetContent'; - -export interface Props { - open: boolean; - onOpenChange: ({ open }: { open: boolean }) => void; - title: string; - description?: string; - charts: TimeChartData; - resolution?: Resolution; - zoomRange?: [ Date, Date ]; - handleZoom: (range: [ Date, Date ]) => void; - handleZoomReset: () => void; -}; - -const FullscreenChartModal = ({ - charts, - open, - onOpenChange, - title, - description, - resolution, - zoomRange, - handleZoom, - handleZoomReset, -}: Props) => { - return ( - - - - - - - { title } - - - { description && ( - - { description } - - ) } - - { Boolean(zoomRange) && ( - - ) } - - - - - - ); -}; - -export default FullscreenChartModal; diff --git a/toolkit/components/charts/ChartWidget.tsx b/toolkit/components/charts/ChartWidget.tsx deleted file mode 100644 index b4f4500418..0000000000 --- a/toolkit/components/charts/ChartWidget.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import type { FlexProps } from '@chakra-ui/react'; -import { Flex, Icon } from '@chakra-ui/react'; -import { range } from 'es-toolkit'; -import React, { useRef } from 'react'; - -import type { AxesConfigFn, TimeChartData } from './types'; - -import RepeatIcon from 'icons/repeat.svg'; - -import { IconButton } from '../../chakra/icon-button'; -import { Link } from '../../chakra/link'; -import { Skeleton } from '../../chakra/skeleton'; -import { Tooltip } from '../../chakra/tooltip'; -import { ChartWidgetContent } from './ChartWidgetContent'; -import { ChartLegend } from './parts/ChartLegend'; -import type { ChartMenuItemId } from './parts/ChartMenu'; -import ChartMenu from './parts/ChartMenu'; -import { useChartZoom } from './utils/useChartZoom'; - -export interface ChartWidgetProps extends FlexProps { - charts: TimeChartData; - title: string; - description?: string; - isLoading: boolean; - isError: boolean; - emptyText?: string; - noAnimation?: boolean; - href?: string; - chartUrl?: string; - axesConfig?: AxesConfigFn; - menuItemIds?: Array; - noWatermark?: boolean; -}; - -export const ChartWidget = React.memo(({ - charts, - title, - description, - isLoading, - isError, - emptyText, - noAnimation, - href, - chartUrl, - axesConfig, - menuItemIds, - noWatermark, - ...rest -}: ChartWidgetProps) => { - const ref = useRef(null); - - const { zoomRange, handleZoom, handleZoomReset } = useChartZoom(); - - const [ selectedCharts, setSelectedCharts ] = React.useState>( - range(charts.length), - ); - - React.useEffect(() => { - if (charts.length > 0) { - setSelectedCharts(range(charts.length)); - } - }, [ charts.length ]); - - const handleLegendItemClick = React.useCallback((index: number) => { - setSelectedCharts((prev) => { - if (prev.includes(index)) { - return prev.filter((item) => item !== index); - } - return [ ...prev, index ]; - }); - }, []); - - const displayedCharts = React.useMemo(() => { - return charts.filter((_, index) => selectedCharts.includes(index)); - }, [ charts, selectedCharts ]); - - const hasNonEmptyCharts = charts.some(({ items }) => items && items.length > 2); - const hasMenu = (() => { - const hasIds = !(menuItemIds && menuItemIds.length === 0); - if (!hasIds) { - return false; - } - if (isError) { - return false; - } - if (!hasNonEmptyCharts) { - return false; - } - return true; - })(); - - const content = ( - - ); - - const chartHeader = ( - - - { title } - - - { description && ( - - { description } - - ) } - - ); - - return ( - - - { href ? ( - - { chartHeader } - - ) : chartHeader } - - - - - - { hasMenu && ( - - ) } - - - - { content } - - { charts.length > 1 && ( - - ) } - - ); -}); diff --git a/toolkit/components/charts/ChartWidgetContent.tsx b/toolkit/components/charts/ChartWidgetContent.tsx deleted file mode 100644 index 7ca08e32cf..0000000000 --- a/toolkit/components/charts/ChartWidgetContent.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { Box, Center, Flex, Text } from '@chakra-ui/react'; -import React from 'react'; - -import type { AxesConfigFn, Resolution, TimeChartData } from './types'; - -import { Link } from '../../chakra/link'; -import { Skeleton } from '../../chakra/skeleton'; -import { apos } from '../../utils/htmlEntities'; -import { Chart } from './Chart'; -import { ChartWatermark } from './parts/ChartWatermark'; - -export interface ChartWidgetContentProps { - charts: TimeChartData; - isLoading?: boolean; - isError?: boolean; - empty?: boolean; - emptyText?: string; - zoomRange?: [ Date, Date ]; - handleZoom: (range: [ Date, Date ]) => void; - isEnlarged?: boolean; - noAnimation?: boolean; - resolution?: Resolution; - axesConfig?: AxesConfigFn; - noWatermark?: boolean; -}; - -export const ChartWidgetContent = React.memo(({ - charts, - isLoading, - isError, - empty, - emptyText, - zoomRange, - handleZoom, - isEnlarged, - noAnimation, - resolution, - axesConfig, - noWatermark, -}: ChartWidgetContentProps) => { - if (isError) { - return ( - - - { `The data didn${ apos }t load. Please, ` } - try to reload the page. - - - ); - } - - if (isLoading) { - return ; - } - - if (empty || charts.length === 0) { - return ( -

- { emptyText || 'No data' } -
- ); - } - - return ( - - - { !noWatermark && } - - ); -}); diff --git a/toolkit/components/charts/components/ChartContent.tsx b/toolkit/components/charts/components/ChartContent.tsx new file mode 100644 index 0000000000..6a635bc962 --- /dev/null +++ b/toolkit/components/charts/components/ChartContent.tsx @@ -0,0 +1,68 @@ +import { Box, Center, Flex, Text } from '@chakra-ui/react'; +import React from 'react'; + +import { EmptyState } from '../../../chakra/empty-state'; +import { Link } from '../../../chakra/link'; +import { Skeleton } from '../../../chakra/skeleton'; +import { apos } from '../../../utils/htmlEntities'; +import { ChartWatermark } from './ChartWatermark'; + +export interface ChartContentProps { + isError?: boolean; + isLoading?: boolean; + isEmpty?: boolean; + isFiltered?: boolean; + emptyText?: string; + noEmptyStateIcon?: boolean; + noWatermark?: boolean; + children: React.ReactNode; +} + +export const ChartContent = ({ isError, isLoading, isEmpty, isFiltered, noEmptyStateIcon, emptyText, noWatermark, children }: ChartContentProps) => { + if (isError) { + return ( + + + { `The data didn${ apos }t load. Please, ` } + try to reload the page. + + + ); + } + + if (isLoading) { + return ; + } + + if (isEmpty) { + if (isFiltered) { + return ( +
+ No charts selected +
+ ); + } + + return ( +
+ +
+ ); + } + + return ( + + { children } + { !noWatermark && } + + ); +}; diff --git a/toolkit/components/charts/components/ChartDialog.tsx b/toolkit/components/charts/components/ChartDialog.tsx new file mode 100644 index 0000000000..0c6c8af28f --- /dev/null +++ b/toolkit/components/charts/components/ChartDialog.tsx @@ -0,0 +1,47 @@ +import { Flex, Text } from '@chakra-ui/react'; +import React from 'react'; + +import type { OnOpenChangeHandler } from 'toolkit/hooks/useDisclosure'; + +import { DialogBody, DialogContent, DialogHeader, DialogRoot } from '../../../chakra/dialog'; +import { Heading } from '../../../chakra/heading'; + +export interface ChartDialogProps { + open: boolean; + onOpenChange: OnOpenChangeHandler; + title: string; + description?: string; + headerRightSlot?: React.ReactNode; + children: React.ReactNode; +} + +export const ChartDialog = ({ open, onOpenChange, title, description, headerRightSlot, children }: ChartDialogProps) => { + return ( + + + + + + + + { title } + + { description && ( + + { description } + + ) } + + { headerRightSlot } + + { children } + + + + ); +}; diff --git a/toolkit/components/charts/components/ChartMenu.tsx b/toolkit/components/charts/components/ChartMenu.tsx new file mode 100644 index 0000000000..db97f637a9 --- /dev/null +++ b/toolkit/components/charts/components/ChartMenu.tsx @@ -0,0 +1,163 @@ +import { Icon } from '@chakra-ui/react'; +import { useCopyToClipboard } from '@uidotdev/usehooks'; +import domToImage from 'dom-to-image'; +import React from 'react'; + +import CopyIcon from 'icons/copy.svg'; +import DotsIcon from 'icons/dots.svg'; +import CsvIcon from 'icons/files/csv.svg'; +import ImageIcon from 'icons/files/image.svg'; +import ScopeIcon from 'icons/scope.svg'; +import ShareIcon from 'icons/share.svg'; +import { useMultichainContext } from 'lib/contexts/multichain'; + +import { useColorModeValue } from '../../../chakra/color-mode'; +import { IconButton } from '../../../chakra/icon-button'; +import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '../../../chakra/menu'; +import { saveAsCsv } from '../../../utils/file'; +import { isBrowser } from '../../../utils/isBrowser'; + +export const CHART_MENU_ITEMS_IDS = [ + 'share' as const, + 'fullscreen' as const, + 'save_png' as const, + 'save_csv' as const, +]; + +const DOWNLOAD_IMAGE_SCALE = 5; + +export type ChartMenuItemId = (typeof CHART_MENU_ITEMS_IDS)[number]; + +export interface ChartMenuProps { + title: string; + description?: string; + items?: Array; + csvData: Array>; + chartUrl?: string; + isLoading?: boolean; + chartRef: React.RefObject; + onShare?: () => void; + onFullscreenOpen?: () => void; +} + +export const ChartMenu = ({ + title, + description, + items = CHART_MENU_ITEMS_IDS, + csvData, + chartUrl, + isLoading, + chartRef, + onShare, + onFullscreenOpen, +}: ChartMenuProps) => { + + const [ , copyToClipboard ] = useCopyToClipboard(); + const pngBackgroundColor = useColorModeValue('white', 'black'); + const multichainContext = useMultichainContext(); + + const chainPostfix = React.useMemo(() => { + return multichainContext?.chain.name ? ` on ${ multichainContext.chain.name }` : ''; + }, [ multichainContext?.chain.name ]); + + // TS thinks window.navigator.share can't be undefined, but it can + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const canShare = isBrowser() && (window.navigator.share as any); + + const handleCopy = React.useCallback(() => { + copyToClipboard(chartUrl ?? ''); + onShare?.(); + }, [ chartUrl, copyToClipboard, onShare ]); + + const handleShare = React.useCallback(async() => { + try { + await window.navigator.share({ + title: title, + text: description, + url: chartUrl, + }); + onShare?.(); + } catch (error) {} + }, [ title, description, chartUrl, onShare ]); + + const handleSavePngClick = React.useCallback(() => { + // wait for context menu to close + setTimeout(() => { + if (chartRef.current) { + domToImage.toPng(chartRef.current, + { + quality: 100, + bgcolor: pngBackgroundColor, + width: chartRef.current.offsetWidth * DOWNLOAD_IMAGE_SCALE, + height: chartRef.current.offsetHeight * DOWNLOAD_IMAGE_SCALE, + filter: (node) => node.nodeName !== 'BUTTON', + style: { + borderColor: 'transparent', + transform: `scale(${ DOWNLOAD_IMAGE_SCALE })`, + 'transform-origin': 'top left', + }, + }) + .then((dataUrl) => { + const link = document.createElement('a'); + link.download = `${ title }${ chainPostfix } (Blockscout chart).png`; + link.href = dataUrl; + link.click(); + link.remove(); + }); + } + }, 100); + }, [ pngBackgroundColor, title, chainPostfix, chartRef ]); + + const handleSaveCsvClick = React.useCallback(() => { + saveAsCsv(csvData, `${ title }${ chainPostfix } (Blockscout stats)`); + }, [ csvData, title, chainPostfix ]); + + return ( + + + + + + + + { items.includes('share') && chartUrl && ( + + { canShare ? : } + { canShare ? 'Share' : 'Copy link' } + + ) } + { items.includes('fullscreen') && onFullscreenOpen && ( + + + View fullscreen + + ) } + { items.includes('save_png') && ( + + + Save as PNG + + ) } + { items.includes('save_csv') && csvData && ( + + + Save as CSV + + ) } + + + ); +}; diff --git a/toolkit/components/charts/components/ChartResetZoomButton.tsx b/toolkit/components/charts/components/ChartResetZoomButton.tsx new file mode 100644 index 0000000000..a3548e444d --- /dev/null +++ b/toolkit/components/charts/components/ChartResetZoomButton.tsx @@ -0,0 +1,21 @@ +import { Icon } from '@chakra-ui/react'; + +import RepeatIcon from 'icons/repeat.svg'; + +import type { IconButtonProps } from '../../../chakra/icon-button'; +import { IconButton } from '../../../chakra/icon-button'; +import { Tooltip } from '../../../chakra/tooltip'; + +export interface ChartResetZoomButtonProps extends IconButtonProps { + range?: unknown; +} + +export const ChartResetZoomButton = ({ range, ...props }: ChartResetZoomButtonProps) => { + return ( + + + + ); +}; diff --git a/toolkit/components/charts/parts/ChartWatermark.tsx b/toolkit/components/charts/components/ChartWatermark.tsx similarity index 100% rename from toolkit/components/charts/parts/ChartWatermark.tsx rename to toolkit/components/charts/components/ChartWatermark.tsx diff --git a/toolkit/components/charts/components/ChartWidget.tsx b/toolkit/components/charts/components/ChartWidget.tsx new file mode 100644 index 0000000000..743c6e4b8f --- /dev/null +++ b/toolkit/components/charts/components/ChartWidget.tsx @@ -0,0 +1,70 @@ +import type { FlexProps } from '@chakra-ui/react'; +import { Flex } from '@chakra-ui/react'; +import React from 'react'; + +import { Link } from '../../../chakra/link'; +import { Skeleton } from '../../../chakra/skeleton'; + +export interface ChartWidgetRootProps extends FlexProps {} + +export const ChartWidgetRoot = React.forwardRef(({ children, ...rest }, ref) => { + return ( + + { children } + + ); +}); + +export interface ChartWidgetHeaderProps extends FlexProps { + title: string; + description?: string; + href?: string; + isLoading?: boolean; +} + +export const ChartWidgetHeader = ({ title, description, href, isLoading, ...rest }: ChartWidgetHeaderProps) => { + + const content = ( + + + { title } + + { description && ( + + { description } + + ) } + + ); + + if (href) { + return { content }; + } + + return content; +}; diff --git a/toolkit/components/charts/components/index.ts b/toolkit/components/charts/components/index.ts new file mode 100644 index 0000000000..5477488159 --- /dev/null +++ b/toolkit/components/charts/components/index.ts @@ -0,0 +1,6 @@ +export * from './ChartWatermark'; +export * from './ChartMenu'; +export * from './ChartWidget'; +export * from './ChartContent'; +export * from './ChartDialog'; +export * from './ChartResetZoomButton'; diff --git a/toolkit/components/charts/index.ts b/toolkit/components/charts/index.ts index 9243a0e685..fd3d189797 100644 --- a/toolkit/components/charts/index.ts +++ b/toolkit/components/charts/index.ts @@ -1,20 +1,4 @@ -export * from './Chart'; -export * from './ChartWidget'; -export * from './ChartWidgetContent'; -export * from './parts/ChartArea'; -export * from './parts/ChartAxis'; -export * from './parts/ChartGridLine'; -export * from './parts/ChartLegend'; -export * from './parts/ChartLine'; -export * from './parts/ChartMenu'; -export * from './parts/ChartOverlay'; -export * from './parts/ChartSelectionX'; -export * from './parts/ChartTooltip'; -export * from './parts/ChartWatermark'; - -export * from './utils/useChartBrushX'; -export * from './utils/useChartZoom'; -export * from './utils/useChartLegend'; -export * from './utils/useTimeChartController'; - export * from './types'; +export * from './components'; +export * from './line'; +export * from './sankey'; diff --git a/toolkit/components/charts/Chart.tsx b/toolkit/components/charts/line/LineChart.tsx similarity index 76% rename from toolkit/components/charts/Chart.tsx rename to toolkit/components/charts/line/LineChart.tsx index cf37d14e17..4d685579a9 100644 --- a/toolkit/components/charts/Chart.tsx +++ b/toolkit/components/charts/line/LineChart.tsx @@ -1,35 +1,36 @@ import { defaultsDeep } from 'es-toolkit/compat'; import React from 'react'; -import type { AxesConfigFn, ChartMargin, TimeChartData } from './types'; -import { Resolution } from './types'; - -import useIsMobile from 'lib/hooks/useIsMobile'; - -import { ChartArea } from './parts/ChartArea'; -import { ChartAxis } from './parts/ChartAxis'; -import { ChartGridLine } from './parts/ChartGridLine'; -import { ChartLine } from './parts/ChartLine'; -import { ChartOverlay } from './parts/ChartOverlay'; -import { ChartSelectionX } from './parts/ChartSelectionX'; -import { ChartTooltip } from './parts/ChartTooltip'; +import type { ChartMargin } from '../types'; +import { ChartResolution } from '../types'; +import type { LineChartAxesConfigFn, LineChartData } from './types'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + +import { LineChartArea } from './parts/LineChartArea'; +import { LineChartAxis } from './parts/LineChartAxis'; +import { LineChartGridLine } from './parts/LineChartGridLine'; +import { LineChartLine } from './parts/LineChartLine'; +import { LineChartOverlay } from './parts/LineChartOverlay'; +import { LineChartSelectionX } from './parts/LineChartSelectionX'; +import { LineChartTooltip } from './parts/LineChartTooltip'; import { getDateLabel } from './utils/getDateLabel'; -import { useTimeChartController } from './utils/useTimeChartController'; +import { useLineChartController } from './utils/useLineChartController'; -export interface ChartProps { - charts: TimeChartData; +export interface LineChartProps { + charts: LineChartData; zoomRange?: [ Date, Date ]; onZoom: (range: [ Date, Date ]) => void; margin?: ChartMargin; noAnimation?: boolean; - resolution?: Resolution; - axesConfig?: AxesConfigFn; + resolution?: ChartResolution; + axesConfig?: LineChartAxesConfigFn; isEnlarged?: boolean; } const DEFAULT_CHART_MARGIN = { bottom: 20, left: 10, right: 20, top: 10 }; -export const Chart = React.memo(({ +export const LineChart = React.memo(({ isEnlarged, charts, onZoom, @@ -38,17 +39,17 @@ export const Chart = React.memo(({ resolution, zoomRange, axesConfig: axesConfigProps, -}: ChartProps) => { +}: LineChartProps) => { const isMobile = useIsMobile(); const overlayRef = React.useRef(null); const range = React.useMemo( () => - zoomRange || [ + zoomRange || (charts[0] ? [ charts[0].items[0].date, charts[0].items[charts[0].items.length - 1].date, - ], + ] : []), [ zoomRange, charts ], ); @@ -85,7 +86,7 @@ export const Chart = React.memo(({ innerHeight, chartMargin, axes, - } = useTimeChartController({ + } = useLineChartController({ data: displayedCharts, margin, axesConfig, @@ -95,7 +96,7 @@ export const Chart = React.memo(({ - - - - + - - + ); diff --git a/toolkit/components/charts/line/LineChartContent.tsx b/toolkit/components/charts/line/LineChartContent.tsx new file mode 100644 index 0000000000..44b5e3a173 --- /dev/null +++ b/toolkit/components/charts/line/LineChartContent.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import type { ChartContentProps } from '../components/ChartContent'; +import { ChartContent } from '../components/ChartContent'; +import type { LineChartProps } from './LineChart'; +import { LineChart } from './LineChart'; + +export interface LineChartContentProps extends Omit, LineChartProps {}; + +export const LineChartContent = React.memo(({ + isLoading, + isError, + isEmpty, + isFiltered, + emptyText, + noWatermark, + noEmptyStateIcon, + ...rest +}: LineChartContentProps) => { + return ( + + + + ); +}); diff --git a/toolkit/components/charts/line/LineChartModal.tsx b/toolkit/components/charts/line/LineChartModal.tsx new file mode 100644 index 0000000000..b3f55ea649 --- /dev/null +++ b/toolkit/components/charts/line/LineChartModal.tsx @@ -0,0 +1,58 @@ +import React from 'react'; + +import type { ChartResolution } from '../types'; +import type { LineChartData } from './types'; + +import type { ChartDialogProps } from '../components/ChartDialog'; +import { ChartDialog } from '../components/ChartDialog'; +import { ChartResetZoomButton } from '../components/ChartResetZoomButton'; +import { LineChartContent } from './LineChartContent'; + +interface Props extends Omit { + charts: LineChartData; + resolution?: ChartResolution; + zoomRange?: [ Date, Date ]; + handleZoom: (range: [ Date, Date ]) => void; + handleZoomReset: () => void; +}; + +const LineChartModal = ({ + charts, + open, + onOpenChange, + title, + description, + resolution, + zoomRange, + handleZoom, + handleZoomReset, +}: Props) => { + + const headerRightSlot = ( + + ); + + return ( + + + + ); +}; + +export default LineChartModal; diff --git a/toolkit/components/charts/line/LineChartWidget.tsx b/toolkit/components/charts/line/LineChartWidget.tsx new file mode 100644 index 0000000000..4d39af1f31 --- /dev/null +++ b/toolkit/components/charts/line/LineChartWidget.tsx @@ -0,0 +1,137 @@ +import type { FlexProps } from '@chakra-ui/react'; +import { Flex } from '@chakra-ui/react'; +import { range } from 'es-toolkit'; +import React, { useRef } from 'react'; + +import type { LineChartAxesConfigFn, LineChartData } from './types'; + +import type { ChartMenuItemId } from '../components/ChartMenu'; +import { ChartResetZoomButton } from '../components/ChartResetZoomButton'; +import { ChartWidgetRoot, ChartWidgetHeader } from '../components/ChartWidget'; +import { LineChartContent } from './LineChartContent'; +import { LineChartLegend } from './parts/LineChartLegend'; +import LineChartMenu from './parts/LineChartMenu'; +import { useLineChartZoom } from './utils/useLineChartZoom'; + +export interface LineChartWidgetProps extends FlexProps { + charts: LineChartData; + title: string; + description?: string; + isLoading: boolean; + isError: boolean; + emptyText?: string; + noAnimation?: boolean; + href?: string; + chartUrl?: string; + axesConfig?: LineChartAxesConfigFn; + menuItems?: Array; + noWatermark?: boolean; + noEmptyStateIcon?: boolean; +}; + +export const LineChartWidget = React.memo(({ + charts, + title, + description, + isLoading, + isError, + emptyText, + noAnimation, + href, + chartUrl, + axesConfig, + menuItems, + noWatermark, + noEmptyStateIcon, + ...rest +}: LineChartWidgetProps) => { + const ref = useRef(null); + + const { zoomRange, handleZoom, handleZoomReset } = useLineChartZoom(); + + const [ selectedCharts, setSelectedCharts ] = React.useState | undefined>( + isLoading ? undefined : range(charts.length), + ); + + React.useEffect(() => { + if (charts.length > 0) { + setSelectedCharts(range(charts.length)); + } + }, [ charts.length ]); + + const handleLegendItemClick = React.useCallback((index: number) => { + setSelectedCharts((prev) => { + if (prev?.includes(index)) { + return prev.filter((item) => item !== index); + } + return [ ...(prev || []), index ]; + }); + }, []); + + const displayedCharts = React.useMemo(() => { + return charts.filter((_, index) => selectedCharts?.includes(index)); + }, [ charts, selectedCharts ]); + + const hasNonEmptyCharts = charts.some(({ items }) => items && items.length > 2); + const hasMenu = (() => { + const hasIds = !(menuItems && menuItems.length === 0); + if (!hasIds) { + return false; + } + if (isError) { + return false; + } + if (!hasNonEmptyCharts) { + return false; + } + return true; + })(); + + return ( + + + + + + { hasMenu && ( + + ) } + + + + + + { charts.length > 1 && ( + + ) } + + ); +}); diff --git a/toolkit/components/charts/line/index.ts b/toolkit/components/charts/line/index.ts new file mode 100644 index 0000000000..3f85c7fe8a --- /dev/null +++ b/toolkit/components/charts/line/index.ts @@ -0,0 +1,19 @@ +export * from './LineChart'; +export * from './LineChartWidget'; +export * from './LineChartContent'; +export * from './parts/LineChartArea'; +export * from './parts/LineChartAxis'; +export * from './parts/LineChartGridLine'; +export * from './parts/LineChartLegend'; +export * from './parts/LineChartLine'; +export * from './parts/LineChartMenu'; +export * from './parts/LineChartOverlay'; +export * from './parts/LineChartSelectionX'; +export * from './parts/LineChartTooltip'; + +export * from './utils/useLineChartBrushX'; +export * from './utils/useLineChartZoom'; +export * from './utils/useLineChartLegend'; +export * from './utils/useLineChartController'; + +export * from './types'; diff --git a/toolkit/components/charts/parts/ChartArea.tsx b/toolkit/components/charts/line/parts/LineChartArea.tsx similarity index 81% rename from toolkit/components/charts/parts/ChartArea.tsx rename to toolkit/components/charts/line/parts/LineChartArea.tsx index 7e1dc08f70..74cfab0028 100644 --- a/toolkit/components/charts/parts/ChartArea.tsx +++ b/toolkit/components/charts/line/parts/LineChartArea.tsx @@ -1,9 +1,9 @@ import * as d3 from 'd3'; import React from 'react'; -import type { TimeChartItem } from '../types'; +import type { LineChartItem } from '../types'; -export interface ChartAreaProps extends React.SVGProps { +export interface LineChartAreaProps extends React.SVGProps { id: string; xScale: d3.ScaleTime | d3.ScaleLinear; yScale: d3.ScaleTime | d3.ScaleLinear; @@ -11,11 +11,11 @@ export interface ChartAreaProps extends React.SVGProps { startColor: string; stopColor: string; }; - data: Array; + data: Array; noAnimation?: boolean; } -export const ChartArea = React.memo(({ id, xScale, yScale, gradient, data, noAnimation, ...props }: ChartAreaProps) => { +export const LineChartArea = React.memo(({ id, xScale, yScale, gradient, data, noAnimation, ...props }: LineChartAreaProps) => { const ref = React.useRef(null); const gradientId = `gradient-chart-area-${ id }`; @@ -32,7 +32,7 @@ export const ChartArea = React.memo(({ id, xScale, yScale, gradient, data, noAni }, [ noAnimation ]); const d = React.useMemo(() => { - const area = d3.area() + const area = d3.area() .defined(({ isApproximate }) => !isApproximate) .x(({ date }) => xScale(date)) .y1(({ value }) => yScale(value)) diff --git a/toolkit/components/charts/parts/ChartAxis.tsx b/toolkit/components/charts/line/parts/LineChartAxis.tsx similarity index 87% rename from toolkit/components/charts/parts/ChartAxis.tsx rename to toolkit/components/charts/line/parts/LineChartAxis.tsx index f762541d48..94012b25b7 100644 --- a/toolkit/components/charts/parts/ChartAxis.tsx +++ b/toolkit/components/charts/line/parts/LineChartAxis.tsx @@ -2,9 +2,9 @@ import { useToken } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -import { useColorModeValue } from '../../../chakra/color-mode'; +import { useColorModeValue } from '../../../../chakra/color-mode'; -export interface ChartAxisProps extends Omit, 'scale'> { +export interface LineChartAxisProps extends Omit, 'scale'> { type: 'left' | 'bottom'; scale: d3.ScaleTime | d3.ScaleLinear; noAnimation?: boolean; @@ -13,7 +13,7 @@ export interface ChartAxisProps extends Omit, 'scale anchorEl?: SVGRectElement | null; } -export const ChartAxis = React.memo(({ type, scale, ticks, tickFormatGenerator, noAnimation, anchorEl, ...props }: ChartAxisProps) => { +export const LineChartAxis = React.memo(({ type, scale, ticks, tickFormatGenerator, noAnimation, anchorEl, ...props }: LineChartAxisProps) => { const ref = React.useRef(null); const textColor = useToken('colors', useColorModeValue('blackAlpha.600', 'whiteAlpha.500')); diff --git a/toolkit/components/charts/parts/ChartGridLine.tsx b/toolkit/components/charts/line/parts/LineChartGridLine.tsx similarity index 82% rename from toolkit/components/charts/parts/ChartGridLine.tsx rename to toolkit/components/charts/line/parts/LineChartGridLine.tsx index 8e0c9b2c4d..07e143640c 100644 --- a/toolkit/components/charts/parts/ChartGridLine.tsx +++ b/toolkit/components/charts/line/parts/LineChartGridLine.tsx @@ -2,7 +2,7 @@ import { useToken } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -export interface ChartGridLineProps extends Omit, 'scale'> { +export interface LineChartGridLineProps extends Omit, 'scale'> { type: 'vertical' | 'horizontal'; scale: d3.ScaleTime | d3.ScaleLinear; noAnimation?: boolean; @@ -10,7 +10,7 @@ export interface ChartGridLineProps extends Omit, 's ticks: number; } -export const ChartGridLine = React.memo(({ type, scale, ticks, size, noAnimation, ...props }: ChartGridLineProps) => { +export const LineChartGridLine = React.memo(({ type, scale, ticks, size, noAnimation, ...props }: LineChartGridLineProps) => { const ref = React.useRef(null); const strokeColor = useToken('colors', 'border.divider'); diff --git a/toolkit/components/charts/parts/ChartLegend.tsx b/toolkit/components/charts/line/parts/LineChartLegend.tsx similarity index 86% rename from toolkit/components/charts/parts/ChartLegend.tsx rename to toolkit/components/charts/line/parts/LineChartLegend.tsx index cf6e3ca865..9ea41a90f6 100644 --- a/toolkit/components/charts/parts/ChartLegend.tsx +++ b/toolkit/components/charts/line/parts/LineChartLegend.tsx @@ -2,15 +2,15 @@ import type { BoxProps } from '@chakra-ui/react'; import { Box, Circle, Text } from '@chakra-ui/react'; import React from 'react'; -import type { TimeChartData } from '../types'; +import type { LineChartData } from '../types'; -export interface ChartLegendProps extends BoxProps { - data: TimeChartData; +export interface LineChartLegendProps extends BoxProps { + data: LineChartData; selectedIndexes?: Array; onItemClick?: (index: number) => void; } -export const ChartLegend = React.memo(({ data, selectedIndexes, onItemClick, ...props }: ChartLegendProps) => { +export const LineChartLegend = React.memo(({ data, selectedIndexes, onItemClick, ...props }: LineChartLegendProps) => { const handleItemClick = React.useCallback( (event: React.MouseEvent) => { const itemIndex = (event.currentTarget as HTMLDivElement).getAttribute( diff --git a/toolkit/components/charts/parts/ChartLine.tsx b/toolkit/components/charts/line/parts/LineChartLine.tsx similarity index 85% rename from toolkit/components/charts/parts/ChartLine.tsx rename to toolkit/components/charts/line/parts/LineChartLine.tsx index da694d1d68..c5f5a59694 100644 --- a/toolkit/components/charts/parts/ChartLine.tsx +++ b/toolkit/components/charts/line/parts/LineChartLine.tsx @@ -1,20 +1,20 @@ import * as d3 from 'd3'; import React from 'react'; -import type { TimeChartItem } from '../types'; +import type { LineChartItem } from '../types'; import type { AnimationType } from '../utils/animations'; import { ANIMATIONS } from '../utils/animations'; import { getIncompleteDataLineSource } from '../utils/formatters'; -export interface ChartLineProps extends React.SVGProps { +export interface LineChartLineProps extends React.SVGProps { xScale: d3.ScaleTime | d3.ScaleLinear; yScale: d3.ScaleTime | d3.ScaleLinear; - data: Array; + data: Array; animation: AnimationType; } -export const ChartLine = React.memo(({ xScale, yScale, data, animation, strokeDasharray, ...props }: ChartLineProps) => { +export const LineChartLine = React.memo(({ xScale, yScale, data, animation, strokeDasharray, ...props }: LineChartLineProps) => { const dataPathRef = React.useRef(null); const incompleteDataPathRef = React.useRef(null); @@ -41,7 +41,7 @@ export const ChartLine = React.memo(({ xScale, yScale, data, animation, strokeDa } }, [ xScale, yScale, animation ]); - const line = d3.line() + const line = d3.line() .x((d) => xScale(d.date)) .y((d) => yScale(d.value)) .curve(d3.curveMonotoneX); diff --git a/toolkit/components/charts/line/parts/LineChartMenu.tsx b/toolkit/components/charts/line/parts/LineChartMenu.tsx new file mode 100644 index 0000000000..a0c2d74950 --- /dev/null +++ b/toolkit/components/charts/line/parts/LineChartMenu.tsx @@ -0,0 +1,79 @@ +import dayjs from 'dayjs'; +import React from 'react'; + +import type { ChartResolution } from '../../types'; +import type { LineChartData } from '../types'; + +import { useDisclosure } from '../../../../hooks/useDisclosure'; +import type { ChartMenuProps } from '../../components/ChartMenu'; +import { ChartMenu } from '../../components/ChartMenu'; +import LineChartModal from '../LineChartModal'; + +export interface Props extends Omit { + charts: LineChartData; + resolution?: ChartResolution; + zoomRange?: [ Date, Date ]; + handleZoom: (range: [ Date, Date ]) => void; + handleZoomReset: () => void; +}; + +const LineChartMenu = ({ + items, + charts, + title, + description, + isLoading, + chartRef, + chartUrl, + resolution, + zoomRange, + handleZoom, + handleZoomReset, + onShare, +}: Props) => { + const modal = useDisclosure(); + + const handleModalOpen = React.useCallback(() => { + modal.onOpenChange({ open: true }); + }, [ modal ]); + + const csvData = React.useMemo(() => { + const headerRows = [ + 'Date', ...charts.map((chart) => chart.name), + ]; + const dataRows = charts[0].items.map((item, index) => [ + item.dateLabel ?? dayjs(item.date).format('YYYY-MM-DD'), + ...charts.map((chart) => String(chart.items[index].value)), + ]); + return [ headerRows, ...dataRows ]; + }, [ charts ]); + + return ( + <> + + + + ); +}; + +export default LineChartMenu; diff --git a/toolkit/components/charts/parts/ChartOverlay.tsx b/toolkit/components/charts/line/parts/LineChartOverlay.tsx similarity index 56% rename from toolkit/components/charts/parts/ChartOverlay.tsx rename to toolkit/components/charts/line/parts/LineChartOverlay.tsx index 6e51212ecb..d0d7ec6338 100644 --- a/toolkit/components/charts/parts/ChartOverlay.tsx +++ b/toolkit/components/charts/line/parts/LineChartOverlay.tsx @@ -1,12 +1,12 @@ import React from 'react'; -export interface ChartOverlayProps { +export interface LineChartOverlayProps { width: number; height: number; children: React.ReactNode; } -export const ChartOverlay = React.forwardRef(({ width, height, children }: ChartOverlayProps, ref: React.ForwardedRef) => { +export const LineChartOverlay = React.forwardRef(({ width, height, children }: LineChartOverlayProps, ref: React.ForwardedRef) => { return ( { children } diff --git a/toolkit/components/charts/parts/ChartSelectionX.tsx b/toolkit/components/charts/line/parts/LineChartSelectionX.tsx similarity index 80% rename from toolkit/components/charts/parts/ChartSelectionX.tsx rename to toolkit/components/charts/line/parts/LineChartSelectionX.tsx index ab63159edf..5209a13035 100644 --- a/toolkit/components/charts/parts/ChartSelectionX.tsx +++ b/toolkit/components/charts/line/parts/LineChartSelectionX.tsx @@ -6,19 +6,22 @@ import React from 'react'; dayjs.extend(minMax); -import type { TimeChartData, TimeChartItem } from '../types'; +import type { LineChartData, LineChartItem } from '../types'; const SELECTION_THRESHOLD = 2; +const CLASS_NAME_RECT = 'LineChartSelectionX__rect'; +const CLASS_NAME_LINE_LEFT = 'LineChartSelectionX__line_left'; +const CLASS_NAME_LINE_RIGHT = 'LineChartSelectionX__line_right'; -export interface ChartSelectionXProps { +export interface LineChartSelectionXProps { height: number; anchorEl?: SVGRectElement | null; scale: d3.ScaleTime; - data: TimeChartData; + data: LineChartData; onSelect: (range: [Date, Date]) => void; } -export const ChartSelectionX = React.memo(({ anchorEl, height, scale, data, onSelect }: ChartSelectionXProps) => { +export const LineChartSelectionX = React.memo(({ anchorEl, height, scale, data, onSelect }: LineChartSelectionXProps) => { const [ borderColor ] = useToken('colors', 'blue.200'); const ref = React.useRef(null); @@ -28,7 +31,7 @@ export const ChartSelectionX = React.memo(({ anchorEl, height, scale, data, onSe const getIndexByX = React.useCallback((x: number) => { const xDate = scale.invert(x); - const bisectDate = d3.bisector((d) => d.date).left; + const bisectDate = d3.bisector((d) => d.date).left; return bisectDate(data[0].items, xDate, 1); }, [ data, scale ]); @@ -39,17 +42,17 @@ export const ChartSelectionX = React.memo(({ anchorEl, height, scale, data, onSe .attr('opacity', 1); d3.select(ref.current) - .select('.ChartSelectionX__line_left') + .select(`.${ CLASS_NAME_LINE_LEFT }`) .attr('x1', x0) .attr('x2', x0); d3.select(ref.current) - .select('.ChartSelectionX__line_right') + .select(`.${ CLASS_NAME_LINE_RIGHT }`) .attr('x1', x1) .attr('x2', x1); d3.select(ref.current) - .select('.ChartSelectionX__rect') + .select(`.${ CLASS_NAME_RECT }`) .attr('x', diffX > 0 ? x0 : diffX + x0) .attr('width', Math.abs(diffX)); }, []); @@ -137,9 +140,9 @@ export const ChartSelectionX = React.memo(({ anchorEl, height, scale, data, onSe return ( - - - + + + ); }); diff --git a/toolkit/components/charts/parts/ChartTooltip.tsx b/toolkit/components/charts/line/parts/LineChartTooltip.tsx similarity index 76% rename from toolkit/components/charts/parts/ChartTooltip.tsx rename to toolkit/components/charts/line/parts/LineChartTooltip.tsx index 8aec171655..ed540d4c08 100644 --- a/toolkit/components/charts/parts/ChartTooltip.tsx +++ b/toolkit/components/charts/line/parts/LineChartTooltip.tsx @@ -1,31 +1,31 @@ import * as d3 from 'd3'; import React from 'react'; -import { Resolution } from '../types'; -import type { TimeChartData } from '../types'; - -import ChartTooltipBackdrop, { useRenderBackdrop } from './tooltip/ChartTooltipBackdrop'; -import ChartTooltipContent, { useRenderContent } from './tooltip/ChartTooltipContent'; -import ChartTooltipLine, { useRenderLine } from './tooltip/ChartTooltipLine'; -import ChartTooltipPoint, { useRenderPoints } from './tooltip/ChartTooltipPoint'; -import ChartTooltipRow, { useRenderRows } from './tooltip/ChartTooltipRow'; -import ChartTooltipTitle, { useRenderTitle } from './tooltip/ChartTooltipTitle'; +import { ChartResolution } from '../../types'; +import type { LineChartData } from '../types'; + +import LineChartTooltipBackdrop, { useRenderBackdrop } from './tooltip/LineChartTooltipBackdrop'; +import LineChartTooltipContent, { useRenderContent } from './tooltip/LineChartTooltipContent'; +import LineChartTooltipLine, { useRenderLine } from './tooltip/LineChartTooltipLine'; +import LineChartTooltipPoint, { useRenderPoints } from './tooltip/LineChartTooltipPoint'; +import LineChartTooltipRow, { useRenderRows } from './tooltip/LineChartTooltipRow'; +import LineChartTooltipTitle, { useRenderTitle } from './tooltip/LineChartTooltipTitle'; import { trackPointer } from './tooltip/pointerTracker'; import type { Pointer } from './tooltip/pointerTracker'; -export interface ChartTooltipProps { +export interface LineChartTooltipProps { width?: number; tooltipWidth?: number; height?: number; - data: TimeChartData; + data: LineChartData; xScale: d3.ScaleTime; yScale: d3.ScaleLinear; anchorEl: SVGRectElement | null; noAnimation?: boolean; - resolution?: Resolution; + resolution?: ChartResolution; } -export const ChartTooltip = React.memo(({ +export const LineChartTooltip = React.memo(({ xScale, yScale, width, @@ -36,7 +36,7 @@ export const ChartTooltip = React.memo(({ noAnimation, resolution, ...props -}: ChartTooltipProps) => { +}: LineChartTooltipProps) => { const ref = React.useRef(null); const trackerId = React.useRef(undefined); const isVisible = React.useRef(false); @@ -151,7 +151,7 @@ export const ChartTooltip = React.memo(({ }; }, [ anchorEl, createPointerTracker, draw, hideContent, showContent ]); - const lastItemDateString = data[0].items[data[0].items.length - 1]?.date?.toISOString(); + const lastItemDateString = data[0]?.items[data[0].items.length - 1]?.date?.toISOString(); React.useEffect(() => { if (trackerId.current) { @@ -170,25 +170,25 @@ export const ChartTooltip = React.memo(({ }} { ...props } > - - { data.map(({ name }) => ) } - - - - - { data.map(({ name }, index) => ) } - + + { data.map(({ name }) => ) } + + + + + { data.map(({ name }, index) => ) } + ); }); -function getDateLabel(resolution?: Resolution): string { +function getDateLabel(resolution?: ChartResolution): string { switch (resolution) { - case Resolution.WEEK: + case ChartResolution.WEEK: return 'Dates'; - case Resolution.MONTH: + case ChartResolution.MONTH: return 'Month'; - case Resolution.YEAR: + case ChartResolution.YEAR: return 'Year'; default: return 'Date'; diff --git a/toolkit/components/charts/parts/tooltip/ChartTooltipBackdrop.tsx b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipBackdrop.tsx similarity index 81% rename from toolkit/components/charts/parts/tooltip/ChartTooltipBackdrop.tsx rename to toolkit/components/charts/line/parts/tooltip/LineChartTooltipBackdrop.tsx index 2919fd1341..bff32d0c92 100644 --- a/toolkit/components/charts/parts/tooltip/ChartTooltipBackdrop.tsx +++ b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipBackdrop.tsx @@ -4,12 +4,14 @@ import React from 'react'; import { calculateContainerHeight } from './utils'; -const ChartTooltipBackdrop = () => { +const CLASS_NAME = 'LineChartTooltip__backdrop'; + +const LineChartTooltipBackdrop = () => { const bgColor = useToken('colors', 'blackAlpha.900'); return ( { ); }; -export default React.memo(ChartTooltipBackdrop); +export default React.memo(LineChartTooltipBackdrop); interface UseRenderBackdropParams { seriesNum: number; @@ -30,7 +32,7 @@ export function useRenderBackdrop(ref: React.RefObject, { se if (transitionDuration) { d3.select(ref.current) - .select('.ChartTooltip__backdrop') + .select(`.${ CLASS_NAME }`) .transition() .duration(transitionDuration) .ease(d3.easeLinear) @@ -38,7 +40,7 @@ export function useRenderBackdrop(ref: React.RefObject, { se .attr('height', height); } else { d3.select(ref.current) - .select('.ChartTooltip__backdrop') + .select(`.${ CLASS_NAME }`) .attr('width', width) .attr('height', height); } diff --git a/toolkit/components/charts/parts/tooltip/ChartTooltipContent.tsx b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipContent.tsx similarity index 90% rename from toolkit/components/charts/parts/tooltip/ChartTooltipContent.tsx rename to toolkit/components/charts/line/parts/tooltip/LineChartTooltipContent.tsx index d412f3113e..f35aa4be88 100644 --- a/toolkit/components/charts/parts/tooltip/ChartTooltipContent.tsx +++ b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipContent.tsx @@ -8,11 +8,13 @@ interface Props { children: React.ReactNode; } -const ChartTooltipContent = ({ children }: Props) => { - return { children }; +const CLASS_NAME = 'LineChartTooltip__content'; + +const LineChartTooltipContent = ({ children }: Props) => { + return { children }; }; -export default React.memo(ChartTooltipContent); +export default React.memo(LineChartTooltipContent); interface UseRenderContentParams { chart: { @@ -24,7 +26,7 @@ interface UseRenderContentParams { export function useRenderContent(ref: React.RefObject, { chart, transitionDuration }: UseRenderContentParams) { return React.useCallback((x: number, y: number) => { - const tooltipContent = d3.select(ref.current).select('.ChartTooltip__content'); + const tooltipContent = d3.select(ref.current).select(`.${ CLASS_NAME }`); const transformAttributeFn: d3.ValueFn = (_, i, nodes) => { const node = nodes[i] as SVGGElement | null; diff --git a/toolkit/components/charts/parts/tooltip/ChartTooltipLine.tsx b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipLine.tsx similarity index 64% rename from toolkit/components/charts/parts/tooltip/ChartTooltipLine.tsx rename to toolkit/components/charts/line/parts/tooltip/LineChartTooltipLine.tsx index c25ed21428..f79569d1a4 100644 --- a/toolkit/components/charts/parts/tooltip/ChartTooltipLine.tsx +++ b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipLine.tsx @@ -2,17 +2,19 @@ import { useToken } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -const ChartTooltipLine = () => { +const CLASS_NAME = 'LineChartTooltip__line'; + +const LineChartTooltipLine = () => { const lineColor = useToken('colors', 'gray.400'); - return ; + return ; }; -export default React.memo(ChartTooltipLine); +export default React.memo(LineChartTooltipLine); export function useRenderLine(ref: React.RefObject, chartHeight: number | undefined) { return React.useCallback((x: number) => { d3.select(ref.current) - .select('.ChartTooltip__line') + .select(`.${ CLASS_NAME }`) .attr('x1', x) .attr('x2', x) .attr('y1', 0) diff --git a/toolkit/components/charts/parts/tooltip/ChartTooltipPoint.tsx b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipPoint.tsx similarity index 78% rename from toolkit/components/charts/parts/tooltip/ChartTooltipPoint.tsx rename to toolkit/components/charts/line/parts/tooltip/LineChartTooltipPoint.tsx index b5d0477599..e31fe7bace 100644 --- a/toolkit/components/charts/parts/tooltip/ChartTooltipPoint.tsx +++ b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipPoint.tsx @@ -2,18 +2,20 @@ import { useToken } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -import type { TimeChartData, TimeChartItem } from '../../types'; +import type { LineChartData, LineChartItem } from '../../types'; -import { useColorModeValue } from '../../../../chakra/color-mode'; +import { useColorModeValue } from '../../../../../chakra/color-mode'; import { POINT_SIZE } from './utils'; -const ChartTooltipPoint = () => { +const CLASS_NAME = 'LineChartTooltip__point'; + +const LineChartTooltipPoint = () => { const bgColor = useToken('colors', useColorModeValue('black', 'white')); const borderColor = useToken('colors', useColorModeValue('white', 'black')); return ( { ); }; -export default React.memo(ChartTooltipPoint); +export default React.memo(LineChartTooltipPoint); interface UseRenderPointsParams { - data: TimeChartData; + data: LineChartData; xScale: d3.ScaleTime; yScale: d3.ScaleLinear; } export interface CurrentPoint { datumIndex: number; - item: TimeChartItem; + item: LineChartItem; } interface RenderPointsReturnType { @@ -45,18 +47,18 @@ interface RenderPointsReturnType { export function useRenderPoints(ref: React.RefObject, params: UseRenderPointsParams) { return React.useCallback((x: number): RenderPointsReturnType => { const xDate = params.xScale.invert(x); - const bisectDate = d3.bisector((d) => d.date).left; + const bisectDate = d3.bisector((d) => d.date).left; let baseXPos = 0; let baseYPos = 0; const currentPoints: Array = []; d3.select(ref.current) - .selectAll('.ChartTooltip__point') + .selectAll(`.${ CLASS_NAME }`) .attr('transform', (_, elementIndex) => { const datum = params.data[elementIndex]; const index = bisectDate(datum.items, xDate, 1); - const d0 = datum.items[index - 1] as TimeChartItem | undefined; - const d1 = datum.items[index] as TimeChartItem | undefined; + const d0 = datum.items[index - 1] as LineChartItem | undefined; + const d1 = datum.items[index] as LineChartItem | undefined; const d = (() => { if (!d0) { return d1; diff --git a/toolkit/components/charts/parts/tooltip/ChartTooltipRow.tsx b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipRow.tsx similarity index 79% rename from toolkit/components/charts/parts/tooltip/ChartTooltipRow.tsx rename to toolkit/components/charts/line/parts/tooltip/LineChartTooltipRow.tsx index 31211ce052..fba10a79de 100644 --- a/toolkit/components/charts/parts/tooltip/ChartTooltipRow.tsx +++ b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipRow.tsx @@ -2,25 +2,29 @@ import { useToken } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -import type { TimeChartData } from '../../types'; +import type { LineChartData } from '../../types'; -import type { CurrentPoint } from './ChartTooltipPoint'; +import type { CurrentPoint } from './LineChartTooltipPoint'; import { calculateRowTransformValue, LABEL_WIDTH, PADDING } from './utils'; +const CLASS_NAME_ROW = 'LineChartTooltip__row'; +const CLASS_NAME_LABEL = 'LineChartTooltip__label'; +const CLASS_NAME_VALUE = 'LineChartTooltip__value'; + type Props = { lineNum: number; } & ({ label: string; children?: never } | { children: React.ReactNode; label?: never }); -const ChartTooltipRow = ({ label, lineNum, children }: Props) => { +const LineChartTooltipRow = ({ label, lineNum, children }: Props) => { const labelColor = useToken('colors', 'blue.100'); const textColor = useToken('colors', 'white'); return ( - + { children || ( <> { { label } { ); }; -export default React.memo(ChartTooltipRow); +export default React.memo(LineChartTooltipRow); interface UseRenderRowsParams { - data: TimeChartData; + data: LineChartData; xScale: d3.ScaleTime; minWidth: number; } @@ -57,7 +61,7 @@ export function useRenderRows(ref: React.RefObject, { data, // update "transform" prop of all rows const isIncompleteData = currentPoints.some(({ item }) => item.isApproximate); d3.select(ref.current) - .selectAll('.ChartTooltip__row') + .selectAll(`.${ CLASS_NAME_ROW }`) .attr('transform', (_, index) => { return calculateRowTransformValue(index - (isIncompleteData ? 0 : 1)); }); @@ -65,7 +69,7 @@ export function useRenderRows(ref: React.RefObject, { data, // update date and indicators value // here we assume that the first value element contains the date const valueNodes = d3.select(ref.current) - .selectAll('.ChartTooltip__value') + .selectAll(`.${ CLASS_NAME_VALUE }`) .text((_, index) => { if (index === 0) { const date = xScale.invert(x); diff --git a/toolkit/components/charts/parts/tooltip/ChartTooltipTitle.tsx b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipTitle.tsx similarity index 51% rename from toolkit/components/charts/parts/tooltip/ChartTooltipTitle.tsx rename to toolkit/components/charts/line/parts/tooltip/LineChartTooltipTitle.tsx index c3b698511f..e7730a682c 100644 --- a/toolkit/components/charts/parts/tooltip/ChartTooltipTitle.tsx +++ b/toolkit/components/charts/line/parts/tooltip/LineChartTooltipTitle.tsx @@ -2,18 +2,20 @@ import { useToken } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -import { Resolution, RESOLUTION_LABELS } from '../../types'; +import { ChartResolution, CHART_RESOLUTION_LABELS } from '../../../types'; -import ChartTooltipRow from './ChartTooltipRow'; +import LineChartTooltipRow from './LineChartTooltipRow'; -const ChartTooltipTitle = ({ resolution = Resolution.DAY }: { resolution?: Resolution }) => { +const CLASS_NAME = 'LineChartTooltip__title'; + +const LineChartTooltipTitle = ({ resolution = ChartResolution.DAY }: { resolution?: ChartResolution }) => { const titleColor = useToken('colors', 'yellow.300'); - const resolutionTitle = RESOLUTION_LABELS.find(({ id }) => id === resolution)?.title || 'day'; + const resolutionTitle = CHART_RESOLUTION_LABELS.find(({ id }) => id === resolution)?.title || 'day'; return ( - + { `Incomplete ${ resolutionTitle.toLowerCase() }` } - + ); }; -export default React.memo(ChartTooltipTitle); +export default React.memo(LineChartTooltipTitle); export function useRenderTitle(ref: React.RefObject) { return React.useCallback((isVisible: boolean) => { d3.select(ref.current) - .select('.ChartTooltip__title') + .select(`.${ CLASS_NAME }`) .attr('opacity', isVisible ? 1 : 0); }, [ ref ]); } diff --git a/toolkit/components/charts/parts/tooltip/pointerTracker.ts b/toolkit/components/charts/line/parts/tooltip/pointerTracker.ts similarity index 100% rename from toolkit/components/charts/parts/tooltip/pointerTracker.ts rename to toolkit/components/charts/line/parts/tooltip/pointerTracker.ts diff --git a/toolkit/components/charts/parts/tooltip/utils.ts b/toolkit/components/charts/line/parts/tooltip/utils.ts similarity index 100% rename from toolkit/components/charts/parts/tooltip/utils.ts rename to toolkit/components/charts/line/parts/tooltip/utils.ts diff --git a/toolkit/components/charts/line/types.tsx b/toolkit/components/charts/line/types.tsx new file mode 100644 index 0000000000..0f33d2b41c --- /dev/null +++ b/toolkit/components/charts/line/types.tsx @@ -0,0 +1,61 @@ +import type * as d3 from 'd3'; + +export interface LineChartItemRaw { + date: Date; + dateLabel?: string; + value: number | string | null; +} + +export interface LineChartItem { + date: Date; + date_to?: Date; + dateLabel?: string; + value: number; + isApproximate?: boolean; +} + +export type LineChartConfig = + | { + type: 'line'; + color: string; + strokeWidth?: number; + strokeDasharray?: string; + } | + { + type: 'area'; + gradient: { + startColor: string; + stopColor: string; + }; + }; + +export interface LineChartDataItem { + id: string; + name: string; + items: Array; + charts: Array; + units?: string; + valueFormatter?: (value: number) => string; +} + +export type LineChartData = Array; + +export interface LineChartAxisConfig { + ticks?: number; + nice?: boolean; + noLabel?: boolean; + scale?: { + min?: number; + }; + tickFormatter?: () => (d: d3.AxisDomain) => string; +} + +export interface LineChartAxesConfig { + x?: LineChartAxisConfig; + y?: LineChartAxisConfig; +} + +export type LineChartAxesConfigFn = (props: { + isEnlarged?: boolean; + isMobile?: boolean; +}) => LineChartAxesConfig; diff --git a/toolkit/components/charts/utils/animations.ts b/toolkit/components/charts/line/utils/animations.ts similarity index 100% rename from toolkit/components/charts/utils/animations.ts rename to toolkit/components/charts/line/utils/animations.ts diff --git a/toolkit/components/charts/utils/formatters.ts b/toolkit/components/charts/line/utils/formatters.ts similarity index 65% rename from toolkit/components/charts/utils/formatters.ts rename to toolkit/components/charts/line/utils/formatters.ts index 9d0d13a7d2..116d216b09 100644 --- a/toolkit/components/charts/utils/formatters.ts +++ b/toolkit/components/charts/line/utils/formatters.ts @@ -1,7 +1,7 @@ -import type { TimeChartItem } from '../types'; +import type { LineChartItem } from '../types'; -export const getIncompleteDataLineSource = (data: Array): Array => { - const result: Array = []; +export const getIncompleteDataLineSource = (data: Array): Array => { + const result: Array = []; for (let index = 0; index < data.length; index++) { const current = data[index]; diff --git a/toolkit/components/charts/utils/getDateLabel.ts b/toolkit/components/charts/line/utils/getDateLabel.ts similarity index 68% rename from toolkit/components/charts/utils/getDateLabel.ts rename to toolkit/components/charts/line/utils/getDateLabel.ts index 98a2e40eb8..0e7acad0f9 100644 --- a/toolkit/components/charts/utils/getDateLabel.ts +++ b/toolkit/components/charts/line/utils/getDateLabel.ts @@ -1,14 +1,14 @@ import * as d3 from 'd3'; -import { Resolution } from '../types'; +import { ChartResolution } from '../../types'; -export function getDateLabel(date: Date, dateTo?: Date, resolution?: Resolution): string { +export function getDateLabel(date: Date, dateTo?: Date, resolution?: ChartResolution): string { switch (resolution) { - case Resolution.WEEK: + case ChartResolution.WEEK: return d3.utcFormat('%e %b %Y')(date) + (dateTo ? ` – ${ d3.utcFormat('%e %b %Y')(dateTo) }` : ''); - case Resolution.MONTH: + case ChartResolution.MONTH: return d3.utcFormat('%b %Y')(date); - case Resolution.YEAR: + case ChartResolution.YEAR: return d3.utcFormat('%Y')(date); default: return d3.utcFormat('%e %b %Y')(date); diff --git a/toolkit/components/charts/utils/timeChartAxis.ts b/toolkit/components/charts/line/utils/lineChartAxis.ts similarity index 88% rename from toolkit/components/charts/utils/timeChartAxis.ts rename to toolkit/components/charts/line/utils/lineChartAxis.ts index 5dc23296e2..46d95716a1 100644 --- a/toolkit/components/charts/utils/timeChartAxis.ts +++ b/toolkit/components/charts/line/utils/lineChartAxis.ts @@ -1,9 +1,9 @@ import * as d3 from 'd3'; import { maxBy, uniq } from 'es-toolkit'; -import type { AxesConfig, AxisConfig, TimeChartData } from '../types'; +import type { LineChartAxesConfig, LineChartAxisConfig, LineChartData } from '../types'; -import { DAY, MONTH, YEAR } from '../../../utils/consts'; +import { DAY, MONTH, YEAR } from '../../../../utils/consts'; export const DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS = 2; export const DEFAULT_MAXIMUM_FRACTION_DIGITS = 3; @@ -14,9 +14,9 @@ export interface LabelFormatParams extends Intl.NumberFormatOptions { maxLabelLength: number; } -type Data = TimeChartData; +type Data = LineChartData; -export function getAxesParams(data: Data, axesConfig?: AxesConfig) { +export function getAxesParams(data: Data, axesConfig?: LineChartAxesConfig) { const { labelFormatParams: labelFormatParamsY, scale: yScale } = getAxisParamsY(data, axesConfig?.y, axesConfig?.y?.tickFormatter); return { @@ -35,7 +35,7 @@ export function getAxesParams(data: Data, axesConfig?: AxesConfig) { function getAxisParamsX(data: Data) { const min = d3.min(data, ({ items }) => d3.min(items, ({ date }) => date)) ?? new Date(); const max = d3.max(data, ({ items }) => d3.max(items, ({ date }) => date)) ?? new Date(); - const scale = d3.scaleTime().domain([ min, max ]); + const scale = d3.scaleUtc().domain([ min, max ]); return { min, max, scale }; } @@ -60,7 +60,7 @@ const tickFormatterX = (axis: d3.Axis) => (d: d3.AxisDomain) => return format(d as Date); }; -function getAxisParamsY(data: Data, config?: AxisConfig, tickFormatter?: () => (d: d3.AxisDomain) => string) { +function getAxisParamsY(data: Data, config?: LineChartAxisConfig, tickFormatter?: () => (d: d3.AxisDomain) => string) { const DEFAULT_TICKS_NUM = 3; const min = d3.min(data, ({ items }) => d3.min(items, ({ value }) => value)) ?? 0; const max = d3.max(data, ({ items }) => d3.max(items, ({ value }) => value)) ?? 0; diff --git a/toolkit/components/charts/utils/useChartBrushX.tsx b/toolkit/components/charts/line/utils/useLineChartBrushX.tsx similarity index 87% rename from toolkit/components/charts/utils/useChartBrushX.tsx rename to toolkit/components/charts/line/utils/useLineChartBrushX.tsx index a8798dd7e8..09d52c65c4 100644 --- a/toolkit/components/charts/utils/useChartBrushX.tsx +++ b/toolkit/components/charts/line/utils/useLineChartBrushX.tsx @@ -2,7 +2,7 @@ import { useToken } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -import { useColorModeValue } from '../../../chakra/color-mode'; +import { useColorModeValue } from '../../../../chakra/color-mode'; interface Props { limits: [[number, number], [number, number]]; @@ -10,7 +10,7 @@ interface Props { setRange: (range: [number, number]) => void; } -export function useChartBrushX({ limits, anchor, setRange }: Props) { +export function useLineChartBrushX({ limits, anchor, setRange }: Props) { const brushRef = React.useRef>(undefined); const [ brushSelectionBg ] = useToken('colors', useColorModeValue('blackAlpha.400', 'whiteAlpha.500')); diff --git a/toolkit/components/charts/utils/useTimeChartController.tsx b/toolkit/components/charts/line/utils/useLineChartController.tsx similarity index 79% rename from toolkit/components/charts/utils/useTimeChartController.tsx rename to toolkit/components/charts/line/utils/useLineChartController.tsx index 77880f68ba..f4f229cd52 100644 --- a/toolkit/components/charts/utils/useTimeChartController.tsx +++ b/toolkit/components/charts/line/utils/useLineChartController.tsx @@ -1,18 +1,19 @@ import React from 'react'; -import type { AxesConfig, ChartMargin, TimeChartData } from '../types'; +import type { ChartMargin } from '../../types'; +import type { LineChartAxesConfig, LineChartData } from '../types'; -import { useClientRect } from '../../../hooks/useClientRect'; -import { calculateInnerSize } from './calculateInnerSize'; -import { getAxesParams } from './timeChartAxis'; +import { useClientRect } from '../../../../hooks/useClientRect'; +import { calculateInnerSize } from '../../utils/calculateInnerSize'; +import { getAxesParams } from './lineChartAxis'; interface Props { - data: TimeChartData; + data: LineChartData; margin?: ChartMargin; - axesConfig?: AxesConfig; + axesConfig?: LineChartAxesConfig; } -export function useTimeChartController({ data, margin, axesConfig }: Props) { +export function useLineChartController({ data, margin, axesConfig }: Props) { const [ rect, ref ] = useClientRect(); diff --git a/toolkit/components/charts/utils/useChartLegend.tsx b/toolkit/components/charts/line/utils/useLineChartLegend.tsx similarity index 89% rename from toolkit/components/charts/utils/useChartLegend.tsx rename to toolkit/components/charts/line/utils/useLineChartLegend.tsx index 00756d9985..9bb01afcb9 100644 --- a/toolkit/components/charts/utils/useChartLegend.tsx +++ b/toolkit/components/charts/line/utils/useLineChartLegend.tsx @@ -1,7 +1,7 @@ import { range } from 'es-toolkit'; import React from 'react'; -export function useChartLegend(dataLength: number) { +export function useLineChartLegend(dataLength: number) { const [ selectedLines, setSelectedLines ] = React.useState>(range(dataLength)); const handleLegendItemClick = React.useCallback((index: number) => { diff --git a/toolkit/components/charts/utils/useChartZoom.tsx b/toolkit/components/charts/line/utils/useLineChartZoom.tsx similarity index 93% rename from toolkit/components/charts/utils/useChartZoom.tsx rename to toolkit/components/charts/line/utils/useLineChartZoom.tsx index fc2a17065d..4e3a4ebc71 100644 --- a/toolkit/components/charts/utils/useChartZoom.tsx +++ b/toolkit/components/charts/line/utils/useLineChartZoom.tsx @@ -1,6 +1,6 @@ import React from 'react'; -export function useChartZoom() { +export function useLineChartZoom() { const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true); const [ zoomRange, setZoomRange ] = React.useState<[ Date, Date ] | undefined>(); diff --git a/toolkit/components/charts/parts/ChartMenu.tsx b/toolkit/components/charts/parts/ChartMenu.tsx deleted file mode 100644 index a6c04b0dd0..0000000000 --- a/toolkit/components/charts/parts/ChartMenu.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import { Icon } from '@chakra-ui/react'; -import { useCopyToClipboard } from '@uidotdev/usehooks'; -import dayjs from 'dayjs'; -import domToImage from 'dom-to-image'; -import React from 'react'; - -import type { Resolution, TimeChartData } from '../types'; - -import CopyIcon from 'icons/copy.svg'; -import DotsIcon from 'icons/dots.svg'; -import CsvIcon from 'icons/files/csv.svg'; -import ImageIcon from 'icons/files/image.svg'; -import ScopeIcon from 'icons/scope.svg'; -import ShareIcon from 'icons/share.svg'; -import { useMultichainContext } from 'lib/contexts/multichain'; - -import { useColorModeValue } from '../../../chakra/color-mode'; -import { IconButton } from '../../../chakra/icon-button'; -import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '../../../chakra/menu'; -import { useDisclosure } from '../../../hooks/useDisclosure'; -import { saveAsCsv } from '../../../utils/file'; -import { isBrowser } from '../../../utils/isBrowser'; -import ChartFullscreenDialog from '../ChartFullscreenDialog'; - -export const CHART_MENU_ITEMS_IDS = [ - 'share' as const, - 'fullscreen' as const, - 'save_png' as const, - 'save_csv' as const, -]; - -export type ChartMenuItemId = (typeof CHART_MENU_ITEMS_IDS)[number]; - -export interface Props { - itemIds?: Array; - charts: TimeChartData; - title: string; - description?: string; - isLoading: boolean; - chartRef: React.RefObject; - chartUrl?: string; - resolution?: Resolution; - zoomRange?: [ Date, Date ]; - handleZoom: (range: [ Date, Date ]) => void; - handleZoomReset: () => void; -}; - -const DOWNLOAD_IMAGE_SCALE = 5; - -const ChartMenu = ({ - itemIds = CHART_MENU_ITEMS_IDS, - charts, - title, - description, - isLoading, - chartRef, - chartUrl, - resolution, - zoomRange, - handleZoom, - handleZoomReset, -}: Props) => { - const pngBackgroundColor = useColorModeValue('white', 'black'); - const fullscreenDialog = useDisclosure(); - - const [ , copyToClipboard ] = useCopyToClipboard(); - - const isInBrowser = isBrowser(); - - const multichainContext = useMultichainContext(); - - const chainPostfix = React.useMemo(() => { - return multichainContext?.chain.name ? ` on ${ multichainContext.chain.name }` : ''; - }, [ multichainContext?.chain.name ]); - - const showChartFullscreen = React.useCallback(() => { - fullscreenDialog.onOpenChange({ open: true }); - }, [ fullscreenDialog ]); - - const handleFileSaveClick = React.useCallback(() => { - // wait for context menu to close - setTimeout(() => { - if (chartRef.current) { - domToImage.toPng(chartRef.current, - { - quality: 100, - bgcolor: pngBackgroundColor, - width: chartRef.current.offsetWidth * DOWNLOAD_IMAGE_SCALE, - height: chartRef.current.offsetHeight * DOWNLOAD_IMAGE_SCALE, - filter: (node) => node.nodeName !== 'BUTTON', - style: { - borderColor: 'transparent', - transform: `scale(${ DOWNLOAD_IMAGE_SCALE })`, - 'transform-origin': 'top left', - }, - }) - .then((dataUrl) => { - const link = document.createElement('a'); - link.download = `${ title }${ chainPostfix } (Blockscout chart).png`; - link.href = dataUrl; - link.click(); - link.remove(); - }); - } - }, 100); - }, [ pngBackgroundColor, title, chainPostfix, chartRef ]); - - const handleSVGSavingClick = React.useCallback(() => { - const headerRows = [ - 'Date', ...charts.map((chart) => chart.name), - ]; - const dataRows = charts[0].items.map((item, index) => [ - item.dateLabel ?? dayjs(item.date).format('YYYY-MM-DD'), - ...charts.map((chart) => String(chart.items[index].value)), - ]); - saveAsCsv(headerRows, dataRows, `${ title }${ chainPostfix } (Blockscout stats)`); - }, [ charts, title, chainPostfix ]); - - // TS thinks window.navigator.share can't be undefined, but it can - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const hasShare = isInBrowser && (window.navigator.share as any); - - const handleCopy = React.useCallback(() => { - copyToClipboard(chartUrl ?? ''); - }, [ chartUrl, copyToClipboard ]); - - const handleShare = React.useCallback(async() => { - try { - await window.navigator.share({ - title: title, - text: description, - url: chartUrl, - }); - } catch (error) {} - }, [ title, description, chartUrl ]); - - return ( - <> - - - - - - - - { itemIds.includes('share') && chartUrl && ( - - { hasShare ? : } - { hasShare ? 'Share' : 'Copy link' } - - ) } - { itemIds.includes('fullscreen') && ( - - - View fullscreen - - ) } - { itemIds.includes('save_png') && ( - - - Save as PNG - - ) } - { itemIds.includes('save_csv') && ( - - - Save as CSV - - ) } - - - - - ); -}; - -export default ChartMenu; diff --git a/toolkit/components/charts/sankey/SankeyChart.tsx b/toolkit/components/charts/sankey/SankeyChart.tsx index 2fa46c8f2c..8784b088f0 100644 --- a/toolkit/components/charts/sankey/SankeyChart.tsx +++ b/toolkit/components/charts/sankey/SankeyChart.tsx @@ -2,24 +2,23 @@ import { useToken } from '@chakra-ui/react'; import React from 'react'; import type { ChartMargin } from '../types'; -import type { SankeyData, SankeyNodeExtended } from './types'; - -import { useColorModeValue } from 'toolkit/chakra/color-mode'; +import type { SankeyChartData, SankeyChartNodeExtended } from './types'; +import { useColorModeValue } from '../../../chakra/color-mode'; import { - DEFAULT_SANKEY_LINK_HOVER_OPACITY, - DEFAULT_SANKEY_LINK_OPACITY, + SANKEY_LINK_DEFAULT_HOVER_OPACITY, + SANKEY_LINK_DEFAULT_OPACITY, SANKEY_NODE_COLOR_TOKENS_DARK, SANKEY_NODE_COLOR_TOKENS_LIGHT, } from './constants'; -import { SankeyLink } from './parts/SankeyLink'; -import { SankeyNode } from './parts/SankeyNode'; +import { SankeyChartLink } from './parts/SankeyChartLink'; +import { SankeyChartNode } from './parts/SankeyChartNode'; import { useSankeyController } from './useSankeyController'; export type SankeyLinkColorMode = 'source' | 'target'; export interface SankeyChartProps { - data: SankeyData; + data: SankeyChartData; margin?: ChartMargin; nodeWidth?: number; nodePadding?: number; @@ -30,7 +29,7 @@ export interface SankeyChartProps { valueFormatter?: (value: number) => string; } -const DEFAULT_CHART_MARGIN: ChartMargin = { top: 40, right: 8, bottom: 8, left: 8 }; +const DEFAULT_CHART_MARGIN: ChartMargin = { top: 40, right: 0, bottom: 0, left: 0 }; const LABEL_Y_GAP = 4; const LABEL_LINE_HEIGHT = 16; const LABEL_LINE_GAP = 2; @@ -40,8 +39,8 @@ export const SankeyChart = React.memo(({ margin: marginProps, nodeWidth, nodePadding, - linkOpacity = DEFAULT_SANKEY_LINK_OPACITY, - linkHoverOpacity = DEFAULT_SANKEY_LINK_HOVER_OPACITY, + linkOpacity = SANKEY_LINK_DEFAULT_OPACITY, + linkHoverOpacity = SANKEY_LINK_DEFAULT_HOVER_OPACITY, linkColorMode = 'source', colors: colorsProp, valueFormatter, @@ -77,7 +76,7 @@ export const SankeyChart = React.memo(({ return map; }, [ nodes, colors ]); - const getNodeColor = React.useCallback((node: SankeyNodeExtended) => { + const getNodeColor = React.useCallback((node: SankeyChartNodeExtended) => { return nodeColorMap.get(node.id) || colors[0]; }, [ nodeColorMap, colors ]); @@ -85,14 +84,14 @@ export const SankeyChart = React.memo(({ if (value === undefined) { return ''; } - return valueFormatter ? valueFormatter(value) : String(value); + return valueFormatter ? valueFormatter(value) : Number(value).toLocaleString(); }, [ valueFormatter ]); return ( { links.map((link, index) => ( - ( - , Omit { + data?: SankeyChartData; +} + +export const SankeyChartContent = React.memo(({ + data, + isError, + isLoading, + isEmpty, + emptyText, + noWatermark, + noEmptyStateIcon, + ...rest +}: SankeyChartContentProps) => { + return ( + + { data && } + + ); +}); diff --git a/toolkit/components/charts/sankey/SankeyChartModal.tsx b/toolkit/components/charts/sankey/SankeyChartModal.tsx new file mode 100644 index 0000000000..21574f8d8c --- /dev/null +++ b/toolkit/components/charts/sankey/SankeyChartModal.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +import type { ChartDialogProps } from '../components/ChartDialog'; +import { ChartDialog } from '../components/ChartDialog'; +import type { SankeyChartContentProps } from './SankeyChartContent'; +import { SankeyChartContent } from './SankeyChartContent'; + +interface Props extends Omit, SankeyChartContentProps {} + +export const SankeyChartModal = React.memo(({ open, onOpenChange, title, description, ...rest }: Props) => { + return ( + + + + ); +}); diff --git a/toolkit/components/charts/sankey/SankeyChartWidget.tsx b/toolkit/components/charts/sankey/SankeyChartWidget.tsx new file mode 100644 index 0000000000..92b8903648 --- /dev/null +++ b/toolkit/components/charts/sankey/SankeyChartWidget.tsx @@ -0,0 +1,61 @@ +import type { FlexProps } from '@chakra-ui/react'; +import { Box, Flex } from '@chakra-ui/react'; +import React from 'react'; + +import type { SankeyChartData } from './types'; + +import type { ChartContentProps } from '../components/ChartContent'; +import { ChartWidgetRoot, ChartWidgetHeader } from '../components/ChartWidget'; +import { SankeyChartMenu } from './parts/SankeyChartMenu'; +import type { SankeyChartProps } from './SankeyChart'; +import { SankeyChartContent } from './SankeyChartContent'; + +export interface SankeyChartWidgetProps extends Omit, Omit { + data?: SankeyChartData; + title: string; + description?: string; + href?: string; + chartUrl?: string; + containerProps?: FlexProps; +} + +export const SankeyChartWidget = ({ + data, + title, + description, + isLoading, + href, + chartUrl, + containerProps, + ...rest +}: SankeyChartWidgetProps) => { + const ref = React.useRef(null); + + return ( + + + + + + + + + + ); +}; diff --git a/toolkit/components/charts/sankey/constants.ts b/toolkit/components/charts/sankey/constants.ts index a404dc7676..14311eaba3 100644 --- a/toolkit/components/charts/sankey/constants.ts +++ b/toolkit/components/charts/sankey/constants.ts @@ -32,7 +32,7 @@ export const SANKEY_NODE_COLOR_TOKENS_DARK: Array = [ 'red.700', ]; -export const DEFAULT_SANKEY_NODE_WIDTH = 16; -export const DEFAULT_SANKEY_NODE_PADDING = 40; -export const DEFAULT_SANKEY_LINK_OPACITY = 0.35; -export const DEFAULT_SANKEY_LINK_HOVER_OPACITY = 0.6; +export const SANKEY_NODE_DEFAULT_WIDTH = 16; +export const SANKEY_NODE_DEFAULT_PADDING = 40; +export const SANKEY_LINK_DEFAULT_OPACITY = 0.35; +export const SANKEY_LINK_DEFAULT_HOVER_OPACITY = 0.6; diff --git a/toolkit/components/charts/sankey/index.ts b/toolkit/components/charts/sankey/index.ts index 79ffefd399..61407423bf 100644 --- a/toolkit/components/charts/sankey/index.ts +++ b/toolkit/components/charts/sankey/index.ts @@ -1,6 +1,6 @@ export * from './SankeyChart'; -export * from './parts/SankeyNode'; -export * from './parts/SankeyLink'; +export * from './parts/SankeyChartNode'; +export * from './parts/SankeyChartLink'; export * from './useSankeyController'; export * from './types'; export * from './constants'; diff --git a/toolkit/components/charts/sankey/parts/SankeyLink.tsx b/toolkit/components/charts/sankey/parts/SankeyChartLink.tsx similarity index 81% rename from toolkit/components/charts/sankey/parts/SankeyLink.tsx rename to toolkit/components/charts/sankey/parts/SankeyChartLink.tsx index 74655b61d5..262cb199eb 100644 --- a/toolkit/components/charts/sankey/parts/SankeyLink.tsx +++ b/toolkit/components/charts/sankey/parts/SankeyChartLink.tsx @@ -2,10 +2,10 @@ import { select } from 'd3-selection'; import 'd3-transition'; import React from 'react'; -import type { SankeyLinkExtended } from '../types'; +import type { SankeyChartLinkExtended } from '../types'; -export interface SankeyLinkProps { - link: SankeyLinkExtended; +export interface SankeyChartLinkProps { + link: SankeyChartLinkExtended; /** Single color when gradient is not used */ color?: string; @@ -19,12 +19,12 @@ export interface SankeyLinkProps { opacity: number; hoverOpacity: number; - pathGenerator: (link: SankeyLinkExtended, ...args: Array) => string | null; - onMouseEnter?: (link: SankeyLinkExtended, event: React.MouseEvent) => void; + pathGenerator: (link: SankeyChartLinkExtended, ...args: Array) => string | null; + onMouseEnter?: (link: SankeyChartLinkExtended, event: React.MouseEvent) => void; onMouseLeave?: () => void; } -export const SankeyLink = React.memo(({ +export const SankeyChartLink = React.memo(({ link, color, sourceColor, @@ -35,7 +35,7 @@ export const SankeyLink = React.memo(({ pathGenerator, onMouseEnter, onMouseLeave, -}: SankeyLinkProps) => { +}: SankeyChartLinkProps) => { const ref = React.useRef(null); const handleMouseEnter = React.useCallback((event: React.MouseEvent) => { @@ -61,7 +61,7 @@ export const SankeyLink = React.memo(({ } const sanitizeSvgId = (value: string | number): string => String(value).replace(/[^\w-]/g, '_'); - const useGradient = sourceColor != null && targetColor != null && sourceColor !== targetColor; + const useGradient = sourceColor !== undefined && targetColor !== undefined && sourceColor !== targetColor; const gradientId = useGradient ? `sankey-link-${ sanitizeSvgId(source.id) }-${ sanitizeSvgId(target.id) }-${ sanitizeSvgId(gradientIdSuffix ?? '') }` : undefined; @@ -102,7 +102,7 @@ export const SankeyLink = React.memo(({ onMouseEnter={ handleMouseEnter } onMouseLeave={ handleMouseLeave } > - { `${ source.name } → ${ target.name }: ${ link.value }` } + { `${ source.name } → ${ target.name }: ${ Number(link.value).toLocaleString() }` } ); diff --git a/toolkit/components/charts/sankey/parts/SankeyChartMenu.tsx b/toolkit/components/charts/sankey/parts/SankeyChartMenu.tsx new file mode 100644 index 0000000000..05203b92c9 --- /dev/null +++ b/toolkit/components/charts/sankey/parts/SankeyChartMenu.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import type { SankeyChartData } from '../types'; + +import { useDisclosure } from '../../../../hooks/useDisclosure'; +import type { ChartMenuProps } from '../../components/ChartMenu'; +import { ChartMenu } from '../../components/ChartMenu'; +import { SankeyChartModal } from '../SankeyChartModal'; + +export interface SankeyChartMenuProps extends Omit { + data?: SankeyChartData; +} + +export const SankeyChartMenu = React.memo(({ data, ...rest }: SankeyChartMenuProps) => { + const modal = useDisclosure(); + + const handleModalOpen = React.useCallback(() => { + modal.onOpenChange({ open: true }); + }, [ modal ]); + + const csvData = React.useMemo(() => { + if (!data) { + return []; + } + const headerRows = [ 'Source', 'Target', 'Value' ]; + const dataRows = data.links.map((link) => { + const source = data.nodes.find((node) => node.id === link.source)?.name; + const target = data.nodes.find((node) => node.id === link.target)?.name; + return [ source ?? '', target ?? '', String(link.value) ]; + }); + return [ headerRows, ...dataRows ]; + }, [ data ]); + + return ( + <> + + + + ); +}); diff --git a/toolkit/components/charts/sankey/parts/SankeyNode.tsx b/toolkit/components/charts/sankey/parts/SankeyChartNode.tsx similarity index 64% rename from toolkit/components/charts/sankey/parts/SankeyNode.tsx rename to toolkit/components/charts/sankey/parts/SankeyChartNode.tsx index a5a7519f5b..9598d98659 100644 --- a/toolkit/components/charts/sankey/parts/SankeyNode.tsx +++ b/toolkit/components/charts/sankey/parts/SankeyChartNode.tsx @@ -1,17 +1,17 @@ import React from 'react'; -import type { SankeyNodeExtended } from '../types'; +import type { SankeyChartNodeExtended } from '../types'; const OUTER_CORNER_RADIUS = 4; -export interface SankeyNodeProps { - node: SankeyNodeExtended; +export interface SankeyChartNodeProps { + node: SankeyChartNodeExtended; color: string; - onMouseEnter?: (node: SankeyNodeExtended, event: React.MouseEvent) => void; + onMouseEnter?: (node: SankeyChartNodeExtended, event: React.MouseEvent) => void; onMouseLeave?: () => void; } -export const SankeyNode = React.memo(({ node, color, onMouseEnter, onMouseLeave }: SankeyNodeProps) => { +export const SankeyChartNode = React.memo(({ node, color, onMouseEnter, onMouseLeave }: SankeyChartNodeProps) => { const { x0 = 0, x1 = 0, y0 = 0, y1 = 0 } = node; const width = x1 - x0; const height = y1 - y0; @@ -43,22 +43,19 @@ export const SankeyNode = React.memo(({ node, color, onMouseEnter, onMouseLeave }, [ x0, x1, y0, y1, width, height, isMiddle, hasIncoming ]); if (!path) { - if (isMiddle) { - return ( - - { `${ node.name }: ${ node.value }` } - - ); - } - return null; + return ( + + { `${ node.name }: ${ Number(node.value).toLocaleString() }` } + + ); } return ( @@ -68,7 +65,7 @@ export const SankeyNode = React.memo(({ node, color, onMouseEnter, onMouseLeave onMouseEnter={ handleMouseEnter } onMouseLeave={ onMouseLeave } > - { `${ node.name }: ${ node.value }` } + { `${ node.name }: ${ Number(node.value).toLocaleString() }` } ); }); diff --git a/toolkit/components/charts/sankey/types.ts b/toolkit/components/charts/sankey/types.ts index aa11e78866..678d20cb56 100644 --- a/toolkit/components/charts/sankey/types.ts +++ b/toolkit/components/charts/sankey/types.ts @@ -11,16 +11,16 @@ export interface SankeyLinkDatum { readonly value: number; } -export interface SankeyData { +export interface SankeyChartData { readonly nodes: ReadonlyArray; readonly links: ReadonlyArray; } // After d3-sankey layout, nodes are the input datum merged with the layout properties. -export type SankeyNodeExtended = SankeyNodeDatum & SankeyNode; +export type SankeyChartNodeExtended = SankeyNodeDatum & SankeyNode; // After d3-sankey layout, source/target are always resolved node objects, not ids. -export type SankeyLinkExtended = Omit, 'source' | 'target'> & { - source: SankeyNodeExtended; - target: SankeyNodeExtended; +export type SankeyChartLinkExtended = Omit, 'source' | 'target'> & { + source: SankeyChartNodeExtended; + target: SankeyChartNodeExtended; }; diff --git a/toolkit/components/charts/sankey/useSankeyController.ts b/toolkit/components/charts/sankey/useSankeyController.ts index 55602bb985..7d11a2da62 100644 --- a/toolkit/components/charts/sankey/useSankeyController.ts +++ b/toolkit/components/charts/sankey/useSankeyController.ts @@ -2,23 +2,22 @@ import { sankey, sankeyLinkHorizontal, sankeyJustify } from 'd3-sankey'; import React from 'react'; import type { ChartMargin } from '../types'; -import type { SankeyData, SankeyLinkExtended, SankeyNodeExtended } from './types'; +import type { SankeyChartData, SankeyChartLinkExtended, SankeyChartNodeExtended } from './types'; -import { calculateInnerSize } from 'toolkit/components/charts/utils/calculateInnerSize'; -import { useClientRect } from 'toolkit/hooks/useClientRect'; - -import { DEFAULT_SANKEY_NODE_PADDING, DEFAULT_SANKEY_NODE_WIDTH } from './constants'; +import { useClientRect } from '../../../hooks/useClientRect'; +import { calculateInnerSize } from '../utils/calculateInnerSize'; +import { SANKEY_NODE_DEFAULT_PADDING, SANKEY_NODE_DEFAULT_WIDTH } from './constants'; interface Props { - data: SankeyData; + data: SankeyChartData; margin?: ChartMargin; nodeWidth?: number; nodePadding?: number; } interface SankeyLayout { - nodes: Array; - links: Array; + nodes: Array; + links: Array; } interface UseSankeyControllerResult { @@ -26,9 +25,9 @@ interface UseSankeyControllerResult { readonly rect: DOMRect | null; readonly innerWidth: number; readonly innerHeight: number; - readonly nodes: Array; - readonly links: Array; - readonly linkPathGenerator: (link: SankeyLinkExtended) => string | null; + readonly nodes: Array; + readonly links: Array; + readonly linkPathGenerator: (link: SankeyChartLinkExtended) => string | null; } export function useSankeyController({ data, margin, nodeWidth, nodePadding }: Props): UseSankeyControllerResult { @@ -41,10 +40,10 @@ export function useSankeyController({ data, margin, nodeWidth, nodePadding }: Pr return { nodes: [], links: [] }; } - const sankeyGenerator = sankey() + const sankeyGenerator = sankey() .nodeId((d) => d.id) - .nodeWidth(nodeWidth ?? DEFAULT_SANKEY_NODE_WIDTH) - .nodePadding(nodePadding ?? DEFAULT_SANKEY_NODE_PADDING) + .nodeWidth(nodeWidth ?? SANKEY_NODE_DEFAULT_WIDTH) + .nodePadding(nodePadding ?? SANKEY_NODE_DEFAULT_PADDING) .nodeAlign(sankeyJustify) .extent([ [ 0, 0 ], [ innerWidth, innerHeight ] ]); @@ -54,10 +53,10 @@ export function useSankeyController({ data, margin, nodeWidth, nodePadding }: Pr }); // After layout, source/target on each link are resolved node objects, not ids. - return { nodes: nodes as Array, links: links as unknown as Array }; + return { nodes: nodes as Array, links: links as unknown as Array }; }, [ data, innerWidth, innerHeight, nodeWidth, nodePadding ]); - const linkPathGenerator: (link: SankeyLinkExtended) => string | null = React.useMemo(() => sankeyLinkHorizontal(), []); + const linkPathGenerator: (link: SankeyChartLinkExtended) => string | null = React.useMemo(() => sankeyLinkHorizontal(), []); return React.useMemo(() => ({ ref, diff --git a/toolkit/components/charts/types.ts b/toolkit/components/charts/types.ts new file mode 100644 index 0000000000..a98d04843c --- /dev/null +++ b/toolkit/components/charts/types.ts @@ -0,0 +1,39 @@ +export interface ChartMargin { + top?: number; + right?: number; + bottom?: number; + left?: number; +} + +export interface ChartOffset { + x?: number; + y?: number; +} + +import { Resolution as ResolutionEnum } from '@blockscout/stats-types'; + +export enum ChartResolution { + DAY = ResolutionEnum.DAY, + WEEK = ResolutionEnum.WEEK, + MONTH = ResolutionEnum.MONTH, + YEAR = ResolutionEnum.YEAR, +}; + +export const CHART_RESOLUTION_LABELS: Array<{ id: ChartResolution; title: string }> = [ + { + id: ChartResolution.DAY, + title: 'Day', + }, + { + id: ChartResolution.WEEK, + title: 'Week', + }, + { + id: ChartResolution.MONTH, + title: 'Month', + }, + { + id: ChartResolution.YEAR, + title: 'Year', + }, +]; diff --git a/toolkit/components/charts/types.tsx b/toolkit/components/charts/types.tsx deleted file mode 100644 index 5e6835212b..0000000000 --- a/toolkit/components/charts/types.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import type * as d3 from 'd3'; - -export { Resolution } from '@blockscout/stats-types'; -import { Resolution as ResolutionEnum } from '@blockscout/stats-types'; - -export const RESOLUTION_LABELS: Array<{ id: ResolutionEnum; title: string }> = [ - { - id: ResolutionEnum.DAY, - title: 'Day', - }, - { - id: ResolutionEnum.WEEK, - title: 'Week', - }, - { - id: ResolutionEnum.MONTH, - title: 'Month', - }, - { - id: ResolutionEnum.YEAR, - title: 'Year', - }, -]; - -export interface TimeChartItemRaw { - date: Date; - dateLabel?: string; - value: number | string | null; -} - -export interface TimeChartItem { - date: Date; - date_to?: Date; - dateLabel?: string; - value: number; - isApproximate?: boolean; -} - -export interface ChartMargin { - top?: number; - right?: number; - bottom?: number; - left?: number; -} - -export interface ChartOffset { - x?: number; - y?: number; -} - -export type ChartConfig = - | { - type: 'line'; - color: string; - strokeWidth?: number; - strokeDasharray?: string; - } | - { - type: 'area'; - gradient: { - startColor: string; - stopColor: string; - }; - }; - -export interface TimeChartDataItem { - id: string; - name: string; - items: Array; - charts: Array; - units?: string; - valueFormatter?: (value: number) => string; -} - -export type TimeChartData = Array; - -export interface AxisConfig { - ticks?: number; - nice?: boolean; - noLabel?: boolean; - scale?: { - min?: number; - }; - tickFormatter?: () => (d: d3.AxisDomain) => string; -} - -export interface AxesConfig { - x?: AxisConfig; - y?: AxisConfig; -} - -export type AxesConfigFn = (props: { - isEnlarged?: boolean; - isMobile?: boolean; -}) => AxesConfig; diff --git a/toolkit/components/forms/fields/FormFieldSwitch.tsx b/toolkit/components/forms/fields/FormFieldSwitch.tsx index a3f61ed6ce..da02e8418e 100644 --- a/toolkit/components/forms/fields/FormFieldSwitch.tsx +++ b/toolkit/components/forms/fields/FormFieldSwitch.tsx @@ -12,7 +12,7 @@ export type FormFieldSwitchProps< Name extends Path, > = Pick< FormFieldPropsBase, - 'name' | 'placeholder' | 'rules' | 'controllerProps' + 'name' | 'rules' | 'controllerProps' > & SwitchProps; @@ -21,11 +21,11 @@ const FormFieldSwitchContent = < Name extends Path, >({ name, - placeholder, onCheckedChange, rules, controllerProps, disabled, + children, ...rest }: FormFieldSwitchProps) => { const { control } = useFormContext(); @@ -51,7 +51,7 @@ const FormFieldSwitchContent = < inputProps={{ onBlur: field.onBlur }} { ...rest } > - { placeholder } + { children } ); }; diff --git a/toolkit/hooks/useClipboard.tsx b/toolkit/hooks/useClipboard.tsx index c7b8df6329..adcda63d9c 100644 --- a/toolkit/hooks/useClipboard.tsx +++ b/toolkit/hooks/useClipboard.tsx @@ -1,7 +1,7 @@ import { useCopyToClipboard } from '@uidotdev/usehooks'; import React from 'react'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; import { SECOND } from '../utils/consts'; import { useDisclosure } from './useDisclosure'; diff --git a/toolkit/hooks/useDisclosure.tsx b/toolkit/hooks/useDisclosure.tsx index 6e4cbc6f65..1bc2cb1633 100644 --- a/toolkit/hooks/useDisclosure.tsx +++ b/toolkit/hooks/useDisclosure.tsx @@ -3,10 +3,16 @@ import type { UseDisclosureProps } from '@chakra-ui/react'; import { useDisclosure as useDisclosureChakra } from '@chakra-ui/react'; import React from 'react'; +export interface OpenChangeDetails { + open: boolean; +} + +export type OnOpenChangeHandler = (details: OpenChangeDetails) => void; + export function useDisclosure(props?: UseDisclosureProps) { const { open, onOpen, onClose, onToggle } = useDisclosureChakra(props); - const onOpenChange = React.useCallback(({ open }: { open: boolean }) => { + const onOpenChange = React.useCallback(({ open }: OpenChangeDetails) => { if (open) { onOpen(); } else { diff --git a/toolkit/package/README.md b/toolkit/package/README.md index 3a5f608481..529ce17d7b 100644 --- a/toolkit/package/README.md +++ b/toolkit/package/README.md @@ -39,7 +39,7 @@ Ensure you have the following peer dependencies installed: "dayjs": ">=1.11.5", "dom-to-image": ">=2.6.0", "es-toolkit": ">=1.39.10", - "next": ">=16.1.7", + "next": ">=16.2.4", "next-themes": ">=0.4.4", "react": ">=18.3.1", "react-dom": ">=18.3.1", diff --git a/toolkit/package/package.json b/toolkit/package/package.json index 564fa1dae4..849ddcb987 100644 --- a/toolkit/package/package.json +++ b/toolkit/package/package.json @@ -43,7 +43,7 @@ "peerDependencies": { "@chakra-ui/react": ">=3.33.0", "@emotion/react": ">=11.14.0", - "next": ">=15.2.3", + "next": ">=16.2.4", "next-themes": ">=0.4.4", "react": ">=18.3.1", "react-dom": ">=18.3.1", @@ -56,7 +56,7 @@ "@types/react-dom": "18.3.1", "@vitejs/plugin-react": "4.3.4", "typescript": "5.9.2", - "vite": "6.4.1", + "vite": "6.4.2", "vite-plugin-dts": "4.5.4", "vite-plugin-svgr": "4.5.0", "vite-tsconfig-paths": "5.1.4" diff --git a/toolkit/package/src/index.ts b/toolkit/package/src/index.ts index 8b052ba2e7..3ba1397451 100644 --- a/toolkit/package/src/index.ts +++ b/toolkit/package/src/index.ts @@ -29,6 +29,7 @@ export * from '../../chakra/rating'; export * from '../../chakra/select'; export * from '../../chakra/skeleton'; export * from '../../chakra/slider'; +export * from '../../chakra/status'; export * from '../../chakra/switch'; export * from '../../chakra/table'; export * from '../../chakra/tabs'; diff --git a/toolkit/package/tsconfig.json b/toolkit/package/tsconfig.json index 3b031e41d7..c02b8531ff 100644 --- a/toolkit/package/tsconfig.json +++ b/toolkit/package/tsconfig.json @@ -18,6 +18,7 @@ "declarationDir": "./dist", "baseUrl": "..", "paths": { + "client/*": ["../client/*"], "configs/*": ["../configs/*"], "lib/*": ["../lib/*"], "types/*": ["../types/*"], diff --git a/toolkit/theme/foundations/semanticTokens.ts b/toolkit/theme/foundations/semanticTokens.ts index 55d9bf1741..0765368dc3 100644 --- a/toolkit/theme/foundations/semanticTokens.ts +++ b/toolkit/theme/foundations/semanticTokens.ts @@ -283,7 +283,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { bg: { DEFAULT: { value: '{colors.alert.bg.info}' }, info: { value: { _light: '{colors.blue.100}', _dark: '{colors.blue.900}' } }, - warning: { value: '{colors.alert.bg.warning}' }, + warning: { value: { _light: '{colors.orange.100}', _dark: '{colors.orange.900}' } }, success: { value: '{colors.alert.bg.success}' }, error: { value: '{colors.alert.bg.error}' }, loading: { value: { _light: '{colors.blue.100}', _dark: '{colors.blue.900}' } }, diff --git a/toolkit/theme/recipes/badge.recipe.ts b/toolkit/theme/recipes/badge.recipe.ts index 681f8a750b..007bbb8c74 100644 --- a/toolkit/theme/recipes/badge.recipe.ts +++ b/toolkit/theme/recipes/badge.recipe.ts @@ -115,7 +115,6 @@ export const recipe = defineRecipe({ sm: { textStyle: 'xs', p: '1', - h: '4.5', minH: '4.5', }, md: { diff --git a/toolkit/theme/recipes/button.recipe.ts b/toolkit/theme/recipes/button.recipe.ts index bbc85c28bd..7f200c6d57 100644 --- a/toolkit/theme/recipes/button.recipe.ts +++ b/toolkit/theme/recipes/button.recipe.ts @@ -33,14 +33,14 @@ export const recipe = defineRecipe({ }, solid_danger: { bg: 'red.600', - color: 'button.solid.text', + color: 'white', _hover: { bg: 'red.500', }, _loading: { opacity: 1, '& .chakra-spinner': { - borderColor: 'button.solid.text', + borderColor: 'white', borderBottomColor: 'spinner.track', borderInlineStartColor: 'spinner.track', }, @@ -318,6 +318,14 @@ export const recipe = defineRecipe({ _expanded: { color: 'hover', }, + _loading: { + opacity: 1, + '& .chakra-spinner': { + borderColor: 'selected.option.bg', + borderBottomColor: 'spinner.track', + borderInlineStartColor: 'spinner.track', + }, + }, }, pagination: { borderWidth: '2px', diff --git a/toolkit/theme/recipes/checkbox.recipe.ts b/toolkit/theme/recipes/checkbox.recipe.ts index 9b9ef6a639..9436c6826b 100644 --- a/toolkit/theme/recipes/checkbox.recipe.ts +++ b/toolkit/theme/recipes/checkbox.recipe.ts @@ -35,6 +35,11 @@ export const recipe = defineSlotRecipe({ variants: { size: { + sm: { + root: { gap: '2' }, + label: { textStyle: 'sm' }, + control: checkmarkRecipe.variants?.size?.md, + }, md: { root: { gap: '2' }, label: { textStyle: 'md' }, diff --git a/toolkit/theme/recipes/code.recipe.ts b/toolkit/theme/recipes/code.recipe.ts index f854dfb996..e29e1b0b82 100644 --- a/toolkit/theme/recipes/code.recipe.ts +++ b/toolkit/theme/recipes/code.recipe.ts @@ -1,20 +1,34 @@ import { defineRecipe } from '@chakra-ui/react'; -import { recipe as badgeRecipe } from './badge.recipe'; - -const { variants, defaultVariants } = badgeRecipe; - export const recipe = defineRecipe({ className: 'chakra-code', base: { fontFamily: 'mono', alignItems: 'center', display: 'inline-flex', - borderRadius: 'l2', + borderRadius: 'sm', + }, + variants: { + variant: { + subtle: {}, + }, + colorPalette: { + gray: { + bg: 'badge.gray.bg', + color: 'badge.gray.fg', + }, + }, + size: { + sm: { + textStyle: 'xs', + px: 1, + py: 0.5, + }, + }, }, - variants, defaultVariants: { - ...defaultVariants, + variant: 'subtle', + colorPalette: 'gray', size: 'sm', }, }); diff --git a/toolkit/theme/recipes/field.recipe.ts b/toolkit/theme/recipes/field.recipe.ts index e08b5f97bb..25e9bdd235 100644 --- a/toolkit/theme/recipes/field.recipe.ts +++ b/toolkit/theme/recipes/field.recipe.ts @@ -31,12 +31,13 @@ export const recipe = defineSlotRecipe({ }, }, errorText: { - display: 'inline-flex', - alignItems: 'center', + display: 'block', fontWeight: 'medium', - gap: '1', color: 'input.fg.error', textStyle: 'sm', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', }, helperText: { color: 'fg.muted', @@ -112,17 +113,16 @@ export const recipe = defineSlotRecipe({ floating: true, css: { label: { - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - padding: '10px 16px 0px 16px', + // 4px = border width + width: 'calc(100% - var(--group-end-element-width, 0px) - 4px)', + padding: '8px var(--input-label-padding-end, 16px) 0px var(--input-label-padding-start, 16px)', textStyle: 'xs', _peerPlaceholderShown: { - padding: '16px', + padding: '16px var(--input-label-padding-end, 16px) 0px var(--input-label-padding-start, 16px)', textStyle: 'md', }, _peerFocusVisible: { - padding: '10px 16px 0px 16px', + padding: '8px var(--input-label-padding-end, 16px) 0px var(--input-label-padding-start, 16px)', textStyle: 'xs', }, _readOnly: { @@ -146,7 +146,7 @@ export const recipe = defineSlotRecipe({ // 20px = scrollbar width // 4px = border width width: 'calc(100% - 4px - 20px)', - padding: '16px 16px 0px 16px', + padding: '16px 0px 0px 16px', textStyle: 'xs', borderTopRightRadius: '0px', borderBottomRightRadius: '0px', diff --git a/toolkit/theme/recipes/index.ts b/toolkit/theme/recipes/index.ts index ca388a475d..b32be65ab3 100644 --- a/toolkit/theme/recipes/index.ts +++ b/toolkit/theme/recipes/index.ts @@ -26,6 +26,7 @@ import { recipe as separator } from './separator.recipe'; import { recipe as skeleton } from './skeleton.recipe'; import { recipe as spinner } from './spinner.recipe'; import { recipe as stat } from './stat.recipe'; +import { recipe as status } from './status.recipe'; import { recipe as switchRecipe } from './switch.recipe'; import { recipe as table } from './table.recipe'; import { recipe as tabs } from './tabs.recipe'; @@ -67,6 +68,7 @@ export const slotRecipes = { ratingGroup, select, stat, + status, 'switch': switchRecipe, table, tabs, diff --git a/toolkit/theme/recipes/input.recipe.ts b/toolkit/theme/recipes/input.recipe.ts index 182a96ae53..11949fcd22 100644 --- a/toolkit/theme/recipes/input.recipe.ts +++ b/toolkit/theme/recipes/input.recipe.ts @@ -7,6 +7,16 @@ export const recipe = defineRecipe({ outline: '0', position: 'relative', appearance: 'textfield', + + // fix for datetime-local input in mobile Safari + '&[type="datetime-local"]': { + appearance: 'none', + }, + '&::-webkit-date-and-time-value': { + textAlign: 'left', + }, + + textOverflow: 'ellipsis', textAlign: 'start', borderRadius: 'base', height: 'var(--input-height)', @@ -110,7 +120,7 @@ export const recipe = defineRecipe({ size: 'lg', floating: true, css: { - padding: '26px 10px 10px 16px', + padding: '24px 10px 8px 16px', }, }, ], diff --git a/toolkit/theme/recipes/popover.recipe.ts b/toolkit/theme/recipes/popover.recipe.ts index 5dbd279f05..3c218881d7 100644 --- a/toolkit/theme/recipes/popover.recipe.ts +++ b/toolkit/theme/recipes/popover.recipe.ts @@ -40,6 +40,7 @@ export const recipe = defineSlotRecipe({ body: { padding: 'var(--popover-padding)', flex: '1', + overflowY: 'auto', }, footer: { display: 'flex', diff --git a/toolkit/theme/recipes/radio-group.recipe.ts b/toolkit/theme/recipes/radio-group.recipe.ts index 4e925984a0..4b593d7ad2 100644 --- a/toolkit/theme/recipes/radio-group.recipe.ts +++ b/toolkit/theme/recipes/radio-group.recipe.ts @@ -41,8 +41,14 @@ export const recipe = defineSlotRecipe({ }, size: { + sm: { + item: { gap: '2' }, + itemText: { textStyle: 'sm' }, + itemControl: radiomarkRecipe.variants?.size?.md, + }, md: { - item: { textStyle: 'md', gap: '2' }, + item: { gap: '2' }, + itemText: { textStyle: 'md' }, itemControl: radiomarkRecipe.variants?.size?.md, }, }, diff --git a/toolkit/theme/recipes/status.recipe.ts b/toolkit/theme/recipes/status.recipe.ts new file mode 100644 index 0000000000..b2bac4d803 --- /dev/null +++ b/toolkit/theme/recipes/status.recipe.ts @@ -0,0 +1,44 @@ +import { defineSlotRecipe } from '@chakra-ui/react'; + +export const recipe = defineSlotRecipe({ + className: 'chakra-status', + slots: [ 'root', 'indicator' ], + + base: { + root: { + display: 'block', + borderRadius: 'full', + borderColor: 'bg.primary', + bg: 'orange.400', + forcedColorAdjust: 'none', + flexShrink: 0, + }, + }, + + variants: { + size: { + xs: { + root: { + boxSize: '6px', + borderWidth: '1px', + }, + }, + sm: { + root: { + boxSize: '8px', + borderWidth: '1px', + }, + }, + md: { + root: { + boxSize: '10px', + borderWidth: '1px', + }, + }, + }, + }, + + defaultVariants: { + size: 'md', + }, +}); diff --git a/toolkit/utils/file.ts b/toolkit/utils/file.ts index 979ff0f081..31f813d6be 100644 --- a/toolkit/utils/file.ts +++ b/toolkit/utils/file.ts @@ -11,11 +11,8 @@ export function downloadBlob(blob: Blob, filename: string) { URL.revokeObjectURL(url); } -export function saveAsCsv(headerRows: Array, dataRows: Array>, filename: string) { - const csv = unparse([ - headerRows, - ...dataRows, - ]); +export function saveAsCsv(data: Array>, filename: string) { + const csv = unparse(data); const blob = new Blob([ csv ], { type: 'text/csv;charset=utf-8;' }); diff --git a/tools/preset-sync/index.ts b/tools/preset-sync/index.ts index 7c7351be87..463a70f02c 100755 --- a/tools/preset-sync/index.ts +++ b/tools/preset-sync/index.ts @@ -18,7 +18,7 @@ const PRESETS = { mega_eth: 'https://megaeth.blockscout.com', mekong: 'https://mekong.blockscout.com', multichain_dev: 'https://superchain.k8s-dev.blockscout.com', - // multichain_prod: 'https://multichain.blockscout.com', + multichain_prod: 'https://explorer.blockscout.com', neon_devnet: 'https://neon-devnet.blockscout.com', numine: 'https://numine.blockscout.com', optimism: 'https://optimism.blockscout.com', @@ -34,7 +34,6 @@ const PRESETS = { tac_spb: 'https://spb.explorer.tac.build', zetachain: 'https://zetascan.com', zetachain_testnet: 'https://testnet.zetascan.com', - zkevm: 'https://polygon-cdk-stavanger.blockscout.com', zksync: 'https://zksync.blockscout.com', zilliqa: 'https://zilliqa.blockscout.com', zora: 'https://explorer.zora.energy', diff --git a/tools/scripts/dev.preset.sh b/tools/scripts/dev.preset.sh index eff11bdcc6..e30af75546 100755 --- a/tools/scripts/dev.preset.sh +++ b/tools/scripts/dev.preset.sh @@ -40,6 +40,10 @@ dotenv \ source ./deploy/scripts/build_sprite.sh echo "" +# generate routes +pnpm routes:generate +echo "" + # generate envs.js file and run the app dotenv \ -v NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) \ diff --git a/tools/scripts/dev.sh b/tools/scripts/dev.sh index ede3135d55..1d2606efc8 100755 --- a/tools/scripts/dev.sh +++ b/tools/scripts/dev.sh @@ -16,6 +16,10 @@ dotenv \ source ./deploy/scripts/build_sprite.sh echo "" +# generate routes +pnpm routes:generate +echo "" + # generate envs.js file and run the app dotenv \ -v NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) \ diff --git a/tools/scripts/og-image-generator.dev.sh b/tools/scripts/og-image-generator.dev.sh index a47f2ac536..c469818051 100755 --- a/tools/scripts/og-image-generator.dev.sh +++ b/tools/scripts/og-image-generator.dev.sh @@ -2,7 +2,7 @@ # use this script for testing the og image generator -config_file="./configs/envs/.env.zkevm" +config_file="./configs/envs/.env.eth" dotenv \ -e $config_file \ diff --git a/types/api/account.ts b/types/api/account.ts index ae775c731e..61d50729d4 100644 --- a/types/api/account.ts +++ b/types/api/account.ts @@ -1,6 +1,6 @@ import type { Abi } from 'viem'; -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export interface AddressTag { address_hash: string; address: AddressParam; diff --git a/types/api/addressParams.ts b/types/api/addressParams.ts deleted file mode 100644 index 20608b200c..0000000000 --- a/types/api/addressParams.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { AddressMetadataTagApi } from './addressMetadata'; -import type { SmartContractProxyType } from './contract'; -import type { TokenReputation } from './token'; - -export interface AddressImplementation { - address_hash: string; - filecoin_robust_address?: string | null; - name?: string | null; -} - -export interface AddressTag { - label: string; - display_name: string; - address_hash: string; -} - -export interface WatchlistName { - label: string; - display_name: string; -} - -export type AddressFilecoinParams = { - actor_type?: FilecoinActorType; - id?: string | null; - robust?: string | null; -}; - -export type FilecoinActorType = - 'account' | - 'cron' | - 'datacap' | - 'eam' | - 'ethaccount' | - 'evm' | - 'init' | - 'market' | - 'miner' | - 'multisig' | - 'paych' | - 'placeholder' | - 'power' | - 'reward' | - 'system' | - 'verifreg'; - -export interface UserTags { - private_tags: Array | null; - watchlist_names: Array | null; - public_tags: Array | null; -} - -export type AddressParamBasic = { - hash: string; - implementations: Array | null; - name: string | null; - is_contract: boolean; - is_verified: boolean | null; - ens_domain_name: string | null; - metadata?: { - reputation: number | null; - tags: Array; - } | null; - filecoin?: AddressFilecoinParams; - proxy_type?: SmartContractProxyType | null; - reputation?: TokenReputation; -}; - -export type AddressParam = UserTags & AddressParamBasic; diff --git a/types/api/addresses.ts b/types/api/addresses.ts deleted file mode 100644 index e220a99e23..0000000000 --- a/types/api/addresses.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { AddressParam } from './addressParams'; - -export type AddressesItem = AddressParam & { transactions_count: string; coin_balance: string | null }; - -export type AddressesResponse = { - items: Array; - next_page_params: { - fetched_coin_balance: string; - hash: string; - items_count: number; - } | null; - total_supply: string; -}; - -export interface AddressesMetadataSearchResult { - items: Array; - next_page_params: null; -} - -export interface AddressesMetadataSearchFilters { - slug: string; - tag_type: string; -} diff --git a/types/api/advancedFilter.ts b/types/api/advancedFilter.ts index 7ecb6cdae5..43181078b2 100644 --- a/types/api/advancedFilter.ts +++ b/types/api/advancedFilter.ts @@ -1,4 +1,5 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; + import type { TokenInfo, TokenType } from './token'; export const ADVANCED_FILTER_ADDRESS_RELATION = [ 'or', 'and' ] as const; diff --git a/types/api/arbitrumL2.ts b/types/api/arbitrumL2.ts index fe53325fe0..a3beeccbe3 100644 --- a/types/api/arbitrumL2.ts +++ b/types/api/arbitrumL2.ts @@ -1,5 +1,5 @@ -import type { Block } from './block'; -import type { Transaction } from './transaction'; +import type { Block } from 'client/slices/block/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; export interface ArbitrumLatestDepositsItem { completion_transaction_hash: string; diff --git a/types/api/block.ts b/types/api/block.ts deleted file mode 100644 index 1e7640f4ec..0000000000 --- a/types/api/block.ts +++ /dev/null @@ -1,170 +0,0 @@ -import type { AddressParam } from 'types/api/addressParams'; -import type { Reward } from 'types/api/reward'; -import type { Transaction } from 'types/api/transaction'; - -import type { ArbitrumBatchStatus, ArbitrumL2TxData } from './arbitrumL2'; -import type { InternalTransaction } from './internalTransaction'; -import type { OptimisticL2BatchDataContainer, OptimisticL2BlobTypeEip4844, OptimisticL2BlobTypeCelestia } from './optimisticL2'; -import type { TokenInfo } from './token'; -import type { ZkSyncBatchesItem } from './zkSyncL2'; - -export type BlockType = 'block' | 'reorg' | 'uncle'; - -export interface BlockBaseFeeCelo { - amount: string; - breakdown: Array<{ amount: string; percentage: number; address: AddressParam }>; - recipient: AddressParam; - token: TokenInfo; -} - -export interface Block { - height: number; - timestamp: string; - transactions_count: number; - internal_transactions_count: number; - miner: AddressParam; - size?: number; - hash: string; - parent_hash: string; - difficulty?: string; - total_difficulty?: string | null; - gas_used: string | null; - gas_limit: string; - nonce: string; - base_fee_per_gas?: string | null; - burnt_fees: string | null; - priority_fee: string | null; - extra_data: string | null; - state_root: string | null; - rewards?: Array; - gas_target_percentage: number | null; - gas_used_percentage: number | null; - burnt_fees_percentage: number | null; - type: BlockType; - transaction_fees: string | null; - uncles_hashes: Array; - withdrawals_count?: number; - beacon_deposits_count?: number; - is_pending_update?: boolean; - // ROOTSTOCK FIELDS - bitcoin_merged_mining_coinbase_transaction?: string | null; - bitcoin_merged_mining_header?: string | null; - bitcoin_merged_mining_merkle_proof?: string | null; - hash_for_merged_mining?: string | null; - minimum_gas_price?: string | null; - // BLOB FIELDS - blob_gas_price?: string; - blob_gas_used?: string; - burnt_blob_fees?: string; - excess_blob_gas?: string; - blob_transactions_count?: number; - // ZKSYNC FIELDS - zksync?: Omit & { - batch_number: number | null; - }; - arbitrum?: ArbitrumBlockData; - optimism?: OptimismBlockData; - // CELO FIELDS - celo?: { - epoch_number: number; - l1_era_finalized_epoch_number: number | null; - base_fee?: BlockBaseFeeCelo; - }; - // ZILLIQA FIELDS - zilliqa?: ZilliqaBlockData; -} - -type ArbitrumBlockData = { - batch_number: number; - commitment_transaction: ArbitrumL2TxData; - confirmation_transaction: ArbitrumL2TxData; - delayed_messages: number; - l1_block_number: number; - send_count: number | null; - send_root: string; - status: ArbitrumBatchStatus; -}; - -export interface OptimismBlockData { - batch_data_container: OptimisticL2BatchDataContainer; - number: number; - blobs: Array | Array | null; - l1_timestamp: string; - l1_transaction_hashes: Array; -} - -export interface ZilliqaBlockData { - view: number; - quorum_certificate: ZilliqaQuorumCertificate; - aggregate_quorum_certificate: (ZilliqaQuorumCertificate & { - nested_quorum_certificates: Array; - }) | null; -} - -export interface ZilliqaQuorumCertificate { - view: number; - signature: string; - signers: Array; -} - -export interface ZilliqaNestedQuorumCertificate extends ZilliqaQuorumCertificate { - proposed_by_validator_index: number; -} - -export interface BlocksResponse { - items: Array; - next_page_params: { - block_number: number; - items_count: number; - } | null; -} - -export interface BlockTransactionsResponse { - items: Array; - next_page_params: { - block_number: number; - items_count: number; - index: number; - } | null; -} - -export interface BlockInternalTransactionsResponse { - items: Array; - next_page_params: { - block_index: number; - items_count: number; - } | null; -} - -export interface NewBlockSocketResponse { - average_block_time: string; - block: Block; -} - -export interface BlockFilters { - type?: BlockType; -} - -export type BlockWithdrawalsResponse = { - items: Array; - next_page_params: { - index: number; - items_count: number; - } | null; -}; - -export type BlockWithdrawalsItem = { - amount: string; - index: number; - receiver: AddressParam; - validator_index: number; -}; - -export interface BlockCountdownResponse { - result: { - CountdownBlock: string; - CurrentBlock: string; - EstimateTimeInSec: string; - RemainingBlock: string; - } | null; -} diff --git a/types/api/configs.ts b/types/api/configs.ts index 97aee5edce..9969adbdb1 100644 --- a/types/api/configs.ts +++ b/types/api/configs.ts @@ -7,10 +7,6 @@ export interface BackendVersionConfig { backend_version: string; } -export interface CsvExportConfig { - limit: number; -} - export interface CeloConfig { l2_migration_block: number; } diff --git a/types/api/contract.ts b/types/api/contract.ts index d78566d824..764cf7db2e 100644 --- a/types/api/contract.ts +++ b/types/api/contract.ts @@ -1,6 +1,6 @@ import type { Abi, AbiType } from 'abitype'; -import type { AddressImplementation } from './addressParams'; +import type { AddressImplementation } from 'client/slices/address/types/api'; export type SmartContractMethodArgType = AbiType; export type SmartContractMethodStateMutability = 'view' | 'nonpayable' | 'payable'; diff --git a/types/api/contracts.ts b/types/api/contracts.ts index ea6aeda8ac..01edd3cdc5 100644 --- a/types/api/contracts.ts +++ b/types/api/contracts.ts @@ -1,4 +1,5 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; + import type { SmartContractLicenseType } from './contract'; export type VerifiedContractsLanguage = 'solidity' | 'vyper' | 'yul' | 'scilla' | 'stylus_rust' | 'geas'; diff --git a/types/api/decodedInput.ts b/types/api/decodedInput.ts deleted file mode 100644 index d390de03e0..0000000000 --- a/types/api/decodedInput.ts +++ /dev/null @@ -1,12 +0,0 @@ -export interface DecodedInput { - method_call: string; - method_id: string; - parameters: Array; -} - -export interface DecodedInputParams { - name: string; - type: string; - value: string | Array | Record; - indexed?: boolean; -} diff --git a/types/api/deposits.ts b/types/api/deposits.ts index a7f3a97a89..aaa929d610 100644 --- a/types/api/deposits.ts +++ b/types/api/deposits.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export type DepositStatus = 'pending' | 'invalid' | 'completed'; diff --git a/types/api/epochs.ts b/types/api/epochs.ts index 0372b7f48e..3721a8f2c9 100644 --- a/types/api/epochs.ts +++ b/types/api/epochs.ts @@ -1,4 +1,5 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; + import type { TokenInfo } from './token'; import type { Erc20TotalPayload, TokenTransfer } from './tokenTransfer'; diff --git a/types/api/fee.ts b/types/api/fee.ts deleted file mode 100644 index 82ad5c5b8b..0000000000 --- a/types/api/fee.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Fee { - type: string; - value: string | null; -} diff --git a/types/api/fheOperations.ts b/types/api/fheOperations.ts index f196198a46..33499a3562 100644 --- a/types/api/fheOperations.ts +++ b/types/api/fheOperations.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export type FheOperationType = | 'arithmetic' | diff --git a/types/api/interchainIndexer.ts b/types/api/interchainIndexer.ts deleted file mode 100644 index 5b9f651043..0000000000 --- a/types/api/interchainIndexer.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface CrossChainMessageFilters { - q?: string; -} - -export interface CrossChainTransferFilters { - q?: string; -} diff --git a/types/api/internalTransaction.ts b/types/api/internalTransaction.ts index fe47e28bb8..ba4d7deadd 100644 --- a/types/api/internalTransaction.ts +++ b/types/api/internalTransaction.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export type TxInternalsType = 'call' | 'delegatecall' | 'staticcall' | 'create' | 'create2' | 'selfdestruct' | 'reward'; diff --git a/types/api/mudWorlds.ts b/types/api/mudWorlds.ts index 704b91e15f..82ad177b76 100644 --- a/types/api/mudWorlds.ts +++ b/types/api/mudWorlds.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export type MudWorldsResponse = { items: Array; diff --git a/types/api/optimisticL2.ts b/types/api/optimisticL2.ts index 7a6064ee54..1e8b017c7b 100644 --- a/types/api/optimisticL2.ts +++ b/types/api/optimisticL2.ts @@ -1,6 +1,6 @@ -import type { AddressParam } from './addressParams'; -import type { Block } from './block'; -import type { Transaction } from './transaction'; +import type { AddressParam } from 'client/slices/address/types/api'; +import type { Block } from 'client/slices/block/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; export type OptimisticL2DepositsItem = { l1_block_number: number; diff --git a/types/api/rawTrace.ts b/types/api/rawTrace.ts deleted file mode 100644 index b46a844d8d..0000000000 --- a/types/api/rawTrace.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface RawTrace { - action: { - callType: string; - from: string; - gas: string; - input: string; - to: string; - value: string; - }; - result: { - gasUsed: string; - output: string; - }; - error: string | null; - subtraces: number; - traceAddress: Array; - type: string; -} - -export type RawTracesResponse = Array; diff --git a/types/api/scrollL2.ts b/types/api/scrollL2.ts index ae93055fcb..842e195af7 100644 --- a/types/api/scrollL2.ts +++ b/types/api/scrollL2.ts @@ -1,5 +1,5 @@ -import type { Block } from './block'; -import type { Transaction } from './transaction'; +import type { Block } from 'client/slices/block/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; export interface ScrollL2BatchesResponse { items: Array; diff --git a/types/api/search.ts b/types/api/search.ts index 43455e66a6..5b2d7b9464 100644 --- a/types/api/search.ts +++ b/types/api/search.ts @@ -1,9 +1,8 @@ import type * as bens from '@blockscout/bens-types'; import type * as tac from '@blockscout/tac-operation-lifecycle-types'; +import type { AddressMetadataTagApi } from 'client/features/address-metadata/types/api'; import type { TokenReputation, TokenType } from 'types/api/token'; -import type { AddressMetadataTagApi } from './addressMetadata'; - export const SEARCH_RESULT_TYPES = { token: 'token', address: 'address', diff --git a/types/api/shibarium.ts b/types/api/shibarium.ts index d39ee9cf91..4b00d6c152 100644 --- a/types/api/shibarium.ts +++ b/types/api/shibarium.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export type ShibariumDepositsItem = { l1_block_number: number; diff --git a/types/api/token.ts b/types/api/token.ts index 5f6e8770ae..a6a52121bf 100644 --- a/types/api/token.ts +++ b/types/api/token.ts @@ -1,5 +1,6 @@ +import type { AddressParam } from 'client/slices/address/types/api'; + import type { TokenInfoApplication } from './account'; -import type { AddressParam } from './addressParams'; export type NFTTokenType = 'ERC-721' | 'ERC-1155' | 'ERC-404'; // token type can come from the environment config, so it can be any string diff --git a/types/api/tokenTransfer.ts b/types/api/tokenTransfer.ts index 7818a12264..2e23d61c37 100644 --- a/types/api/tokenTransfer.ts +++ b/types/api/tokenTransfer.ts @@ -1,4 +1,5 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; + import type { TokenInfo, TokenInstance, TokenType } from './token'; export type Erc20TotalPayload = { diff --git a/types/api/transaction.ts b/types/api/transaction.ts deleted file mode 100644 index 7d81e17a4f..0000000000 --- a/types/api/transaction.ts +++ /dev/null @@ -1,235 +0,0 @@ -import type { AddressParam } from './addressParams'; -import type { ArbitrumBatchStatus, ArbitrumL2TxData } from './arbitrumL2'; -import type { BlockTransactionsResponse } from './block'; -import type { DecodedInput } from './decodedInput'; -import type { Fee } from './fee'; -import type { ChainInfo, MessageStatus } from './interop'; -import type { OptimisticL2WithdrawalClaimInfo, OptimisticL2WithdrawalStatus } from './optimisticL2'; -import type { ScrollL2BlockStatus } from './scrollL2'; -import type { TokenInfo } from './token'; -import type { TokenTransfer } from './tokenTransfer'; -import type { TxAction } from './txAction'; -import type { ZkSyncBatchesItem } from './zkSyncL2'; - -export type TransactionRevertReason = { - raw: string; -} | DecodedInput; - -export type WrappedTransactionFields = 'decoded_input' | 'fee' | 'gas_limit' | 'gas_price' | 'hash' | 'max_fee_per_gas' | -'max_priority_fee_per_gas' | 'method' | 'nonce' | 'raw_input' | 'to' | 'type' | 'value'; - -export interface OpWithdrawal extends OptimisticL2WithdrawalClaimInfo { - l1_transaction_hash: string; - nonce: number; - status: OptimisticL2WithdrawalStatus; -} - -export type Transaction = { - to: AddressParam | null; - created_contract: AddressParam | null; - hash: string; - result: string; - confirmations: number; - status: 'ok' | 'error' | null | undefined; - block_number: number | null; - timestamp: string | null; - confirmation_duration: Array | null; - from: AddressParam; - value: string; - fee: Fee; - gas_price: string | null; - type: number | null; - gas_used: string | null; - gas_limit: string; - max_fee_per_gas: string | null; - max_priority_fee_per_gas: string | null; - priority_fee: string | null; - base_fee_per_gas: string | null; - transaction_burnt_fee: string | null; - nonce: number; - position: number | null; - revert_reason: TransactionRevertReason | null; - raw_input: string; - decoded_input: DecodedInput | null; - token_transfers: Array | null; - token_transfers_overflow: boolean; - exchange_rate: string | null; - historic_exchange_rate: string | null; - method: string | null; - transaction_types: Array; - transaction_tag: string | null; - actions: Array; - l1_fee?: string; - l1_fee_scalar?: string; - l1_gas_price?: string; - l1_gas_used?: string; - has_error_in_internal_transactions: boolean | null; - is_pending_update?: boolean; - // optimism fields - op_withdrawals?: Array; - operator_fee?: string; - // SUAVE fields - execution_node?: AddressParam | null; - allowed_peekers?: Array; - wrapped?: Pick; - // Stability fields - stability_fee?: { - dapp_address: AddressParam; - dapp_fee: string; - token: TokenInfo; - total_fee: string; - validator_address: AddressParam; - validator_fee: string; - }; - // Celo fields - celo?: { - gas_token: TokenInfo | null; - }; - // zkEvm fields - zkevm_verify_hash?: string; - zkevm_batch_number?: number; - zkevm_status?: typeof ZKEVM_L2_TX_STATUSES[number]; - zkevm_sequence_hash?: string; - // zkSync FIELDS - zksync?: Omit & { - batch_number: number | null; - }; - // Zilliqa fields - zilliqa?: { - is_scilla: boolean; - }; - // blob tx fields - blob_versioned_hashes?: Array; - blob_gas_used?: string; - blob_gas_price?: string; - burnt_blob_fee?: string; - max_fee_per_blob_gas?: string; - arbitrum?: ArbitrumTransactionData; - scroll?: ScrollTransactionData; - // EIP-7702 - authorization_list?: Array; - // Interop - op_interop_messages?: Array; - // FHE operations - fhe_operations_count?: number; -}; - -type ArbitrumTransactionData = { - batch_number: number; - commitment_transaction: ArbitrumL2TxData; - confirmation_transaction: ArbitrumL2TxData; - contains_message: 'incoming' | 'outcoming' | null; - gas_used_for_l1: string; - gas_used_for_l2: string; - network_fee: string; - poster_fee: string; - status: ArbitrumBatchStatus; - message_related_info: { - associated_l1_transaction_hash: string | null; - message_status: ArbitrumTransactionMessageStatus; - }; -}; - -export type ArbitrumTransactionMessageStatus = 'Relayed' | 'Syncing with base layer' | 'Waiting for confirmation' | 'Ready for relay' | 'Settlement pending'; - -export const ZKEVM_L2_TX_STATUSES = [ 'Confirmed by Sequencer', 'L1 Confirmed' ]; - -export interface TransactionsStats { - pending_transactions_count: string; - transaction_fees_avg_24h: string; - transaction_fees_sum_24h: string; - transactions_count_24h: string; -} - -export type TransactionsResponse = TransactionsResponseValidated | TransactionsResponsePending; - -export interface TransactionsResponseValidated { - items: Array; - next_page_params: { - block_number: number; - index: number; - items_count: number; - filter: 'validated'; - } | null; -} - -export interface TransactionsResponsePending { - items: Array; - next_page_params: { - inserted_at: string; - hash: string; - filter: 'pending'; - } | null; -} - -export interface TransactionsResponseWithBlobs { - items: Array; - next_page_params: { - block_number: number; - index: number; - items_count: number; - } | null; -} - -export interface TransactionsResponseWatchlist { - items: Array; - next_page_params: { - block_number: number; - index: number; - items_count: 50; - } | null; -} - -export type TransactionType = 'rootstock_remasc' | -'rootstock_bridge' | -'token_transfer' | -'contract_creation' | -'contract_call' | -'token_creation' | -'coin_transfer' | -'blob_transaction'; - -export type TxsResponse = TransactionsResponseValidated | TransactionsResponsePending | BlockTransactionsResponse; - -export interface TransactionsSorting { - sort: 'value' | 'fee' | 'block_number'; - order: 'asc' | 'desc'; -} - -export type TransactionsSortingField = TransactionsSorting['sort']; - -export type TransactionsSortingValue = `${ TransactionsSortingField }-${ TransactionsSorting['order'] }` | 'default'; - -export type ScrollTransactionData = { - l1_fee: string; - l2_fee: Fee; - l1_fee_commit_scalar: number; - l1_base_fee: number; - l1_blob_base_fee: number; - l1_fee_scalar: number; - l1_fee_overhead: number; - l1_fee_blob_scalar: number; - l1_gas_used: number; - l2_block_status: ScrollL2BlockStatus; - queue_index: number; -}; - -export interface TxAuthorization { - address_hash: string; - authority: string; - chain_id: number; - nonce: number; - status: 'ok' | 'invalid_chain_id' | 'invalid_nonce' | 'invalid_signature' | null; -} - -export interface InteropTransactionInfo { - nonce: number; - payload: string; - init_chain?: ChainInfo | null; - relay_chain?: ChainInfo | null; - init_transaction_hash?: string; - relay_transaction_hash?: string; - sender_address_hash: string; - status: MessageStatus; - target_address_hash: string; -} diff --git a/types/api/txInterpretation.ts b/types/api/txInterpretation.ts index dd4dbb1e48..7407b70c12 100644 --- a/types/api/txInterpretation.ts +++ b/types/api/txInterpretation.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from 'types/api/addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; import type { TokenInfo } from 'types/api/token'; export interface TxInterpretationResponse { diff --git a/types/api/txStateChanges.ts b/types/api/txStateChanges.ts deleted file mode 100644 index 342bbc1908..0000000000 --- a/types/api/txStateChanges.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { AddressParam } from './addressParams'; -import type { TokenInfo } from './token'; -import type { Erc721TotalPayload } from './tokenTransfer'; - -export type TxStateChange = (TxStateChangeCoin | TxStateChangeToken) & { - address: AddressParam; - is_miner: boolean; - balance_before: string | null; - balance_after: string | null; -}; - -export interface TxStateChangeCoin { - type: 'coin'; - change: string; - token: null; -} - -export type TxStateChangeToken = TxStateChangeTokenErc20 | TxStateChangeTokenErc721 | TxStateChangeTokenErc1155; - -type ChangeDirection = 'from' | 'to'; - -export interface TxStateChangeTokenErc20 { - type: 'token'; - token: TokenInfo; - change: string; -} - -export interface TxStateChangeTokenErc721 { - type: 'token'; - token: TokenInfo; - change: Array<{ - direction: ChangeDirection; - total: Erc721TotalPayload; - }>; -} - -export interface TxStateChangeTokenErc1155 { - type: 'token'; - token: TokenInfo; - change: string; - token_id: string; -} - -export interface TxStateChangeTokenErc404 { - type: 'token'; - token: TokenInfo; - change: string; - token_id: string; -} - -export type TxStateChanges = { - items: Array; - next_page_params: { - items_count: number; - state_changes: null; - }; -}; diff --git a/types/api/txsFilters.ts b/types/api/txsFilters.ts deleted file mode 100644 index 4704d1f7e8..0000000000 --- a/types/api/txsFilters.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type TTxsFilters = { - filter: 'pending' | 'validated'; - type?: Array; - method?: Array; -}; - -export type TTxsWithBlobsFilters = { - type: 'blob_transaction'; -}; - -export type TypeFilter = 'token_transfer' | 'contract_creation' | 'contract_call' | 'coin_transfer' | 'token_creation' | 'blob_transaction'; - -export type MethodFilter = 'approve' | 'transfer' | 'multicall' | 'mint' | 'commit'; diff --git a/types/api/userOps.ts b/types/api/userOps.ts index c0e528d41d..45398b8103 100644 --- a/types/api/userOps.ts +++ b/types/api/userOps.ts @@ -1,5 +1,5 @@ -import type { AddressParamBasic } from './addressParams'; -import type { DecodedInput } from './decodedInput'; +import type { AddressParamBasic } from 'client/slices/address/types/api'; +import type { DecodedInput } from 'client/slices/log/types/api'; export type UserOpsItem = { hash: string; diff --git a/types/api/validators.ts b/types/api/validators.ts index 7084339349..6238ae8ca1 100644 --- a/types/api/validators.ts +++ b/types/api/validators.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; // Stability diff --git a/types/api/withdrawals.ts b/types/api/withdrawals.ts index 55c8720f1e..1d427af4b9 100644 --- a/types/api/withdrawals.ts +++ b/types/api/withdrawals.ts @@ -1,4 +1,4 @@ -import type { AddressParam } from './addressParams'; +import type { AddressParam } from 'client/slices/address/types/api'; export type WithdrawalsResponse = { items: Array; diff --git a/types/api/zkEvmL2.ts b/types/api/zkEvmL2.ts deleted file mode 100644 index d99962e525..0000000000 --- a/types/api/zkEvmL2.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { Transaction } from './transaction'; - -export type ZkEvmL2DepositsItem = { - block_number: number; - index: number; - l1_transaction_hash: string; - l2_transaction_hash: string | null; - timestamp: string; - value: string; - symbol: string; -}; - -export type ZkEvmL2DepositsResponse = { - items: Array; - next_page_params: { - items_count: number; - index: number; - }; -}; - -export type ZkEvmL2WithdrawalsItem = { - block_number: number; - index: number; - l1_transaction_hash: string | null; - l2_transaction_hash: string; - timestamp: string; - value: string; - symbol: string; -}; - -export type ZkEvmL2WithdrawalsResponse = { - items: Array; - next_page_params: { - items_count: number; - index: number; - }; -}; - -export type ZkEvmL2TxnBatchesItem = { - number: number; - verify_transaction_hash: string | null; - sequence_transaction_hash: string | null; - status: string; - timestamp: string | null; - transactions_count: number; -}; - -export type ZkEvmL2TxnBatchesResponse = { - items: Array; - next_page_params: { - number: number; - items_count: number; - } | null; -}; - -export const ZKEVM_L2_TX_BATCH_STATUSES = [ 'Unfinalized', 'L1 Sequence Confirmed', 'Finalized' ]; - -export type ZkEvmL2TxnBatch = { - acc_input_hash: string; - global_exit_root: string; - number: number; - sequence_transaction_hash: string; - state_root: string; - status: typeof ZKEVM_L2_TX_BATCH_STATUSES[number]; - timestamp: string | null; - transactions: Array; - verify_transaction_hash: string; -}; - -export type ZkEvmL2TxnBatchTxs = { - items: Array; - // API response doesn't have next_page_params option, but we need to add it to the type for consistency - next_page_params: null; -}; - -export type NewZkEvmBatchSocketResponse = { batch: ZkEvmL2TxnBatchesItem }; diff --git a/types/api/zkSyncL2.ts b/types/api/zkSyncL2.ts index 635b448d77..b2bc923f93 100644 --- a/types/api/zkSyncL2.ts +++ b/types/api/zkSyncL2.ts @@ -1,4 +1,4 @@ -import type { Transaction } from './transaction'; +import type { Transaction } from 'client/slices/tx/types/api'; export const ZKSYNC_L2_TX_BATCH_STATUSES = [ 'Processed on L2' as const, diff --git a/types/client/adButlerConfig.ts b/types/client/adButlerConfig.ts index b69f5a7b7e..d0b5123133 100644 --- a/types/client/adButlerConfig.ts +++ b/types/client/adButlerConfig.ts @@ -1,5 +1,12 @@ -export type AdButlerConfig = { +export type AdButlerDeviceConfig = { id: string; width: string; height: string; }; + +export type AdButlerConfig = { + config: { + desktop: AdButlerDeviceConfig; + mobile: AdButlerDeviceConfig; + }; +}; diff --git a/types/client/adProviders.ts b/types/client/adProviders.ts index 2f9be8f9de..d46a924d27 100644 --- a/types/client/adProviders.ts +++ b/types/client/adProviders.ts @@ -3,7 +3,7 @@ import type { ArrayElement } from 'types/utils'; export const SUPPORTED_AD_BANNER_PROVIDERS = [ 'slise', 'adbutler', - 'coinzilla', + 'sevio', 'none', ] as const; export type AdBannerProviders = ArrayElement; @@ -11,5 +11,5 @@ export type AdBannerProviders = ArrayElement; -export const SUPPORTED_AD_TEXT_PROVIDERS = [ 'coinzilla', 'none' ] as const; +export const SUPPORTED_AD_TEXT_PROVIDERS = [ 'sevio', 'none' ] as const; export type AdTextProviders = ArrayElement; diff --git a/types/client/address.ts b/types/client/address.ts deleted file mode 100644 index e46be6c086..0000000000 --- a/types/client/address.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { AddressFromToFilter } from 'types/api/address'; - -export type CsvExportParams = { - type: 'transactions' | 'internal-transactions' | 'token-transfers'; - filterType?: 'address'; - filterValue?: AddressFromToFilter; -} | { - type: 'logs'; - filterType?: 'topic'; - filterValue?: string; -} | { - type: 'holders'; - filterType?: undefined; - filterValue?: undefined; -} | { - type: 'epoch-rewards'; - filterType?: undefined; - filterValue?: undefined; -}; diff --git a/types/client/addressProfileAPIConfig.ts b/types/client/addressProfileAPIConfig.ts deleted file mode 100644 index 66078b3fcd..0000000000 --- a/types/client/addressProfileAPIConfig.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type AddressProfileAPIConfig = { - api_url_template: string; - tag_link_template?: string; - tag_icon?: string; - tag_bg_color?: string; - tag_text_color?: string; -}; diff --git a/types/client/rollup.ts b/types/client/rollup.ts index d260ec5be4..1f73d08c12 100644 --- a/types/client/rollup.ts +++ b/types/client/rollup.ts @@ -4,7 +4,6 @@ export const ROLLUP_TYPES = [ 'optimistic', 'arbitrum', 'shibarium', - 'zkEvm', 'zkSync', 'scroll', ] as const; diff --git a/types/client/stats.ts b/types/client/stats.ts deleted file mode 100644 index 1e4219bba6..0000000000 --- a/types/client/stats.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type StatsInterval = { id: StatsIntervalIds; title: string }; -export type StatsIntervalIds = keyof typeof StatsIntervalId; -export enum StatsIntervalId { - all, - oneMonth, - threeMonths, - sixMonths, - oneYear, -} diff --git a/types/homepage.ts b/types/homepage.ts index c3c7258433..adc29d61aa 100644 --- a/types/homepage.ts +++ b/types/homepage.ts @@ -32,6 +32,7 @@ export interface HeroBannerConfig { search?: { border_width?: Array; }; + text?: string; } export interface HighlightsBannerConfig { diff --git a/types/networks.ts b/types/networks.ts index 1d29ad56bc..b44bfd73ab 100644 --- a/types/networks.ts +++ b/types/networks.ts @@ -26,5 +26,5 @@ export interface NetworkExplorer { } export type NetworkVerificationTypeEnvs = 'mining' | 'validation' | 'fee reception'; -export type NetworkVerificationTypeComputed = 'posting' | 'sequencing'; +export type NetworkVerificationTypeComputed = 'posting'; export type NetworkVerificationType = NetworkVerificationTypeEnvs | NetworkVerificationTypeComputed; diff --git a/ui/address/AddressContract.tsx b/ui/address/AddressContract.tsx index b3896ccd28..36d40eb61f 100644 --- a/ui/address/AddressContract.tsx +++ b/ui/address/AddressContract.tsx @@ -2,15 +2,17 @@ import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { Address } from 'types/api/address'; - -import { getResourceKey } from 'lib/api/useApiQuery'; -import delay from 'lib/delay'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { Address } from 'client/slices/address/types/api'; + +import { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import delay from 'client/shared/utils/delay'; + import type { Props as RoutedTabsProps } from 'toolkit/components/AdaptiveTabs/AdaptiveTabs'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import { SECOND } from 'toolkit/utils/consts'; diff --git a/ui/address/AddressCsvExportLink.tsx b/ui/address/AddressCsvExportLink.tsx deleted file mode 100644 index 1cc6994602..0000000000 --- a/ui/address/AddressCsvExportLink.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { chakra } from '@chakra-ui/react'; -import React from 'react'; - -import type { CsvExportParams } from 'types/client/address'; -import type { ClusterChainConfig } from 'types/multichain'; - -import { route } from 'nextjs/routes'; - -import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import { Link } from 'toolkit/chakra/link'; -import { Tooltip } from 'toolkit/chakra/tooltip'; -import IconSvg from 'ui/shared/IconSvg'; - -interface Props { - address: string; - params: CsvExportParams; - className?: string; - isLoading?: boolean; - chainData?: ClusterChainConfig; -} - -const AddressCsvExportLink = ({ className, address, params, isLoading, chainData }: Props) => { - const isMobile = useIsMobile(); - const isInitialLoading = useIsInitialLoading(isLoading); - const multichainContext = useMultichainContext(); - - const chainConfig = chainData?.app_config || multichainContext?.chain.app_config || config; - - if (!chainConfig.features.csvExport.isEnabled) { - return null; - } - - return ( - - - - Download - - - ); -}; - -export default React.memo(chakra(AddressCsvExportLink)); diff --git a/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_dark-color-mode_base-view-dark-mode-1.png deleted file mode 100644 index 278b972cc7..0000000000 Binary files a/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_base-view-dark-mode-1.png b/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_base-view-dark-mode-1.png deleted file mode 100644 index 55d6a0db43..0000000000 Binary files a/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_base-view-dark-mode-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_mobile-base-view-1.png b/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_mobile-base-view-1.png deleted file mode 100644 index 56f7d15074..0000000000 Binary files a/ui/address/__screenshots__/Address3rdPartyWidgets.pw.tsx_default_mobile-base-view-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png deleted file mode 100644 index 181d976e8d..0000000000 Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png deleted file mode 100644 index 953fdfa977..0000000000 Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_base-view-dark-mode-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png b/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png deleted file mode 100644 index d72b984270..0000000000 Binary files a/ui/address/__screenshots__/AddressCoinBalance.pw.tsx_default_mobile-base-view-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png b/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index fbfd759f63..0000000000 Binary files a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png b/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index b52f60d1fd..0000000000 Binary files a/ui/address/__screenshots__/AddressEpochRewards.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index cf466b70ed..0000000000 Binary files a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png b/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index f6a78cb2b6..0000000000 Binary files a/ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-pagination-1.png deleted file mode 100644 index 3474a57f3d..0000000000 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-with-pagination-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-without-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-without-pagination-1.png deleted file mode 100644 index 25a0a6872c..0000000000 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_mobile-without-pagination-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-pagination-1.png deleted file mode 100644 index 7a0c3ba764..0000000000 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_with-pagination-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_without-pagination-1.png b/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_without-pagination-1.png deleted file mode 100644 index 7c68aea568..0000000000 Binary files a/ui/address/__screenshots__/AddressTokenTransfers.pw.tsx_default_without-pagination-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png deleted file mode 100644 index f9ea16e28e..0000000000 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-1.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png deleted file mode 100644 index 2bbeaaf706..0000000000 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-desktop-2.png and /dev/null differ diff --git a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png b/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png deleted file mode 100644 index a8ca44c0f0..0000000000 Binary files a/ui/address/__screenshots__/AddressTxs.pw.tsx_default_base-view-screen-xl-base-view-1.png and /dev/null differ diff --git a/ui/address/address3rdPartyWidgets/useWidgetData.ts b/ui/address/address3rdPartyWidgets/useWidgetData.ts deleted file mode 100644 index 5a9c86aa94..0000000000 --- a/ui/address/address3rdPartyWidgets/useWidgetData.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { get } from 'es-toolkit/compat'; - -import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; - -const RESOURCE_NAME = 'general:address_3rd_party_info'; - -const formatValue = (value: unknown): string | undefined => { - if (typeof value !== 'number' && typeof value !== 'string') { - return undefined; - } - - const num = Number(value); - if (!isNaN(num)) { - if (num === -1) { - return '0'; - } - return num.toLocaleString(); - } - - return String(value); -}; - -export default function useWidgetData(name: string, valuePath: string | undefined, address: string, isLoading: boolean) { - const query = useApiQuery(RESOURCE_NAME, { - pathParams: { name }, - queryParams: { address, chain_id: config.chain.id }, - queryOptions: { - select: (response) => { - try { - const value = get(response, valuePath || ''); - return formatValue(value); - } catch { - return undefined; - } - }, - enabled: !isLoading && Boolean(valuePath), - refetchOnMount: false, - }, - }); - - return query; -} diff --git a/ui/address/contract/ContractDetails.tsx b/ui/address/contract/ContractDetails.tsx index 0dd83309cf..b6b3d548ca 100644 --- a/ui/address/contract/ContractDetails.tsx +++ b/ui/address/contract/ContractDetails.tsx @@ -4,14 +4,15 @@ import { useRouter } from 'next/router'; import type { Channel } from 'phoenix'; import React from 'react'; -import type { Address } from 'types/api/address'; -import type { AddressImplementation } from 'types/api/addressParams'; +import type { Address, AddressImplementation } from 'client/slices/address/types/api'; import type { SmartContract } from 'types/api/contract'; -import type { ResourceError } from 'lib/api/resources'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; +import type { ResourceError } from 'client/api/resources'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import getQueryParamString from 'lib/router/getQueryParamString'; import * as stubs from 'stubs/contract'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; diff --git a/ui/address/contract/ContractDetailsByteCode.tsx b/ui/address/contract/ContractDetailsByteCode.tsx index 72ccebec1e..0da5bead93 100644 --- a/ui/address/contract/ContractDetailsByteCode.tsx +++ b/ui/address/contract/ContractDetailsByteCode.tsx @@ -1,7 +1,7 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import type { SmartContract } from 'types/api/contract'; import { Alert } from 'toolkit/chakra/alert'; diff --git a/ui/address/contract/ContractDetailsConstructorArgs.tsx b/ui/address/contract/ContractDetailsConstructorArgs.tsx index 20764a5cc7..2173c6b742 100644 --- a/ui/address/contract/ContractDetailsConstructorArgs.tsx +++ b/ui/address/contract/ContractDetailsConstructorArgs.tsx @@ -3,7 +3,8 @@ import React from 'react'; import type { SmartContract } from 'types/api/contract'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import type { Truncation } from 'ui/shared/entities/base/components'; import RawDataSnippet from 'ui/shared/RawDataSnippet'; diff --git a/ui/address/contract/ContractDetailsDeployedByteCode.tsx b/ui/address/contract/ContractDetailsDeployedByteCode.tsx index 819a6787bb..43b5884ac3 100644 --- a/ui/address/contract/ContractDetailsDeployedByteCode.tsx +++ b/ui/address/contract/ContractDetailsDeployedByteCode.tsx @@ -1,10 +1,11 @@ import { Flex, createListCollection } from '@chakra-ui/react'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; + +import hexToUtf8 from 'client/shared/transformers/hex-to-utf8'; import config from 'configs/app'; -import hexToUtf8 from 'lib/hexToUtf8'; import type { SelectOption } from 'toolkit/chakra/select'; import { Select } from 'toolkit/chakra/select'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/address/contract/ContractExternalLibraries.tsx b/ui/address/contract/ContractExternalLibraries.tsx index 8d74edb3d3..fe19953956 100644 --- a/ui/address/contract/ContractExternalLibraries.tsx +++ b/ui/address/contract/ContractExternalLibraries.tsx @@ -3,7 +3,10 @@ import React from 'react'; import type { SmartContractExternalLibrary } from 'types/api/contract'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { Alert } from 'toolkit/chakra/alert'; import { Button } from 'toolkit/chakra/button'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; @@ -12,7 +15,6 @@ import { PopoverRoot, PopoverBody, PopoverContent, PopoverTrigger } from 'toolki import { Skeleton } from 'toolkit/chakra/skeleton'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; import { apos } from 'toolkit/utils/htmlEntities'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import IconSvg from 'ui/shared/IconSvg'; interface Props { diff --git a/ui/address/contract/ContractSourceAddressSelector.tsx b/ui/address/contract/ContractSourceAddressSelector.tsx index 3b20b6f45c..fb5669a9d4 100644 --- a/ui/address/contract/ContractSourceAddressSelector.tsx +++ b/ui/address/contract/ContractSourceAddressSelector.tsx @@ -3,10 +3,11 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Select } from 'toolkit/chakra/select'; import { Skeleton } from 'toolkit/chakra/skeleton'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import LinkNewTab from 'ui/shared/links/LinkNewTab'; export interface Item { diff --git a/ui/address/contract/alerts/ConflictingImplementationsModal.tsx b/ui/address/contract/alerts/ConflictingImplementationsModal.tsx index 4bb58b34b5..4f8a1b9cde 100644 --- a/ui/address/contract/alerts/ConflictingImplementationsModal.tsx +++ b/ui/address/contract/alerts/ConflictingImplementationsModal.tsx @@ -3,10 +3,11 @@ import React from 'react'; import type { SmartContractConflictingImplementation } from 'types/api/contract'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Button } from 'toolkit/chakra/button'; import { DialogActionTrigger, DialogBody, DialogContent, DialogHeader, DialogRoot, DialogTrigger } from 'toolkit/chakra/dialog'; import { Link } from 'toolkit/chakra/link'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import { PROXY_TYPES } from './utils'; diff --git a/ui/address/contract/alerts/ContractDetailsAlertVerificationStatus.tsx b/ui/address/contract/alerts/ContractDetailsAlertVerificationStatus.tsx index ba16ec13b2..00bb9fb996 100644 --- a/ui/address/contract/alerts/ContractDetailsAlertVerificationStatus.tsx +++ b/ui/address/contract/alerts/ContractDetailsAlertVerificationStatus.tsx @@ -1,7 +1,7 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import type { SmartContract } from 'types/api/contract'; import { Alert } from 'toolkit/chakra/alert'; diff --git a/ui/address/contract/alerts/ContractDetailsAlerts.pwstory.tsx b/ui/address/contract/alerts/ContractDetailsAlerts.pwstory.tsx index 4b5301cb59..f0cf6dd54b 100644 --- a/ui/address/contract/alerts/ContractDetailsAlerts.pwstory.tsx +++ b/ui/address/contract/alerts/ContractDetailsAlerts.pwstory.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import useSocketChannel from 'lib/socket/useSocketChannel'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; import type { Props } from './ContractDetailsAlerts'; import ContractDetailsAlerts from './ContractDetailsAlerts'; diff --git a/ui/address/contract/alerts/ContractDetailsAlerts.tsx b/ui/address/contract/alerts/ContractDetailsAlerts.tsx index ee22533d73..c13a2e632f 100644 --- a/ui/address/contract/alerts/ContractDetailsAlerts.tsx +++ b/ui/address/contract/alerts/ContractDetailsAlerts.tsx @@ -2,16 +2,18 @@ import { chakra, Box, Flex } from '@chakra-ui/react'; import type { Channel } from 'phoenix'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { Address } from 'types/api/address'; +import type { SocketMessage } from 'client/api/socket/types'; +import type { Address } from 'client/slices/address/types/api'; import type { SmartContract } from 'types/api/contract'; import { route } from 'nextjs-routes'; -import useSocketMessage from 'lib/socket/useSocketMessage'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Alert } from 'toolkit/chakra/alert'; import { Link } from 'toolkit/chakra/link'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ContractDetailsAlertProxyPattern from './ContractDetailsAlertProxyPattern'; import ContractDetailsAlertVerificationStatus from './ContractDetailsAlertVerificationStatus'; diff --git a/ui/address/contract/audits/ContractSecurityAudits.tsx b/ui/address/contract/audits/ContractSecurityAudits.tsx index 9cbff8a343..dde44cb598 100644 --- a/ui/address/contract/audits/ContractSecurityAudits.tsx +++ b/ui/address/contract/audits/ContractSecurityAudits.tsx @@ -3,7 +3,8 @@ import React from 'react'; import type { SmartContractSecurityAuditSubmission } from 'types/api/contract'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; diff --git a/ui/address/contract/audits/ContractSubmitAuditForm.tsx b/ui/address/contract/audits/ContractSubmitAuditForm.tsx index 536c7553cd..30be817349 100644 --- a/ui/address/contract/audits/ContractSubmitAuditForm.tsx +++ b/ui/address/contract/audits/ContractSubmitAuditForm.tsx @@ -5,8 +5,9 @@ import { FormProvider, useForm } from 'react-hook-form'; import type { SmartContractSecurityAuditSubmission } from 'types/api/contract'; -import type { ResourceError } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import type { ResourceError } from 'client/api/resources'; + import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { toaster } from 'toolkit/chakra/toaster'; diff --git a/ui/address/contract/audits/__screenshots__/ContractSubmitAuditForm.pw.tsx_default_base-view-1.png b/ui/address/contract/audits/__screenshots__/ContractSubmitAuditForm.pw.tsx_default_base-view-1.png index f9f6a98176..8da8a62f71 100644 Binary files a/ui/address/contract/audits/__screenshots__/ContractSubmitAuditForm.pw.tsx_default_base-view-1.png and b/ui/address/contract/audits/__screenshots__/ContractSubmitAuditForm.pw.tsx_default_base-view-1.png differ diff --git a/ui/address/contract/info/ContractDetailsInfo.tsx b/ui/address/contract/info/ContractDetailsInfo.tsx index c7676e9a52..f7191758ed 100644 --- a/ui/address/contract/info/ContractDetailsInfo.tsx +++ b/ui/address/contract/info/ContractDetailsInfo.tsx @@ -1,7 +1,7 @@ import { Flex, Grid } from '@chakra-ui/react'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import type { SmartContract } from 'types/api/contract'; import config from 'configs/app'; diff --git a/ui/address/contract/info/ContractDetailsInfoCreator.tsx b/ui/address/contract/info/ContractDetailsInfoCreator.tsx index db663bd867..ec59e58832 100644 --- a/ui/address/contract/info/ContractDetailsInfoCreator.tsx +++ b/ui/address/contract/info/ContractDetailsInfoCreator.tsx @@ -3,8 +3,9 @@ import React from 'react'; import type { SmartContractCreationStatus } from 'types/api/contract'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import ContractCreationStatus from 'ui/shared/statusTag/ContractCreationStatus'; import ContractDetailsInfoItem from './ContractDetailsInfoItem'; diff --git a/ui/address/contract/info/ContractDetailsInfoImplementations.tsx b/ui/address/contract/info/ContractDetailsInfoImplementations.tsx index aef64a7475..81eb6599ba 100644 --- a/ui/address/contract/info/ContractDetailsInfoImplementations.tsx +++ b/ui/address/contract/info/ContractDetailsInfoImplementations.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import type { AddressImplementation } from 'types/api/addressParams'; +import type { AddressImplementation } from 'client/slices/address/types/api'; import type { SmartContractProxyType } from 'types/api/contract'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import ContainerWithScrollY from 'ui/shared/ContainerWithScrollY'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ContractDetailsInfoItem from './ContractDetailsInfoItem'; diff --git a/ui/address/contract/methods/ContractMethodsCustom.tsx b/ui/address/contract/methods/ContractMethodsCustom.tsx index 91415d5d42..625ca145b8 100644 --- a/ui/address/contract/methods/ContractMethodsCustom.tsx +++ b/ui/address/contract/methods/ContractMethodsCustom.tsx @@ -5,8 +5,10 @@ import React from 'react'; import type { SmartContract } from 'types/api/contract'; -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { Button } from 'toolkit/chakra/button'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/ui/address/contract/methods/ContractMethodsMudSystem.tsx b/ui/address/contract/methods/ContractMethodsMudSystem.tsx index 92fbb08753..3124b62cff 100644 --- a/ui/address/contract/methods/ContractMethodsMudSystem.tsx +++ b/ui/address/contract/methods/ContractMethodsMudSystem.tsx @@ -4,8 +4,9 @@ import React from 'react'; import type { SmartContractMudSystemItem } from 'types/api/contract'; -import useApiQuery from 'lib/api/useApiQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; import type { Item } from '../ContractSourceAddressSelector'; import ContractSourceAddressSelector from '../ContractSourceAddressSelector'; diff --git a/ui/address/contract/methods/ContractMethodsProxy.tsx b/ui/address/contract/methods/ContractMethodsProxy.tsx index c453f8d8ea..8f103e5371 100644 --- a/ui/address/contract/methods/ContractMethodsProxy.tsx +++ b/ui/address/contract/methods/ContractMethodsProxy.tsx @@ -2,11 +2,12 @@ import { Flex } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import type { AddressImplementation } from 'types/api/addressParams'; +import type { AddressImplementation } from 'client/slices/address/types/api'; import type { SmartContractConflictingImplementation, SmartContractProxyType } from 'types/api/contract'; -import useApiQuery from 'lib/api/useApiQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; import ContractSourceAddressSelector from '../ContractSourceAddressSelector'; import ContractAbi from './ContractAbi'; diff --git a/ui/address/contract/methods/ContractMethodsRegular.tsx b/ui/address/contract/methods/ContractMethodsRegular.tsx index 97cb9500f0..a9b4860451 100644 --- a/ui/address/contract/methods/ContractMethodsRegular.tsx +++ b/ui/address/contract/methods/ContractMethodsRegular.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import type { Abi } from 'viem'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; import ContractAbi from './ContractAbi'; import ContractMethodsAlerts from './ContractMethodsAlerts'; diff --git a/ui/address/contract/methods/alerts/ConnectWalletAlert.tsx b/ui/address/contract/methods/alerts/ConnectWalletAlert.tsx index 482807d097..c41e825632 100644 --- a/ui/address/contract/methods/alerts/ConnectWalletAlert.tsx +++ b/ui/address/contract/methods/alerts/ConnectWalletAlert.tsx @@ -1,14 +1,17 @@ import { Flex, Spinner } from '@chakra-ui/react'; import React from 'react'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import useWeb3Wallet from 'client/features/connect-wallet/hooks/useWallet'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useWeb3Wallet from 'lib/web3/useWallet'; import { Alert } from 'toolkit/chakra/alert'; import { Button } from 'toolkit/chakra/button'; import { IconButton } from 'toolkit/chakra/icon-button'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import IconSvg from 'ui/shared/IconSvg'; interface Props { diff --git a/ui/address/contract/methods/form/ContractMethodAddressButton.tsx b/ui/address/contract/methods/form/ContractMethodAddressButton.tsx index 14533d9d8e..fc44a4dd1d 100644 --- a/ui/address/contract/methods/form/ContractMethodAddressButton.tsx +++ b/ui/address/contract/methods/form/ContractMethodAddressButton.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import useAccount from 'lib/web3/useAccount'; +import useAccount from 'client/features/connect-wallet/hooks/useAccount'; + import { Button } from 'toolkit/chakra/button'; import { Tooltip } from 'toolkit/chakra/tooltip'; diff --git a/ui/address/contract/methods/form/ContractMethodForm.tsx b/ui/address/contract/methods/form/ContractMethodForm.tsx index 3063bc1b2a..f61279ca53 100644 --- a/ui/address/contract/methods/form/ContractMethodForm.tsx +++ b/ui/address/contract/methods/form/ContractMethodForm.tsx @@ -6,8 +6,9 @@ import { encodeFunctionData, type AbiFunction } from 'viem'; import type { FormSubmitHandler, FormSubmitResult, MethodCallStrategy, SmartContractMethod } from '../types'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import config from 'configs/app'; -import * as mixpanel from 'lib/mixpanel/index'; import { Button } from 'toolkit/chakra/button'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { useDisclosure } from 'toolkit/hooks/useDisclosure'; diff --git a/ui/address/contract/methods/form/__screenshots__/ContractMethodForm.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/ui/address/contract/methods/form/__screenshots__/ContractMethodForm.pw.tsx_mobile_base-view-mobile-dark-mode-1.png index 353c67fe23..b1f5114f04 100644 Binary files a/ui/address/contract/methods/form/__screenshots__/ContractMethodForm.pw.tsx_mobile_base-view-mobile-dark-mode-1.png and b/ui/address/contract/methods/form/__screenshots__/ContractMethodForm.pw.tsx_mobile_base-view-mobile-dark-mode-1.png differ diff --git a/ui/address/contract/methods/useCallMethodPublicClient.ts b/ui/address/contract/methods/useCallMethodPublicClient.ts index 41bfb6b08f..148d2b00a3 100644 --- a/ui/address/contract/methods/useCallMethodPublicClient.ts +++ b/ui/address/contract/methods/useCallMethodPublicClient.ts @@ -4,9 +4,10 @@ import { usePublicClient } from 'wagmi'; import type { FormSubmitResult, MethodCallStrategy, SmartContractMethod } from './types'; +import useAccount from 'client/features/connect-wallet/hooks/useAccount'; + import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; -import useAccount from 'lib/web3/useAccount'; import { getNativeCoinValue } from './utils'; diff --git a/ui/address/contract/methods/useCallMethodWalletClient.ts b/ui/address/contract/methods/useCallMethodWalletClient.ts index 4dab912eb9..13cd6c2e43 100644 --- a/ui/address/contract/methods/useCallMethodWalletClient.ts +++ b/ui/address/contract/methods/useCallMethodWalletClient.ts @@ -4,6 +4,8 @@ import { useAccount, useSwitchChain, useWriteContract, useSendTransaction } from import type { FormSubmitResult, SmartContractMethod } from './types'; +import useWallet from 'client/features/connect-wallet/hooks/useWallet'; + import config from 'configs/app'; import { useMultichainContext } from 'lib/contexts/multichain'; import useRewardsActivity from 'lib/hooks/useRewardsActivity'; diff --git a/ui/address/contract/methods/useMethodsFilters.ts b/ui/address/contract/methods/useMethodsFilters.ts index 78e5709713..1bbeeb5a7e 100644 --- a/ui/address/contract/methods/useMethodsFilters.ts +++ b/ui/address/contract/methods/useMethodsFilters.ts @@ -4,7 +4,7 @@ import React from 'react'; import type { MethodType, SmartContractMethod } from './types'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; import type { CONTRACT_MAIN_TAB_IDS } from '../utils'; import { TYPE_FILTER_OPTIONS, isReadMethod, isWriteMethod } from './utils'; diff --git a/ui/address/contract/specs/ContractDetails.tsx b/ui/address/contract/specs/ContractDetails.tsx index 7af14a463f..b552f2ef8e 100644 --- a/ui/address/contract/specs/ContractDetails.tsx +++ b/ui/address/contract/specs/ContractDetails.tsx @@ -1,8 +1,9 @@ import { useRouter } from 'next/router'; -import useApiQuery from 'lib/api/useApiQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; +import useApiQuery from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; import useContractTabs from '../useContractTabs'; diff --git a/ui/address/contract/useContractDetailsTabs.tsx b/ui/address/contract/useContractDetailsTabs.tsx index 4f9a54d1ae..cdd10cac55 100644 --- a/ui/address/contract/useContractDetailsTabs.tsx +++ b/ui/address/contract/useContractDetailsTabs.tsx @@ -1,7 +1,7 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import type { SmartContract } from 'types/api/contract'; import CodeViewSnippet from 'ui/shared/CodeViewSnippet'; diff --git a/ui/address/contract/useContractTabs.tsx b/ui/address/contract/useContractTabs.tsx index 412b601ce2..8341e9a5f4 100644 --- a/ui/address/contract/useContractTabs.tsx +++ b/ui/address/contract/useContractTabs.tsx @@ -1,11 +1,12 @@ import type { Channel } from 'phoenix'; import React from 'react'; -import type { Address } from 'types/api/address'; +import type { Address } from 'client/slices/address/types/api'; import type { ClusterChainConfig } from 'types/multichain'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import * as stubs from 'stubs/contract'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import ContractDetails from 'ui/address/contract/ContractDetails'; diff --git a/ui/address/details/__screenshots__/AddressAlerts.pw.tsx_default_base-view-1.png b/ui/address/details/__screenshots__/AddressAlerts.pw.tsx_default_base-view-1.png deleted file mode 100644 index 829ef51413..0000000000 Binary files a/ui/address/details/__screenshots__/AddressAlerts.pw.tsx_default_base-view-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressAlerts.pw.tsx_default_with-scam-token-1.png b/ui/address/details/__screenshots__/AddressAlerts.pw.tsx_default_with-scam-token-1.png deleted file mode 100644 index a0f79c11e3..0000000000 Binary files a/ui/address/details/__screenshots__/AddressAlerts.pw.tsx_default_with-scam-token-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_dark-color-mode_with-single-multichain-button-internal-dark-mode-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_dark-color-mode_with-single-multichain-button-internal-dark-mode-1.png deleted file mode 100644 index 366ded0413..0000000000 Binary files a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_dark-color-mode_with-single-multichain-button-internal-dark-mode-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_base-view-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_base-view-1.png deleted file mode 100644 index 85f29e9c0d..0000000000 Binary files a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_base-view-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-multichain-button-internal-small-screen-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-multichain-button-internal-small-screen-1.png deleted file mode 100644 index a2a862695f..0000000000 Binary files a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-multichain-button-internal-small-screen-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-single-multichain-button-external-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-single-multichain-button-external-1.png deleted file mode 100644 index 5ea109667e..0000000000 Binary files a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-single-multichain-button-external-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-single-multichain-button-internal-dark-mode-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-single-multichain-button-internal-dark-mode-1.png deleted file mode 100644 index 11b9a1b185..0000000000 Binary files a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-single-multichain-button-internal-dark-mode-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-and-promo-1.png b/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-and-promo-1.png deleted file mode 100644 index eb96c9d25c..0000000000 Binary files a/ui/address/details/__screenshots__/AddressNetWorth.pw.tsx_default_with-two-multichain-button-and-promo-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png deleted file mode 100644 index 9a05ca4df8..0000000000 Binary files a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_default_default-view-mobile-dark-mode-1.png b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_default_default-view-mobile-dark-mode-1.png deleted file mode 100644 index 1319674323..0000000000 Binary files a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_default_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_mobile_default-view-mobile-dark-mode-1.png b/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_mobile_default-view-mobile-dark-mode-1.png deleted file mode 100644 index df3ec46443..0000000000 Binary files a/ui/address/details/__screenshots__/AddressQrCode.pw.tsx_mobile_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png deleted file mode 100644 index 09ee8f8802..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-2.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-2.png deleted file mode 100644 index 8beb4841a6..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_dark-color-mode_base-view-dark-mode-2.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-1.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-1.png deleted file mode 100644 index 6e32032ffa..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-1.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-2.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-2.png deleted file mode 100644 index 0019c217ba..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_base-view-dark-mode-2.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_long-values-1.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_long-values-1.png deleted file mode 100644 index ea0cb7f244..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_long-values-1.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_native-token-1.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_native-token-1.png deleted file mode 100644 index 325ff787cc..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_native-token-1.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_sort-1.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_sort-1.png deleted file mode 100644 index 89e202ec64..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_sort-1.png and /dev/null differ diff --git a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_sort-2.png b/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_sort-2.png deleted file mode 100644 index 8edef12dca..0000000000 Binary files a/ui/address/tokenSelect/__screenshots__/TokenSelect.pw.tsx_default_sort-2.png and /dev/null differ diff --git a/ui/addressVerification/AddressVerificationModal.tsx b/ui/addressVerification/AddressVerificationModal.tsx index 908732dc5d..42aa5d64c1 100644 --- a/ui/addressVerification/AddressVerificationModal.tsx +++ b/ui/addressVerification/AddressVerificationModal.tsx @@ -4,9 +4,10 @@ import React from 'react'; import type { AddressVerificationFormFirstStepFields, AddressCheckStatusSuccess } from './types'; import type { VerifiedAddress } from 'types/api/account'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import config from 'configs/app'; import { FallbackProvider } from 'lib/contexts/fallback'; -import * as mixpanel from 'lib/mixpanel/index'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; const Web3ProviderBase = dynamic(() => import('ui/shared/web3/Web3Provider'), { ssr: false }); diff --git a/ui/addressVerification/steps/AddressVerificationStepAddress.pw.tsx b/ui/addressVerification/steps/AddressVerificationStepAddress.pw.tsx index 70d4e89cfe..c41bf27ece 100644 --- a/ui/addressVerification/steps/AddressVerificationStepAddress.pw.tsx +++ b/ui/addressVerification/steps/AddressVerificationStepAddress.pw.tsx @@ -1,12 +1,13 @@ import React from 'react'; -import buildUrl from 'lib/api/buildUrl'; +import buildUrl from 'client/api/build-url'; + import * as mocks from 'mocks/account/verifiedAddresses'; import { test, expect } from 'playwright/lib'; import AddressVerificationStepAddress from './AddressVerificationStepAddress'; -const CHECK_ADDRESS_URL = buildUrl('contractInfo:address_verification', { chainId: '1', type: ':prepare' }); +const CHECK_ADDRESS_URL = buildUrl('contractInfo:address_verification', { instanceId: '1', type: ':prepare' }); test('base view', async({ render, page }) => { await page.route(CHECK_ADDRESS_URL, (route) => route.fulfill({ diff --git a/ui/addressVerification/steps/AddressVerificationStepAddress.tsx b/ui/addressVerification/steps/AddressVerificationStepAddress.tsx index ba0ff63fc5..5f6ba892eb 100644 --- a/ui/addressVerification/steps/AddressVerificationStepAddress.tsx +++ b/ui/addressVerification/steps/AddressVerificationStepAddress.tsx @@ -13,9 +13,10 @@ import type { import { route } from 'nextjs-routes'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import type { ResourceError } from 'client/api/resources'; + import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; import { Alert } from 'toolkit/chakra/alert'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; @@ -54,7 +55,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) = 'contractInfo:address_verification', { fetchParams: { method: 'POST', body }, - pathParams: { chainId: config.chain.id, type: ':prepare' }, + pathParams: { instanceId: config.apis.contractInfo?.instanceId, type: ':prepare' }, }, ); diff --git a/ui/addressVerification/steps/AddressVerificationStepSignature.pw.tsx b/ui/addressVerification/steps/AddressVerificationStepSignature.pw.tsx index 250b402aac..5f42951673 100644 --- a/ui/addressVerification/steps/AddressVerificationStepSignature.pw.tsx +++ b/ui/addressVerification/steps/AddressVerificationStepSignature.pw.tsx @@ -1,12 +1,14 @@ import React from 'react'; -import buildUrl from 'lib/api/buildUrl'; +import buildUrl from 'client/api/build-url'; + +import config from 'configs/app'; import * as mocks from 'mocks/account/verifiedAddresses'; import { test, expect } from 'playwright/lib'; import AddressVerificationStepSignature from './AddressVerificationStepSignature'; -const VERIFY_ADDRESS_URL = buildUrl('contractInfo:address_verification', { chainId: '1', type: ':verify' }); +const VERIFY_ADDRESS_URL = buildUrl('contractInfo:address_verification', { instanceId: config.apis.contractInfo?.instanceId, type: ':verify' }); test('base view', async({ render, page }) => { await page.route(VERIFY_ADDRESS_URL, (route) => route.fulfill({ diff --git a/ui/addressVerification/steps/AddressVerificationStepSignature.tsx b/ui/addressVerification/steps/AddressVerificationStepSignature.tsx index 5be2c56749..f52f3b8ddf 100644 --- a/ui/addressVerification/steps/AddressVerificationStepSignature.tsx +++ b/ui/addressVerification/steps/AddressVerificationStepSignature.tsx @@ -14,10 +14,13 @@ import type { } from '../types'; import type { VerifiedAddress } from 'types/api/account'; +import useApiFetch from 'client/api/hooks/useApiFetch'; + +import useWallet from 'client/features/connect-wallet/hooks/useWallet'; + +import shortenString from 'client/shared/text/shorten-string'; + import config from 'configs/app'; -import useApiFetch from 'lib/api/useApiFetch'; -import shortenString from 'lib/shortenString'; -import useWallet from 'lib/web3/useWallet'; import { Alert } from 'toolkit/chakra/alert'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; @@ -69,7 +72,7 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre 'contractInfo:address_verification', { fetchParams: { method: 'POST', body }, - pathParams: { chainId: config.chain.id, type: ':verify' }, + pathParams: { instanceId: config.apis.contractInfo?.instanceId, type: ':verify' }, }, ); diff --git a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png index 6a2364b7e6..c98cd47316 100644 Binary files a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png and b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png differ diff --git a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_base-view-1.png b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_base-view-1.png index 1059accfcc..d4f087cede 100644 Binary files a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_base-view-1.png and b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_default_base-view-1.png differ diff --git a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_mobile_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_mobile_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png index bbc452cfc9..6c77bf1962 100644 Binary files a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_mobile_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png and b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepAddress.pw.tsx_mobile_SOURCE-CODE-NOT-VERIFIED-ERROR-view-mobile-1.png differ diff --git a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_default_INVALID-SIGNER-ERROR-view-mobile-1.png b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_default_INVALID-SIGNER-ERROR-view-mobile-1.png index e3d8265fbb..bb925d0c66 100644 Binary files a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_default_INVALID-SIGNER-ERROR-view-mobile-1.png and b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_default_INVALID-SIGNER-ERROR-view-mobile-1.png differ diff --git a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_mobile_INVALID-SIGNER-ERROR-view-mobile-1.png b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_mobile_INVALID-SIGNER-ERROR-view-mobile-1.png index 66d412fdff..f27ddd3f7c 100644 Binary files a/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_mobile_INVALID-SIGNER-ERROR-view-mobile-1.png and b/ui/addressVerification/steps/__screenshots__/AddressVerificationStepSignature.pw.tsx_mobile_INVALID-SIGNER-ERROR-view-mobile-1.png differ diff --git a/ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx b/ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx index 6ef8d111bb..34e3dde290 100644 --- a/ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx +++ b/ui/addressesLabelSearch/AddressesLabelSearchListItem.tsx @@ -1,11 +1,13 @@ import { HStack } from '@chakra-ui/react'; import React from 'react'; -import type { AddressesItem } from 'types/api/addresses'; +import type { AddressesItem } from 'client/slices/address/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import { currencyUnits } from 'client/shared/chain/units'; -import { currencyUnits } from 'lib/units'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/addressesLabelSearch/AddressesLabelSearchTable.tsx b/ui/addressesLabelSearch/AddressesLabelSearchTable.tsx index 656edbd0ef..fc7689f160 100644 --- a/ui/addressesLabelSearch/AddressesLabelSearchTable.tsx +++ b/ui/addressesLabelSearch/AddressesLabelSearchTable.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import type { AddressesItem } from 'types/api/addresses'; +import type { AddressesItem } from 'client/slices/address/types/api'; + +import { currencyUnits } from 'client/shared/chain/units'; -import { currencyUnits } from 'lib/units'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import AddressesLabelSearchTableItem from './AddressesLabelSearchTableItem'; diff --git a/ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx b/ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx index b1e7d23dca..118b12385a 100644 --- a/ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx +++ b/ui/addressesLabelSearch/AddressesLabelSearchTableItem.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import type { AddressesItem } from 'types/api/addresses'; +import type { AddressesItem } from 'client/slices/address/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; type Props = { diff --git a/ui/advancedFilter/ExportCSV.tsx b/ui/advancedFilter/ExportCSV.tsx deleted file mode 100644 index 2cae1d3221..0000000000 --- a/ui/advancedFilter/ExportCSV.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from 'react'; - -import type { AdvancedFilterParams } from 'types/api/advancedFilter'; - -import config from 'configs/app'; -import buildUrl from 'lib/api/buildUrl'; -import isNeedProxy from 'lib/api/isNeedProxy'; -import { useMultichainContext } from 'lib/contexts/multichain'; -import dayjs from 'lib/date/dayjs'; -import { Button } from 'toolkit/chakra/button'; -import { toaster } from 'toolkit/chakra/toaster'; -import { Tooltip } from 'toolkit/chakra/tooltip'; -import { downloadBlob } from 'toolkit/utils/file'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; - -type Props = { - filters: AdvancedFilterParams; -}; - -const ExportCSV = ({ filters }: Props) => { - const multichainContext = useMultichainContext(); - const recaptcha = useReCaptcha(); - - const [ isLoading, setIsLoading ] = React.useState(false); - - const apiFetchFactory = React.useCallback(async(recaptchaToken?: string) => { - const url = buildUrl('general:advanced_filter_csv', undefined, filters, undefined, multichainContext?.chain); - - const response = await fetch(url, { - headers: { - 'content-type': 'application/octet-stream', - ...(recaptchaToken && { 'recaptcha-v2-response': recaptchaToken }), - ...(isNeedProxy() && multichainContext?.chain ? { 'x-endpoint': multichainContext.chain.app_config.apis.general?.endpoint } : {}), - }, - }); - - if (!response.ok) { - throw new Error(response.statusText, { - cause: { - status: response.status, - }, - }); - } - - return response; - }, [ filters, multichainContext?.chain ]); - - const handleExportCSV = React.useCallback(async() => { - try { - setIsLoading(true); - - const response = await recaptcha.fetchProtectedResource(apiFetchFactory); - - const blob = await response.blob(); - - const chainText = multichainContext?.chain ? `${ multichainContext.chain.name.replace(' ', '-') }_` : ''; - const fileName = `${ chainText }export-filtered-txs-${ dayjs().format('YYYY-MM-DD-HH-mm-ss') }.csv`; - downloadBlob(blob, fileName); - - } catch (error) { - toaster.error({ - title: 'Error', - description: (error as Error)?.message || 'Something went wrong. Try again later.', - }); - } finally { - setIsLoading(false); - } - }, [ apiFetchFactory, recaptcha, multichainContext?.chain ]); - - const chainConfig = multichainContext?.chain.app_config || config; - - if (!chainConfig.services.reCaptchaV2.siteKey) { - return null; - } - - return ( - <> - - - - - - ); -}; - -export default ExportCSV; diff --git a/ui/advancedFilter/ItemByColumn.tsx b/ui/advancedFilter/ItemByColumn.tsx index 4123a39867..f5372fec8a 100644 --- a/ui/advancedFilter/ItemByColumn.tsx +++ b/ui/advancedFilter/ItemByColumn.tsx @@ -4,15 +4,16 @@ import React from 'react'; import type { AdvancedFilterResponseItem } from 'types/api/advancedFilter'; import type { ClusterChainConfig } from 'types/multichain'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import AddressFromToIcon from 'client/slices/address/components/from-to/AddressFromToIcon'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import config from 'configs/app'; import { isConfidentialTokenType } from 'lib/token/tokenTypes'; import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; import type { ColumnsIds } from 'ui/advancedFilter/constants'; -import AddressFromToIcon from 'ui/shared/address/AddressFromToIcon'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import AssetValue from 'ui/shared/value/AssetValue'; import ConfidentialValue from 'ui/shared/value/ConfidentialValue'; diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png index 02f426450a..2267c34def 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png index aaa7c6002f..5b8aa426a7 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png index 71dc696f32..22114ee435 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png index 79e0bf7b30..fe1394300b 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/filters/AssetFilter.tsx b/ui/advancedFilter/filters/AssetFilter.tsx index 84655af9da..73a950b55a 100644 --- a/ui/advancedFilter/filters/AssetFilter.tsx +++ b/ui/advancedFilter/filters/AssetFilter.tsx @@ -5,8 +5,10 @@ import React from 'react'; import type { AdvancedFilterParams } from 'types/api/advancedFilter'; import type { TokenInfo } from 'types/api/token'; -import useApiQuery from 'lib/api/useApiQuery'; -import useDebounce from 'lib/hooks/useDebounce'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import useDebounce from 'client/shared/hooks/useDebounce'; + import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; import { Select } from 'toolkit/chakra/select'; import { Tag } from 'toolkit/chakra/tag'; diff --git a/ui/advancedFilter/filters/MethodFilter.tsx b/ui/advancedFilter/filters/MethodFilter.tsx index e0a2b5131f..44319780aa 100644 --- a/ui/advancedFilter/filters/MethodFilter.tsx +++ b/ui/advancedFilter/filters/MethodFilter.tsx @@ -4,8 +4,10 @@ import React from 'react'; import type { AdvancedFilterMethodInfo, AdvancedFilterParams } from 'types/api/advancedFilter'; -import useApiQuery from 'lib/api/useApiQuery'; -import useDebounce from 'lib/hooks/useDebounce'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import useDebounce from 'client/shared/hooks/useDebounce'; + import { Badge } from 'toolkit/chakra/badge'; import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; diff --git a/ui/apiDocs/GraphQL.tsx b/ui/apiDocs/GraphQL.tsx index 3a7847f32e..f5d883fd76 100644 --- a/ui/apiDocs/GraphQL.tsx +++ b/ui/apiDocs/GraphQL.tsx @@ -3,10 +3,11 @@ import { createGraphiQLFetcher } from '@graphiql/toolkit'; import { GraphiQL } from 'graphiql'; import React from 'react'; -import config from 'configs/app'; -import buildUrl from 'lib/api/buildUrl'; +import buildUrl from 'client/api/build-url'; import 'graphiql/graphiql.css'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import config from 'configs/app'; import { useColorMode } from 'toolkit/chakra/color-mode'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import { ZERO_ADDRESS } from 'toolkit/utils/consts'; diff --git a/ui/apiDocs/RestApi.tsx b/ui/apiDocs/RestApi.tsx index 114030f9ca..c93a771fa6 100644 --- a/ui/apiDocs/RestApi.tsx +++ b/ui/apiDocs/RestApi.tsx @@ -2,8 +2,9 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { AccordionItem, AccordionItemContent, AccordionItemTrigger, AccordionRoot, useAccordion } from 'toolkit/chakra/accordion'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; diff --git a/ui/apiDocs/utils.spec.ts b/ui/apiDocs/utils.spec.ts index 4dabada7bb..0c1a846dd3 100644 --- a/ui/apiDocs/utils.spec.ts +++ b/ui/apiDocs/utils.spec.ts @@ -74,6 +74,30 @@ describe('coreApiRequestInterceptorFactory', () => { const result = interceptor(req); expect(result.url).toBe('https://example.com/api/v2/search'); }); + + test('does not produce double /api segment', () => { + const interceptor = coreApiRequestInterceptorFactory(api); + const req = { url: 'http://localhost/api/v2/tokens' }; + const result = interceptor(req); + expect(result.url).toBe('https://example.com/blockscout/api/v2/tokens'); + expect(result.url).not.toContain('/api/api/'); + }); + }); + + describe('DEFAULT_SERVER_NEW_BASE (http://localhost)', () => { + test('replaces localhost with endpoint + basePath for non-api paths', () => { + const interceptor = coreApiRequestInterceptorFactory(api); + const req = { url: 'http://localhost/v2/search?q=test' }; + const result = interceptor(req); + expect(result.url).toBe('https://example.com/blockscout/v2/search?q=test'); + }); + + test('works without basePath for non-api paths', () => { + const interceptor = coreApiRequestInterceptorFactory(apiWithoutBasePath); + const req = { url: 'http://localhost/v2/search' }; + const result = interceptor(req); + expect(result.url).toBe('https://example.com/v2/search'); + }); }); test('does not modify loadSpec requests', () => { diff --git a/ui/apiDocs/utils.ts b/ui/apiDocs/utils.ts index be0c4056ff..33a6f5dd55 100644 --- a/ui/apiDocs/utils.ts +++ b/ui/apiDocs/utils.ts @@ -5,6 +5,7 @@ import type { ApiPropsBase, ApiPropsFull } from 'configs/app/apis'; const DEFAULT_SERVER = 'blockscout.com/poa/core'; const DEFAULT_SERVER_NEW = 'http://localhost/api'; +const DEFAULT_SERVER_NEW_BASE = 'http://localhost'; export const coreApiRequestInterceptorFactory = (api: ApiPropsFull) => (req: SwaggerRequest): SwaggerRequest => { if (!req.loadSpec) { @@ -30,6 +31,10 @@ export const coreApiRequestInterceptorFactory = (api: ApiPropsFull) => (req: Swa return new URL(req.url.replace(DEFAULT_SERVER_NEW, `${ api.endpoint }${ api.basePath ?? '' }/api`)); } + if (req.url.includes(DEFAULT_SERVER_NEW_BASE)) { + return new URL(req.url.replace(DEFAULT_SERVER_NEW_BASE, `${ api.endpoint }${ api.basePath ?? '' }`)); + } + } catch (error) {} })(); diff --git a/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx b/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx index f1dfda18fd..46f8c1713e 100644 --- a/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx +++ b/ui/apiKey/ApiKeyModal/ApiKeyForm.tsx @@ -6,10 +6,12 @@ import { useForm, FormProvider } from 'react-hook-form'; import type { ApiKey, ApiKeys, ApiKeyErrors } from 'types/api/account'; -import type { ResourceErrorAccount } from 'lib/api/resources'; -import { resourceKey } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; -import getErrorMessage from 'lib/getErrorMessage'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import type { ResourceErrorAccount } from 'client/api/resources'; +import { resourceKey } from 'client/api/resources'; + +import getErrorMessage from 'client/features/account/utils/get-api-error-text'; + import { Button } from 'toolkit/chakra/button'; import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; diff --git a/ui/apiKey/DeleteApiKeyModal.tsx b/ui/apiKey/DeleteApiKeyModal.tsx index e09d85dcc7..b984b00e62 100644 --- a/ui/apiKey/DeleteApiKeyModal.tsx +++ b/ui/apiKey/DeleteApiKeyModal.tsx @@ -4,8 +4,9 @@ import React, { useCallback } from 'react'; import type { ApiKey, ApiKeys } from 'types/api/account'; -import { resourceKey } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import { resourceKey } from 'client/api/resources'; + import DeleteModal from 'ui/shared/DeleteModal'; type Props = { diff --git a/ui/blob/BlobData.tsx b/ui/blob/BlobData.tsx index fdbc0f80bd..657c64e807 100644 --- a/ui/blob/BlobData.tsx +++ b/ui/blob/BlobData.tsx @@ -1,12 +1,13 @@ import { createListCollection, Flex } from '@chakra-ui/react'; import React from 'react'; +import bytesToBase64 from 'client/shared/transformers/bytes-to-base64'; +import hexToBase64 from 'client/shared/transformers/hex-to-base64'; +import hexToBytes from 'client/shared/transformers/hex-to-bytes'; +import hexToUtf8 from 'client/shared/transformers/hex-to-utf8'; + import * as blobUtils from 'lib/blob'; import removeNonSignificantZeroBytes from 'lib/blob/removeNonSignificantZeroBytes'; -import bytesToBase64 from 'lib/bytesToBase64'; -import hexToBase64 from 'lib/hexToBase64'; -import hexToBytes from 'lib/hexToBytes'; -import hexToUtf8 from 'lib/hexToUtf8'; import { Button } from 'toolkit/chakra/button'; import type { SelectOption } from 'toolkit/chakra/select'; import { Select } from 'toolkit/chakra/select'; diff --git a/ui/blob/BlobInfo.tsx b/ui/blob/BlobInfo.tsx index 1dfe723498..5fee01239a 100644 --- a/ui/blob/BlobInfo.tsx +++ b/ui/blob/BlobInfo.tsx @@ -3,12 +3,13 @@ import React from 'react'; import type { Blob } from 'types/api/blobs'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import { Alert } from 'toolkit/chakra/alert'; import { Skeleton } from 'toolkit/chakra/skeleton'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoSponsoredItem from 'ui/shared/DetailedInfo/DetailedInfoSponsoredItem'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import BlobData from './BlobData'; diff --git a/ui/cluster/ClusterDetails.tsx b/ui/cluster/ClusterDetails.tsx index b331133b53..95cbd9fa47 100644 --- a/ui/cluster/ClusterDetails.tsx +++ b/ui/cluster/ClusterDetails.tsx @@ -2,12 +2,14 @@ import React from 'react'; import type { ClusterByNameResponse } from 'types/api/clusters'; -import { isEvmAddress } from 'lib/address/isEvmAddress'; -import { currencyUnits } from 'lib/units'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/contractVerification/ContractVerificationForm.tsx b/ui/contractVerification/ContractVerificationForm.tsx index 6dbf8cea19..b49151f97a 100644 --- a/ui/contractVerification/ContractVerificationForm.tsx +++ b/ui/contractVerification/ContractVerificationForm.tsx @@ -4,20 +4,22 @@ import type { SubmitHandler } from 'react-hook-form'; import { useForm, FormProvider } from 'react-hook-form'; import type { FormFields } from './types'; -import type { SocketMessage } from 'lib/socket/types'; +import type { SocketMessage } from 'client/api/socket/types'; import type { SmartContract, SmartContractVerificationMethodApi } from 'types/api/contract'; import type { SmartContractVerificationConfig } from 'types/client/contract'; import { route } from 'nextjs-routes'; -import useApiFetch from 'lib/api/useApiFetch'; -import capitalizeFirstLetter from 'lib/capitalizeFirstLetter'; -import delay from 'lib/delay'; -import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import getErrorObjStatusCode from 'client/shared/errors/get-error-obj-status-code'; +import capitalizeFirstLetter from 'client/shared/text/capitalize-first-letter'; +import delay from 'client/shared/utils/delay'; + import useRewardsActivity from 'lib/hooks/useRewardsActivity'; -import * as mixpanel from 'lib/mixpanel/index'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; import { Button } from 'toolkit/chakra/button'; import { toaster } from 'toolkit/chakra/toaster'; import { useUpdateEffect } from 'toolkit/hooks/useUpdateEffect'; diff --git a/ui/contractVerification/ContractVerificationFormRow.tsx b/ui/contractVerification/ContractVerificationFormRow.tsx index db676b9731..3b5a231906 100644 --- a/ui/contractVerification/ContractVerificationFormRow.tsx +++ b/ui/contractVerification/ContractVerificationFormRow.tsx @@ -1,7 +1,7 @@ import { chakra, GridItem } from '@chakra-ui/react'; import React from 'react'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; interface Props { children: [React.JSX.Element, React.JSX.Element | null] | (React.JSX.Element | null); diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_dark-color-mode_flatten-source-code-method-dark-mode-mobile-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_dark-color-mode_flatten-source-code-method-dark-mode-mobile-1.png index cc07291097..04d49edae5 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_dark-color-mode_flatten-source-code-method-dark-mode-mobile-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_dark-color-mode_flatten-source-code-method-dark-mode-mobile-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_flatten-source-code-method-dark-mode-mobile-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_flatten-source-code-method-dark-mode-mobile-1.png index bcf092c002..78f2b4f92c 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_flatten-source-code-method-dark-mode-mobile-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_flatten-source-code-method-dark-mode-mobile-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_multi-part-files-method-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_multi-part-files-method-1.png index ee931353bf..27295171ba 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_multi-part-files-method-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_multi-part-files-method-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-foundry-method-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-foundry-method-1.png index cc9fd63f8c..d016610740 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-foundry-method-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-foundry-method-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-hardhat-method-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-hardhat-method-1.png index d716d8ad00..6f374e5336 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-hardhat-method-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_solidity-hardhat-method-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_standard-input-json-method-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_standard-input-json-method-1.png index fd2cc5f1ae..c764623544 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_standard-input-json-method-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_standard-input-json-method-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-stylus-rust-contract-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-stylus-rust-contract-1.png index 1faad87ac7..a0b58be284 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-stylus-rust-contract-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-stylus-rust-contract-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-zkSync-contract-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-zkSync-contract-1.png index 4ec2f3e6a7..67c41a111b 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-zkSync-contract-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_default_verification-of-zkSync-contract-1.png differ diff --git a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_mobile_flatten-source-code-method-dark-mode-mobile-1.png b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_mobile_flatten-source-code-method-dark-mode-mobile-1.png index 322c801ab5..10da6c6d25 100644 Binary files a/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_mobile_flatten-source-code-method-dark-mode-mobile-1.png and b/ui/contractVerification/__screenshots__/ContractVerificationForm.pw.tsx_mobile_flatten-source-code-method-dark-mode-mobile-1.png differ diff --git a/ui/contractVerification/fields/ContractVerificationFieldCommit.tsx b/ui/contractVerification/fields/ContractVerificationFieldCommit.tsx index 4a91ac4f11..e4d3dffc55 100644 --- a/ui/contractVerification/fields/ContractVerificationFieldCommit.tsx +++ b/ui/contractVerification/fields/ContractVerificationFieldCommit.tsx @@ -4,8 +4,10 @@ import { useFormContext } from 'react-hook-form'; import type { FormFields } from '../types'; -import delay from 'lib/delay'; -import useFetch from 'lib/hooks/useFetch'; +import useFetch from 'client/api/hooks/useFetch'; + +import delay from 'client/shared/utils/delay'; + import { Link } from 'toolkit/chakra/link'; import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; diff --git a/ui/contractVerification/fields/ContractVerificationFieldGitHubRepo.tsx b/ui/contractVerification/fields/ContractVerificationFieldGitHubRepo.tsx index 3873f95622..ce11490fd3 100644 --- a/ui/contractVerification/fields/ContractVerificationFieldGitHubRepo.tsx +++ b/ui/contractVerification/fields/ContractVerificationFieldGitHubRepo.tsx @@ -4,8 +4,10 @@ import { useFormContext } from 'react-hook-form'; import type { FormFields } from '../types'; -import delay from 'lib/delay'; -import useFetch from 'lib/hooks/useFetch'; +import useFetch from 'client/api/hooks/useFetch'; + +import delay from 'client/shared/utils/delay'; + import { FormFieldUrl } from 'toolkit/components/forms/fields/FormFieldUrl'; import ContractVerificationFormRow from '../ContractVerificationFormRow'; diff --git a/ui/contractVerification/useFormConfigQuery.tsx b/ui/contractVerification/useFormConfigQuery.tsx index 4a28edec85..56bbcf6a51 100644 --- a/ui/contractVerification/useFormConfigQuery.tsx +++ b/ui/contractVerification/useFormConfigQuery.tsx @@ -1,5 +1,6 @@ +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { isValidVerificationMethod, sortVerificationMethods } from './utils'; diff --git a/ui/contractVerification/utils.ts b/ui/contractVerification/utils.ts index b72c38da15..e2c1887e76 100644 --- a/ui/contractVerification/utils.ts +++ b/ui/contractVerification/utils.ts @@ -18,7 +18,8 @@ import type { } from 'types/api/contract'; import type { SmartContractVerificationConfig, SmartContractVerificationMethod } from 'types/client/contract'; -import type { Params as FetchParams } from 'lib/hooks/useFetch'; +import type { Params as FetchParams } from 'client/api/hooks/useFetch'; + import { stripLeadingSlash } from 'toolkit/utils/url'; export const SUPPORTED_VERIFICATION_METHODS: Array = [ diff --git a/ui/crossChain/address/AddressTxsCrossChain.tsx b/ui/crossChain/address/AddressTxsCrossChain.tsx index f9e4c1eb94..38a633895d 100644 --- a/ui/crossChain/address/AddressTxsCrossChain.tsx +++ b/ui/crossChain/address/AddressTxsCrossChain.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useTableViewValue from 'lib/hooks/useTableViewValue'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useTableViewValue from 'client/shared/hooks/useTableViewValue'; + import ActionBar from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; import TableViewToggleButton from 'ui/shared/TableViewToggleButton'; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChain.tsx b/ui/crossChain/transfers/TokenTransfersCrossChain.tsx index 6fb5032b2c..5a9f8d30b1 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChain.tsx +++ b/ui/crossChain/transfers/TokenTransfersCrossChain.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { INTERCHAIN_STATS_COMMON, INTERCHAIN_TRANSFER } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChainListItem.tsx b/ui/crossChain/transfers/TokenTransfersCrossChainListItem.tsx index 6faf941294..b1888ba5c3 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChainListItem.tsx +++ b/ui/crossChain/transfers/TokenTransfersCrossChainListItem.tsx @@ -3,15 +3,16 @@ import React from 'react'; import type { InterchainTransfer } from '@blockscout/interchain-indexer-types'; +import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; +import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; + import config from 'configs/app'; import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { mdash } from 'toolkit/utils/htmlEntities'; import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import AddressEntityInterchain from 'ui/shared/entities/address/AddressEntityInterchain'; import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; -import TxEntityInterchain from 'ui/shared/entities/tx/TxEntityInterchain'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChainTable.tsx b/ui/crossChain/transfers/TokenTransfersCrossChainTable.tsx index eb8e423951..d06a062892 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChainTable.tsx +++ b/ui/crossChain/transfers/TokenTransfersCrossChainTable.tsx @@ -3,7 +3,8 @@ import React from 'react'; import type { InterchainTransfer } from '@blockscout/interchain-indexer-types'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/crossChain/transfers/TokenTransfersCrossChainTableItem.tsx b/ui/crossChain/transfers/TokenTransfersCrossChainTableItem.tsx index d702c9578e..f7ea429a4c 100644 --- a/ui/crossChain/transfers/TokenTransfersCrossChainTableItem.tsx +++ b/ui/crossChain/transfers/TokenTransfersCrossChainTableItem.tsx @@ -3,15 +3,16 @@ import React from 'react'; import type { InterchainTransfer } from '@blockscout/interchain-indexer-types'; +import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; +import AddressFromToIcon from 'client/slices/address/components/from-to/AddressFromToIcon'; +import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; + import config from 'configs/app'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { mdash } from 'toolkit/utils/htmlEntities'; -import AddressFromToIcon from 'ui/shared/address/AddressFromToIcon'; import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import AddressEntityInterchain from 'ui/shared/entities/address/AddressEntityInterchain'; import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; -import TxEntityInterchain from 'ui/shared/entities/tx/TxEntityInterchain'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/crossChain/tx/TxCrossChain.tsx b/ui/crossChain/tx/TxCrossChain.tsx index cf004b6d26..65fd2e9700 100644 --- a/ui/crossChain/tx/TxCrossChain.tsx +++ b/ui/crossChain/tx/TxCrossChain.tsx @@ -3,9 +3,11 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; -import useApiQuery from 'lib/api/useApiQuery'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import TextAd from 'ui/shared/ad/TextAd'; diff --git a/ui/crossChain/tx/TxCrossChainDetails.tsx b/ui/crossChain/tx/TxCrossChainDetails.tsx index 97d788445f..25a62764c8 100644 --- a/ui/crossChain/tx/TxCrossChainDetails.tsx +++ b/ui/crossChain/tx/TxCrossChainDetails.tsx @@ -2,11 +2,12 @@ import React from 'react'; import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; +import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; + import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import AddressEntityInterchain from 'ui/shared/entities/address/AddressEntityInterchain'; import RawInputData from 'ui/shared/RawInputData'; import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; diff --git a/ui/crossChain/tx/TxCrossChainDetailsLifecycle.tsx b/ui/crossChain/tx/TxCrossChainDetailsLifecycle.tsx index 4b733d2f23..2071042b77 100644 --- a/ui/crossChain/tx/TxCrossChainDetailsLifecycle.tsx +++ b/ui/crossChain/tx/TxCrossChainDetailsLifecycle.tsx @@ -3,8 +3,9 @@ import React from 'react'; import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; import { MessageStatus } from '@blockscout/interchain-indexer-types'; +import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; + import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import TxEntityInterchain from 'ui/shared/entities/tx/TxEntityInterchain'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; import { Root, Item, Trigger, ItemContent, ItemBody, ItemRow } from 'ui/shared/lifecycle/LifecycleAccordion'; diff --git a/ui/crossChain/tx/TxCrossChainDetailsTransfers.tsx b/ui/crossChain/tx/TxCrossChainDetailsTransfers.tsx index 994440c450..69e4f0221b 100644 --- a/ui/crossChain/tx/TxCrossChainDetailsTransfers.tsx +++ b/ui/crossChain/tx/TxCrossChainDetailsTransfers.tsx @@ -5,11 +5,12 @@ import type { InterchainTransfer } from '@blockscout/interchain-indexer-types'; import { route } from 'nextjs-routes'; +import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; +import AddressFromToIcon from 'client/slices/address/components/from-to/AddressFromToIcon'; + import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressFromToIcon from 'ui/shared/address/AddressFromToIcon'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; -import AddressEntityInterchain from 'ui/shared/entities/address/AddressEntityInterchain'; import IconSvg from 'ui/shared/IconSvg'; import TokenValueInterchain from 'ui/shared/value/TokenValueInterchain'; diff --git a/ui/crossChain/txs/Transactions.tsx b/ui/crossChain/txs/Transactions.tsx index 82533cc9ad..b73e397b19 100644 --- a/ui/crossChain/txs/Transactions.tsx +++ b/ui/crossChain/txs/Transactions.tsx @@ -2,11 +2,12 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import TxsTabs, { getTabId } from 'client/slices/tx/pages/index/list/TxsTabs'; +import TxsStats from 'client/slices/tx/pages/index/stats/TxsStats'; + import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import PageTitle from 'ui/shared/Page/PageTitle'; -import TxsStats from 'ui/txs/TxsStats'; -import TxsTabs, { getTabId } from 'ui/txs/TxsTabs'; import TransactionsCrossChain from './TransactionsCrossChain'; diff --git a/ui/crossChain/txs/TransactionsCrossChain.tsx b/ui/crossChain/txs/TransactionsCrossChain.tsx index de0b3bee42..a16df3458b 100644 --- a/ui/crossChain/txs/TransactionsCrossChain.tsx +++ b/ui/crossChain/txs/TransactionsCrossChain.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { INTERCHAIN_MESSAGE, INTERCHAIN_STATS_COMMON } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/crossChain/txs/TransactionsCrossChainListItem.tsx b/ui/crossChain/txs/TransactionsCrossChainListItem.tsx index 07fa3efb33..9ec4ce52d1 100644 --- a/ui/crossChain/txs/TransactionsCrossChainListItem.tsx +++ b/ui/crossChain/txs/TransactionsCrossChainListItem.tsx @@ -6,6 +6,9 @@ import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; import { route } from 'nextjs-routes'; +import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; +import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; + import config from 'configs/app'; import dayjs from 'lib/date/dayjs'; import { Link } from 'toolkit/chakra/link'; @@ -13,9 +16,7 @@ import { Skeleton } from 'toolkit/chakra/skeleton'; import { mdash } from 'toolkit/utils/htmlEntities'; import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import AddressEntityInterchain from 'ui/shared/entities/address/AddressEntityInterchain'; import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; -import TxEntityInterchain from 'ui/shared/entities/tx/TxEntityInterchain'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/ui/crossChain/txs/TransactionsCrossChainStats.tsx b/ui/crossChain/txs/TransactionsCrossChainStats.tsx index 14c33e46e8..af0c3a1502 100644 --- a/ui/crossChain/txs/TransactionsCrossChainStats.tsx +++ b/ui/crossChain/txs/TransactionsCrossChainStats.tsx @@ -1,7 +1,8 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { INTERCHAIN_STATS_DAILY } from 'stubs/interchainIndexer'; import StatsWidget from 'ui/shared/stats/StatsWidget'; diff --git a/ui/crossChain/txs/TransactionsCrossChainTable.tsx b/ui/crossChain/txs/TransactionsCrossChainTable.tsx index f6011040e5..67d135f022 100644 --- a/ui/crossChain/txs/TransactionsCrossChainTable.tsx +++ b/ui/crossChain/txs/TransactionsCrossChainTable.tsx @@ -3,7 +3,8 @@ import React from 'react'; import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { TableBody, TableColumnHeader, TableHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/crossChain/txs/TransactionsCrossChainTableItem.tsx b/ui/crossChain/txs/TransactionsCrossChainTableItem.tsx index bceb1554e8..670707f0eb 100644 --- a/ui/crossChain/txs/TransactionsCrossChainTableItem.tsx +++ b/ui/crossChain/txs/TransactionsCrossChainTableItem.tsx @@ -5,17 +5,18 @@ import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; import { route } from 'nextjs-routes'; +import AddressEntityInterchain from 'client/slices/address/components/entity/AddressEntityInterchain'; +import AddressFromToIcon from 'client/slices/address/components/from-to/AddressFromToIcon'; +import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; + import config from 'configs/app'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { mdash } from 'toolkit/utils/htmlEntities'; -import AddressFromToIcon from 'ui/shared/address/AddressFromToIcon'; import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; import CrossChainFromToTag from 'ui/shared/crossChain/CrossChainFromToTag'; -import AddressEntityInterchain from 'ui/shared/entities/address/AddressEntityInterchain'; import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; -import TxEntityInterchain from 'ui/shared/entities/tx/TxEntityInterchain'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/csvExport/CsvExportForm.tsx b/ui/csvExport/CsvExportForm.tsx deleted file mode 100644 index 31f16b3942..0000000000 --- a/ui/csvExport/CsvExportForm.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { chakra, Flex } from '@chakra-ui/react'; -import React from 'react'; -import type { SubmitHandler } from 'react-hook-form'; -import { useForm, FormProvider } from 'react-hook-form'; - -import type { FormFields } from './types'; -import type { CsvExportParams } from 'types/client/address'; - -import config from 'configs/app'; -import buildUrl from 'lib/api/buildUrl'; -import isNeedProxy from 'lib/api/isNeedProxy'; -import type { ResourceName } from 'lib/api/resources'; -import { useMultichainContext } from 'lib/contexts/multichain'; -import dayjs from 'lib/date/dayjs'; -import { Alert } from 'toolkit/chakra/alert'; -import { Button } from 'toolkit/chakra/button'; -import { toaster } from 'toolkit/chakra/toaster'; -import { downloadBlob } from 'toolkit/utils/file'; -import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; -import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; - -import CsvExportFormField from './CsvExportFormField'; - -interface Props { - hash: string; - resource: ResourceName; - filterType?: CsvExportParams['filterType'] | null; - filterValue?: CsvExportParams['filterValue'] | null; - fileNameTemplate: string; - exportType: CsvExportParams['type'] | undefined; -} - -const CsvExportForm = ({ hash, resource, filterType, filterValue, fileNameTemplate, exportType }: Props) => { - const formApi = useForm({ - mode: 'onBlur', - defaultValues: { - from: dayjs().subtract(1, 'day').format('YYYY-MM-DDTHH:mm'), - to: dayjs().format('YYYY-MM-DDTHH:mm'), - }, - }); - const { handleSubmit, formState } = formApi; - const recaptcha = useReCaptcha(); - const multichainContext = useMultichainContext(); - - const chainConfig = multichainContext?.chain.app_config || config; - - const apiFetchFactory = React.useCallback((data: FormFields) => { - return async(recaptchaToken?: string) => { - const url = buildUrl(resource, { hash } as never, { - from_period: exportType !== 'holders' ? dayjs(data.from).toISOString() : null, - to_period: exportType !== 'holders' ? dayjs(data.to).toISOString() : null, - filter_type: filterType, - filter_value: filterValue, - }, undefined, multichainContext?.chain); - - const response = await fetch(url, { - headers: { - 'content-type': 'application/octet-stream', - ...(recaptchaToken && { 'recaptcha-v2-response': recaptchaToken }), - ...(isNeedProxy() && multichainContext?.chain ? { 'x-endpoint': multichainContext.chain.app_config.apis.general?.endpoint } : {}), - }, - }); - - if (!response.ok) { - throw new Error(response.statusText, { - cause: { - status: response.status, - }, - }); - } - - return response; - }; - }, [ resource, hash, exportType, filterType, filterValue, multichainContext?.chain ]); - - const onFormSubmit: SubmitHandler = React.useCallback(async(data) => { - try { - const response = await recaptcha.fetchProtectedResource(apiFetchFactory(data)); - const chainText = multichainContext?.chain ? `${ multichainContext.chain.name.replace(' ', '-') }_` : ''; - - const blob = await response.blob(); - const fileName = exportType === 'holders' ? - `${ chainText }${ fileNameTemplate }_${ hash }.csv` : - // eslint-disable-next-line max-len - `${ chainText }${ fileNameTemplate }_${ hash }_${ data.from }_${ data.to }${ filterType && filterValue ? '_with_filter_type_' + filterType + '_value_' + filterValue : '' }.csv`; - downloadBlob(blob, fileName); - - } catch (error) { - toaster.error({ - title: 'Error', - description: (error as Error)?.message || 'Something went wrong. Try again later.', - }); - } - - }, [ recaptcha, apiFetchFactory, multichainContext?.chain, exportType, fileNameTemplate, hash, filterType, filterValue ]); - - if (!chainConfig.services.reCaptchaV2.siteKey) { - return ( - - CSV export is not available at the moment since reCaptcha is not configured for this application. - Please contact the service maintainer to make necessary changes in the service configuration. - - ); - } - - return ( - - - - { exportType !== 'holders' && } - { exportType !== 'holders' && } - - - - - - ); -}; - -export default React.memo(CsvExportForm); diff --git a/ui/csvExport/types.ts b/ui/csvExport/types.ts deleted file mode 100644 index ca5ec6a0c2..0000000000 --- a/ui/csvExport/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface FormFields { - from: string; - to: string; -} diff --git a/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx b/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx index 393b2eb9fe..a882989272 100644 --- a/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx +++ b/ui/customAbi/CustomAbiModal/CustomAbiForm.tsx @@ -6,10 +6,12 @@ import { useForm, FormProvider } from 'react-hook-form'; import type { CustomAbi, CustomAbis, CustomAbiErrors } from 'types/api/account'; -import type { ResourceErrorAccount } from 'lib/api/resources'; -import { resourceKey } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; -import getErrorMessage from 'lib/getErrorMessage'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import type { ResourceErrorAccount } from 'client/api/resources'; +import { resourceKey } from 'client/api/resources'; + +import getErrorMessage from 'client/features/account/utils/get-api-error-text'; + import { Button } from 'toolkit/chakra/button'; import { FormFieldAddress } from 'toolkit/components/forms/fields/FormFieldAddress'; import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; diff --git a/ui/customAbi/CustomAbiTable/CustomAbiListItem.tsx b/ui/customAbi/CustomAbiTable/CustomAbiListItem.tsx index 69d2fc28e7..c22f516342 100644 --- a/ui/customAbi/CustomAbiTable/CustomAbiListItem.tsx +++ b/ui/customAbi/CustomAbiTable/CustomAbiListItem.tsx @@ -3,8 +3,9 @@ import React, { useCallback } from 'react'; import type { CustomAbi } from 'types/api/account'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; diff --git a/ui/customAbi/CustomAbiTable/CustomAbiTableItem.tsx b/ui/customAbi/CustomAbiTable/CustomAbiTableItem.tsx index a23cb121c8..10678e5551 100644 --- a/ui/customAbi/CustomAbiTable/CustomAbiTableItem.tsx +++ b/ui/customAbi/CustomAbiTable/CustomAbiTableItem.tsx @@ -3,9 +3,10 @@ import React, { useCallback } from 'react'; import type { CustomAbi } from 'types/api/account'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; interface Props { diff --git a/ui/customAbi/DeleteCustomAbiModal.tsx b/ui/customAbi/DeleteCustomAbiModal.tsx index 95a6ba0708..b0cdd594a3 100644 --- a/ui/customAbi/DeleteCustomAbiModal.tsx +++ b/ui/customAbi/DeleteCustomAbiModal.tsx @@ -4,8 +4,9 @@ import React, { useCallback } from 'react'; import type { CustomAbi, CustomAbis } from 'types/api/account'; -import { resourceKey } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import { resourceKey } from 'client/api/resources'; + import DeleteModal from 'ui/shared/DeleteModal'; type Props = { diff --git a/ui/deposits/beaconChain/BeaconChainDepositsList.tsx b/ui/deposits/beaconChain/BeaconChainDepositsList.tsx index d622fe1596..fe4b5bf0d1 100644 --- a/ui/deposits/beaconChain/BeaconChainDepositsList.tsx +++ b/ui/deposits/beaconChain/BeaconChainDepositsList.tsx @@ -3,7 +3,7 @@ import React from 'react'; import type { DepositsItem } from 'types/api/deposits'; -import useLazyRenderedList from 'lib/hooks/useLazyRenderedList'; +import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; import BeaconChainDepositsListItem from './BeaconChainDepositsListItem'; diff --git a/ui/deposits/beaconChain/BeaconChainDepositsListItem.tsx b/ui/deposits/beaconChain/BeaconChainDepositsListItem.tsx index bce646e5f4..a900dcdbe3 100644 --- a/ui/deposits/beaconChain/BeaconChainDepositsListItem.tsx +++ b/ui/deposits/beaconChain/BeaconChainDepositsListItem.tsx @@ -2,13 +2,14 @@ import React from 'react'; import type { DepositsItem } from 'types/api/deposits'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import config from 'configs/app'; import BeaconChainDepositSignature from 'ui/shared/beacon/BeaconChainDepositSignature'; import BeaconChainDepositStatusTag from 'ui/shared/beacon/BeaconChainDepositStatusTag'; import BeaconChainValidatorLink from 'ui/shared/beacon/BeaconChainValidatorLink'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/deposits/beaconChain/BeaconChainDepositsTable.tsx b/ui/deposits/beaconChain/BeaconChainDepositsTable.tsx index c21c2183db..07a4f2255b 100644 --- a/ui/deposits/beaconChain/BeaconChainDepositsTable.tsx +++ b/ui/deposits/beaconChain/BeaconChainDepositsTable.tsx @@ -2,9 +2,11 @@ import React from 'react'; import type { DepositsItem } from 'types/api/deposits'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + +import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; + import config from 'configs/app'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import useLazyRenderedList from 'lib/hooks/useLazyRenderedList'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/deposits/beaconChain/BeaconChainDepositsTableItem.tsx b/ui/deposits/beaconChain/BeaconChainDepositsTableItem.tsx index 9bc9f0b742..98424306f5 100644 --- a/ui/deposits/beaconChain/BeaconChainDepositsTableItem.tsx +++ b/ui/deposits/beaconChain/BeaconChainDepositsTableItem.tsx @@ -2,13 +2,14 @@ import React from 'react'; import type { DepositsItem } from 'types/api/deposits'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import { TableCell, TableRow } from 'toolkit/chakra/table'; import BeaconChainDepositSignature from 'ui/shared/beacon/BeaconChainDepositSignature'; import BeaconChainDepositStatusTag from 'ui/shared/beacon/BeaconChainDepositStatusTag'; import BeaconChainValidatorLink from 'ui/shared/beacon/BeaconChainValidatorLink'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/deposits/optimisticL2/OptimisticDepositsListItem.tsx b/ui/deposits/optimisticL2/OptimisticDepositsListItem.tsx index 7c66104b45..d8f04ee12b 100644 --- a/ui/deposits/optimisticL2/OptimisticDepositsListItem.tsx +++ b/ui/deposits/optimisticL2/OptimisticDepositsListItem.tsx @@ -3,13 +3,15 @@ import React from 'react'; import type { OptimisticL2DepositsItem } from 'types/api/optimisticL2'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import AddressEntityL1 from 'client/features/rollup/common/components/AddressEntityL1'; +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntityL1 from 'ui/shared/entities/address/AddressEntityL1'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/deposits/optimisticL2/OptimisticDepositsTable.tsx b/ui/deposits/optimisticL2/OptimisticDepositsTable.tsx index 92d6aea902..836156e3d8 100644 --- a/ui/deposits/optimisticL2/OptimisticDepositsTable.tsx +++ b/ui/deposits/optimisticL2/OptimisticDepositsTable.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type { OptimisticL2DepositsItem } from 'types/api/optimisticL2'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { layerLabels } from 'lib/rollups/utils'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/deposits/optimisticL2/OptimisticDepositsTableItem.tsx b/ui/deposits/optimisticL2/OptimisticDepositsTableItem.tsx index 0a547198f2..dab2e3ce59 100644 --- a/ui/deposits/optimisticL2/OptimisticDepositsTableItem.tsx +++ b/ui/deposits/optimisticL2/OptimisticDepositsTableItem.tsx @@ -3,13 +3,15 @@ import React from 'react'; import type { OptimisticL2DepositsItem } from 'types/api/optimisticL2'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import AddressEntityL1 from 'client/features/rollup/common/components/AddressEntityL1'; +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntityL1 from 'ui/shared/entities/address/AddressEntityL1'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; const rollupFeature = config.features.rollup; diff --git a/ui/deposits/scrollL2/ScrollL2DepositsListItem.tsx b/ui/deposits/scrollL2/ScrollL2DepositsListItem.tsx index 7b8c456892..6616b7677a 100644 --- a/ui/deposits/scrollL2/ScrollL2DepositsListItem.tsx +++ b/ui/deposits/scrollL2/ScrollL2DepositsListItem.tsx @@ -3,12 +3,14 @@ import React from 'react'; import type { ScrollL2MessageItem } from 'types/api/scrollL2'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/deposits/scrollL2/ScrollL2DepositsTable.tsx b/ui/deposits/scrollL2/ScrollL2DepositsTable.tsx index 589de5c833..11b2dddfe0 100644 --- a/ui/deposits/scrollL2/ScrollL2DepositsTable.tsx +++ b/ui/deposits/scrollL2/ScrollL2DepositsTable.tsx @@ -2,8 +2,9 @@ import React from 'react'; import type { ScrollL2MessageItem } from 'types/api/scrollL2'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import config from 'configs/app'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { layerLabels } from 'lib/rollups/utils'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/deposits/scrollL2/ScrollL2DepositsTableItem.tsx b/ui/deposits/scrollL2/ScrollL2DepositsTableItem.tsx index 5d6457c71b..7380cc5bbc 100644 --- a/ui/deposits/scrollL2/ScrollL2DepositsTableItem.tsx +++ b/ui/deposits/scrollL2/ScrollL2DepositsTableItem.tsx @@ -3,12 +3,14 @@ import React from 'react'; import type { ScrollL2MessageItem } from 'types/api/scrollL2'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/deposits/shibarium/DepositsListItem.tsx b/ui/deposits/shibarium/DepositsListItem.tsx index bdb4cbed31..6c32c0c6a0 100644 --- a/ui/deposits/shibarium/DepositsListItem.tsx +++ b/ui/deposits/shibarium/DepositsListItem.tsx @@ -2,12 +2,14 @@ import React from 'react'; import type { ShibariumDepositsItem } from 'types/api/shibarium'; +import AddressStringOrParam from 'client/slices/address/components/entity/AddressStringOrParam'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; -import AddressStringOrParam from 'ui/shared/entities/address/AddressStringOrParam'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/deposits/shibarium/DepositsTable.tsx b/ui/deposits/shibarium/DepositsTable.tsx index 5ee44bc30f..90f00f2d89 100644 --- a/ui/deposits/shibarium/DepositsTable.tsx +++ b/ui/deposits/shibarium/DepositsTable.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type { ShibariumDepositsItem } from 'types/api/shibarium'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { layerLabels } from 'lib/rollups/utils'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/deposits/shibarium/DepositsTableItem.tsx b/ui/deposits/shibarium/DepositsTableItem.tsx index 4155f30f0d..14363ffb50 100644 --- a/ui/deposits/shibarium/DepositsTableItem.tsx +++ b/ui/deposits/shibarium/DepositsTableItem.tsx @@ -2,12 +2,14 @@ import React from 'react'; import type { ShibariumDepositsItem } from 'types/api/shibarium'; +import AddressStringOrParam from 'client/slices/address/components/entity/AddressStringOrParam'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressStringOrParam from 'ui/shared/entities/address/AddressStringOrParam'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; const feature = config.features.rollup; diff --git a/ui/deposits/zkEvmL2/ZkEvmL2DepositsListItem.tsx b/ui/deposits/zkEvmL2/ZkEvmL2DepositsListItem.tsx deleted file mode 100644 index 6097aeeccc..0000000000 --- a/ui/deposits/zkEvmL2/ZkEvmL2DepositsListItem.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { chakra } from '@chakra-ui/react'; -import BigNumber from 'bignumber.js'; -import React from 'react'; - -import type { ZkEvmL2DepositsItem } from 'types/api/zkEvmL2'; - -import config from 'configs/app'; -import { layerLabels } from 'lib/rollups/utils'; -import { Skeleton } from 'toolkit/chakra/skeleton'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; -import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; -import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; -import SimpleValue from 'ui/shared/value/SimpleValue'; - -const rollupFeature = config.features.rollup; - -type Props = { item: ZkEvmL2DepositsItem; isLoading?: boolean }; - -const ZkEvmL2DepositsListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') { - return null; - } - - return ( - - - { layerLabels.parent } block - - - - - Index - - - { item.index } - - - - { layerLabels.parent } txn hash - - - - - Age - - - - - { layerLabels.current } txn hash - - { item.l2_transaction_hash ? ( - - ) : ( - - Pending Claim - - ) } - - - Value - - - - - Token - - - { item.symbol } - - - - - ); -}; - -export default ZkEvmL2DepositsListItem; diff --git a/ui/deposits/zkEvmL2/ZkEvmL2DepositsTable.tsx b/ui/deposits/zkEvmL2/ZkEvmL2DepositsTable.tsx deleted file mode 100644 index 1ac7d137d6..0000000000 --- a/ui/deposits/zkEvmL2/ZkEvmL2DepositsTable.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; - -import type { ZkEvmL2DepositsItem } from 'types/api/zkEvmL2'; - -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import { layerLabels } from 'lib/rollups/utils'; -import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; -import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; - -import ZkEvmL2DepositsTableItem from './ZkEvmL2DepositsTableItem'; - -type Props = { - items: Array; - top: number; - isLoading?: boolean; -}; - -const ZkEvmL2DepositsTable = ({ items, top, isLoading }: Props) => { - return ( - - - - - { layerLabels.parent } block - Index - { layerLabels.parent } txn hash - - Timestamp - - - { layerLabels.current } txn hash - Value - Token - - - - { items.map((item, index) => ( - - )) } - - - - ); -}; - -export default ZkEvmL2DepositsTable; diff --git a/ui/deposits/zkEvmL2/ZkEvmL2DepositsTableItem.tsx b/ui/deposits/zkEvmL2/ZkEvmL2DepositsTableItem.tsx deleted file mode 100644 index 62660155f6..0000000000 --- a/ui/deposits/zkEvmL2/ZkEvmL2DepositsTableItem.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { chakra } from '@chakra-ui/react'; -import BigNumber from 'bignumber.js'; -import React from 'react'; - -import type { ZkEvmL2DepositsItem } from 'types/api/zkEvmL2'; - -import config from 'configs/app'; -import { Skeleton } from 'toolkit/chakra/skeleton'; -import { TableCell, TableRow } from 'toolkit/chakra/table'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; -import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; -import SimpleValue from 'ui/shared/value/SimpleValue'; - -const rollupFeature = config.features.rollup; - -type Props = { item: ZkEvmL2DepositsItem; isLoading?: boolean }; - -const ZkEvmL2DepositsTableItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkEvm') { - return null; - } - - return ( - - - - - - - { item.index } -
- - - - - - - - - { item.l2_transaction_hash ? ( - - ) : ( - - Pending Claim - - ) } - - - - - - - { item.symbol } - - - - ); -}; - -export default ZkEvmL2DepositsTableItem; diff --git a/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesListItem.tsx b/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesListItem.tsx index 6c05190a00..2a484fb76d 100644 --- a/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesListItem.tsx +++ b/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesListItem.tsx @@ -2,11 +2,12 @@ import React from 'react'; import type { OptimisticL2DisputeGamesItem } from 'types/api/optimisticL2'; +import BlockEntityL2 from 'client/features/rollup/common/components/BlockEntityL2'; + import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2'; import HashStringShorten from 'ui/shared/HashStringShorten'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesTableItem.tsx b/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesTableItem.tsx index f0c953f8af..d2217b1f29 100644 --- a/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesTableItem.tsx +++ b/ui/disputeGames/optimisticL2/OptimisticL2DisputeGamesTableItem.tsx @@ -3,11 +3,12 @@ import React from 'react'; import type { OptimisticL2DisputeGamesItem } from 'types/api/optimisticL2'; +import BlockEntityL2 from 'client/features/rollup/common/components/BlockEntityL2'; + import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2'; import HashStringShorten from 'ui/shared/HashStringShorten'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/epochs/EpochDetails.tsx b/ui/epochs/EpochDetails.tsx index c6a779452e..22898a73c6 100644 --- a/ui/epochs/EpochDetails.tsx +++ b/ui/epochs/EpochDetails.tsx @@ -3,10 +3,12 @@ import React from 'react'; import type { CeloEpochDetails } from 'types/api/epochs'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import CeloEpochStatus from 'ui/shared/statusTag/CeloEpochStatus'; import TokenTransferSnippet from 'ui/shared/TokenTransferSnippet/TokenTransferSnippet'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/epochs/electionRewards/EpochElectionRewardDetailsDesktop.tsx b/ui/epochs/electionRewards/EpochElectionRewardDetailsDesktop.tsx index a5ae3982b1..934e1b2d2b 100644 --- a/ui/epochs/electionRewards/EpochElectionRewardDetailsDesktop.tsx +++ b/ui/epochs/electionRewards/EpochElectionRewardDetailsDesktop.tsx @@ -5,9 +5,11 @@ import React from 'react'; import type { CeloEpochDetails } from 'types/api/epochs'; import type { TokenInfo } from 'types/api/token'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import useLazyLoadedList from 'ui/shared/pagination/useLazyLoadedList'; import AssetValue from 'ui/shared/value/AssetValue'; diff --git a/ui/epochs/electionRewards/EpochElectionRewardDetailsMobile.tsx b/ui/epochs/electionRewards/EpochElectionRewardDetailsMobile.tsx index e9c5ebdecf..73530602ef 100644 --- a/ui/epochs/electionRewards/EpochElectionRewardDetailsMobile.tsx +++ b/ui/epochs/electionRewards/EpochElectionRewardDetailsMobile.tsx @@ -5,9 +5,11 @@ import React from 'react'; import type { CeloEpochDetails } from 'types/api/epochs'; import type { TokenInfo } from 'types/api/token'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import useLazyLoadedList from 'ui/shared/pagination/useLazyLoadedList'; import TokenValue from 'ui/shared/value/TokenValue'; diff --git a/ui/games/CapybaraRunner.tsx b/ui/games/CapybaraRunner.tsx index 49c4fca05d..b4ecc6ef40 100644 --- a/ui/games/CapybaraRunner.tsx +++ b/ui/games/CapybaraRunner.tsx @@ -3,8 +3,9 @@ import { Box, Text, Flex } from '@chakra-ui/react'; import Script from 'next/script'; import React from 'react'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; diff --git a/ui/gasTracker/GasTrackerChart.tsx b/ui/gasTracker/GasTrackerChart.tsx index 05d132eba8..1ee4c95a50 100644 --- a/ui/gasTracker/GasTrackerChart.tsx +++ b/ui/gasTracker/GasTrackerChart.tsx @@ -3,12 +3,14 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import useApiQuery from 'lib/api/useApiQuery'; -import { STATS_CHARTS_SECTION_GAS } from 'stubs/stats'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import ChartWidgetContainer from 'client/features/chain-stats/components/ChartWidgetContainer'; +import { CHAIN_STATS_CHARTS_SECTION_GAS } from 'client/features/chain-stats/stubs/charts'; + import { Link } from 'toolkit/chakra/link'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; -import ChartWidgetContainer from 'ui/stats/ChartWidgetContainer'; const GAS_PRICE_CHART_ID = 'averageGasPrice'; @@ -17,7 +19,7 @@ const GasTrackerChart = () => { const { data, isPlaceholderData, isError } = useApiQuery('stats:lines', { queryOptions: { placeholderData: { - sections: [ STATS_CHARTS_SECTION_GAS ], + sections: [ CHAIN_STATS_CHARTS_SECTION_GAS ], }, }, }); @@ -47,7 +49,7 @@ const GasTrackerChart = () => { title={ chart.title } description={ chart.description } interval="oneMonth" - isPlaceholderData={ isPlaceholderData } + isLoading={ isPlaceholderData } onLoadingError={ handleLoadingError } h="320px" /> diff --git a/ui/gasTracker/GasTrackerFaq.tsx b/ui/gasTracker/GasTrackerFaq.tsx index cd7843db2b..1bf375441d 100644 --- a/ui/gasTracker/GasTrackerFaq.tsx +++ b/ui/gasTracker/GasTrackerFaq.tsx @@ -1,8 +1,9 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; -import { currencyUnits } from 'lib/units'; import { AccordionRoot } from 'toolkit/chakra/accordion'; import { Heading } from 'toolkit/chakra/heading'; diff --git a/ui/gasTracker/GasTrackerNetworkUtilization.tsx b/ui/gasTracker/GasTrackerNetworkUtilization.tsx index a077019c71..3728db8e37 100644 --- a/ui/gasTracker/GasTrackerNetworkUtilization.tsx +++ b/ui/gasTracker/GasTrackerNetworkUtilization.tsx @@ -1,7 +1,8 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; -import getNetworkUtilizationParams from 'lib/networks/getNetworkUtilizationParams'; +import getChainUtilizationParams from 'client/shared/chain/get-chain-utilization-params'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { mdash } from 'toolkit/utils/htmlEntities'; @@ -11,7 +12,7 @@ interface Props { } const GasTrackerNetworkUtilization = ({ percentage, isLoading }: Props) => { - const { load, color } = getNetworkUtilizationParams(percentage); + const { load, color } = getChainUtilizationParams(percentage); return ( diff --git a/ui/home/HeroBanner.pw.tsx b/ui/home/HeroBanner.pw.tsx index bb59828ef5..c087f00365 100644 --- a/ui/home/HeroBanner.pw.tsx +++ b/ui/home/HeroBanner.pw.tsx @@ -24,7 +24,7 @@ authTest('customization +@dark-mode', async({ render, page, mockEnvs, mockApiRes await mockEnvs([ ...ENVS_MAP.rewardsService, // eslint-disable-next-line max-len - [ 'NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG', `{"background":["lightpink","no-repeat center/cover url(${ IMAGE_URL })"],"text_color":["deepskyblue","white"],"border":["3px solid green","3px dashed yellow"],"button":{"_default":{"background":["deeppink"],"text_color":["white"]},"_selected":{"background":["lime"]}}}` ], + [ 'NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG', `{"text": "Duck migration observer","background":["lightpink","no-repeat center/cover url(${ IMAGE_URL })"],"text_color":["deepskyblue","white"],"border":["3px solid green","3px dashed yellow"],"button":{"_default":{"background":["deeppink"],"text_color":["white"]},"_selected":{"background":["lime"]}}}` ], ]); await page.route(IMAGE_URL, (route) => { diff --git a/ui/home/HeroBanner.tsx b/ui/home/HeroBanner.tsx index 58f761eb33..57a73d09e3 100644 --- a/ui/home/HeroBanner.tsx +++ b/ui/home/HeroBanner.tsx @@ -3,8 +3,9 @@ import { Box, Flex, Heading } from '@chakra-ui/react'; import React from 'react'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; import RewardsButton from 'ui/rewards/RewardsButton'; import AdBanner from 'ui/shared/ad/AdBanner'; import SearchBar from 'ui/snippets/searchBar/SearchBarDesktop'; @@ -49,6 +50,16 @@ const HeroBanner = () => { config.UI.homepage.heroBanner?.border?.[1] || config.UI.homepage.heroBanner?.border?.[0] || BORDER_DEFAULT, }; + const text = (() => { + if (config.UI.homepage.heroBanner?.text) { + return config.UI.homepage.heroBanner.text; + } + + return config.meta.seo.enhancedDataEnabled ? + `${ config.chain.name } blockchain explorer` : + `${ config.chain.name } explorer`; + })(); + return ( { fontWeight={{ base: 500, lg: 700 }} color={ textColor } > - { - config.meta.seo.enhancedDataEnabled ? - `${ config.chain.name } blockchain explorer` : - `${ config.chain.name } explorer` - } + { text } { config.UI.navigation.layout === 'vertical' && ( diff --git a/ui/home/Highlights.tsx b/ui/home/Highlights.tsx index fcf02d8df2..8136179707 100644 --- a/ui/home/Highlights.tsx +++ b/ui/home/Highlights.tsx @@ -6,8 +6,9 @@ import React from 'react'; import type { HighlightsBannerConfig } from 'types/homepage'; +import useFetch from 'client/api/hooks/useFetch'; + import config from 'configs/app'; -import useFetch from 'lib/hooks/useFetch'; import { HOMEPAGE_HIGHLIGHTS_BANNER } from 'stubs/homepage'; import HighlightsItem from './highlights/HighlightsItem'; diff --git a/ui/home/LatestBatchStatsWidget.tsx b/ui/home/LatestBatchStatsWidget.tsx new file mode 100644 index 0000000000..f70ade3985 --- /dev/null +++ b/ui/home/LatestBatchStatsWidget.tsx @@ -0,0 +1,32 @@ +import { chakra } from '@chakra-ui/react'; +import React from 'react'; + +import StatsWidget from 'ui/shared/stats/StatsWidget'; + +import { useHomeDataContext } from './homeDataContext'; + +type Props = { + className?: string; + isLoading: boolean; +}; + +const LatestBatchStatsWidget = ({ className, isLoading }: Props) => { + const { latestBatchQuery } = useHomeDataContext(); + + if (latestBatchQuery?.data === undefined) { + return null; + } + + return ( + + ); +}; + +export default chakra(React.memo(LatestBatchStatsWidget)); diff --git a/ui/home/LatestBlockStatsWidget.tsx b/ui/home/LatestBlockStatsWidget.tsx new file mode 100644 index 0000000000..40907a76f4 --- /dev/null +++ b/ui/home/LatestBlockStatsWidget.tsx @@ -0,0 +1,34 @@ +import { chakra } from '@chakra-ui/react'; +import React from 'react'; + +import StatsWidget from 'ui/shared/stats/StatsWidget'; + +import { useHomeDataContext } from './homeDataContext'; + +type Props = { + className?: string; + isLoading: boolean; + fallbackValue: number | string | undefined; +}; + +const LatestBlockStatsWidget = ({ className, isLoading, fallbackValue }: Props) => { + const { blocksQuery } = useHomeDataContext(); + + const value = blocksQuery?.data?.[0]?.height ?? fallbackValue; + if (value === undefined) { + return null; + } + + return ( + + ); +}; + +export default chakra(React.memo(LatestBlockStatsWidget)); diff --git a/ui/home/LatestBlocks.pw.tsx b/ui/home/LatestBlocks.pw.tsx index a571e85a85..5c94338725 100644 --- a/ui/home/LatestBlocks.pw.tsx +++ b/ui/home/LatestBlocks.pw.tsx @@ -1,21 +1,25 @@ import React from 'react'; -import * as blockMock from 'mocks/blocks/block'; +import * as blockMock from 'client/slices/block/mocks/block'; + import * as statsMock from 'mocks/stats/index'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import * as socketServer from 'playwright/fixtures/socketServer'; import { test, expect } from 'playwright/lib'; import { HomeRpcDataContextProvider } from './fallbacks/rpcDataContext'; +import { HomeDataContextProvider } from './homeDataContext'; import LatestBlocks from './LatestBlocks'; test('default view +@mobile +@dark-mode', async({ render, mockApiResponse }) => { await mockApiResponse('general:stats', statsMock.base); await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); const component = await render( - - - , + + + + + , ); await expect(component).toHaveScreenshot(); }); @@ -25,9 +29,11 @@ test('L2 view', async({ render, mockEnvs, mockApiResponse }) => { await mockApiResponse('general:stats', statsMock.base); await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); const component = await render( - - - , + + + + + , ); await expect(component).toHaveScreenshot(); }); @@ -37,9 +43,11 @@ test('no reward view', async({ render, mockEnvs, mockApiResponse }) => { await mockApiResponse('general:stats', statsMock.base); await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); const component = await render( - - - , + + + + + , ); await expect(component).toHaveScreenshot(); }); @@ -48,9 +56,11 @@ test('with long block height', async({ render, mockApiResponse }) => { await mockApiResponse('general:stats', statsMock.base); await mockApiResponse('general:homepage_blocks', [ { ...blockMock.base, height: 123456789012345 } ]); const component = await render( - - - , + + + + + , ); await expect(component).toHaveScreenshot(); }); @@ -61,9 +71,11 @@ test.describe('socket', () => { await mockApiResponse('general:stats', statsMock.base); await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); const component = await render( - - - , + + + + + , undefined, { withSocket: true }, ); diff --git a/ui/home/LatestBlocks.tsx b/ui/home/LatestBlocks.tsx index 2e8538e45b..16211c23af 100644 --- a/ui/home/LatestBlocks.tsx +++ b/ui/home/LatestBlocks.tsx @@ -1,21 +1,18 @@ import { chakra, Box, Flex, Text, VStack, HStack } from '@chakra-ui/react'; -import { useQueryClient } from '@tanstack/react-query'; import { upperFirst } from 'es-toolkit'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; import { route } from 'nextjs-routes'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import getChainUtilizationParams from 'client/shared/chain/get-chain-utilization-params'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useInitialList from 'client/shared/lists/useInitialList'; + import config from 'configs/app'; -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -import useInitialList from 'lib/hooks/useInitialList'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getNetworkUtilizationParams from 'lib/networks/getNetworkUtilizationParams'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import { BLOCK } from 'stubs/block'; import { HOMEPAGE_STATS } from 'stubs/stats'; import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; @@ -26,6 +23,7 @@ import FallbackRpcIcon from 'ui/shared/fallbacks/FallbackRpcIcon'; import LatestBlocksDegraded from './fallbacks/LatestBlocksDegraded'; import { useHomeRpcDataContext } from './fallbacks/rpcDataContext'; +import { useHomeDataContext } from './homeDataContext'; import LatestBlocksItem from './LatestBlocksItem'; const LatestBlocks = () => { @@ -37,18 +35,13 @@ const LatestBlocks = () => { } else { blocksMaxCount = isMobile ? 2 : 3; } - const { data, isPlaceholderData, isError } = useApiQuery('general:homepage_blocks', { - queryOptions: { - placeholderData: Array(blocksMaxCount).fill(BLOCK), - }, - }); - const initialList = useInitialList({ - data: data ?? [], + const { blocksQuery } = useHomeDataContext(); + const initialList = useInitialList({ + data: blocksQuery?.data ?? [], idFn: (block) => block.height, - enabled: !isPlaceholderData, + enabled: Boolean(blocksQuery && !blocksQuery.isPlaceholderData), }); - const queryClient = useQueryClient(); const statsQueryResult = useApiQuery('general:stats', { queryOptions: { refetchOnMount: false, @@ -59,50 +52,27 @@ const LatestBlocks = () => { const rpcDataContext = useHomeRpcDataContext(); const isRpcData = rpcDataContext.isEnabled && !rpcDataContext.isLoading && !rpcDataContext.isError && rpcDataContext.subscriptions.includes('latest-blocks'); - const handleNewBlockMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => { - queryClient.setQueryData(getResourceKey('general:homepage_blocks'), (prevData: Array | undefined) => { - - const newData = prevData ? [ ...prevData ] : []; - - if (newData.some((block => block.height === payload.block.height))) { - return newData; - } - - return [ payload.block, ...newData ].sort((b1, b2) => b2.height - b1.height).slice(0, blocksMaxCount); - }); - }, [ queryClient, blocksMaxCount ]); - - const channel = useSocketChannel({ - topic: 'blocks:new_block', - isDisabled: isPlaceholderData || isError, - }); - useSocketMessage({ - channel, - event: 'new_block', - handler: handleNewBlockMessage, - }); - const content = (() => { - if (isError) { + if (blocksQuery?.isError) { return ; } - if (data && data.length > 0) { - const dataToShow = data.slice(0, blocksMaxCount); + if (blocksQuery?.data && blocksQuery.data.length > 0) { + const dataToShow = blocksQuery.data.slice(0, blocksMaxCount); return ( <> { dataToShow.map(((block, index) => ( ))) } - View all blocks + View all blocks ); @@ -110,7 +80,7 @@ const LatestBlocks = () => { return No latest blocks found.; })(); - const networkUtilization = getNetworkUtilizationParams(statsQueryResult.data?.network_utilization_percentage ?? 0); + const networkUtilization = getChainUtilizationParams(statsQueryResult.data?.network_utilization_percentage ?? 0); return ( diff --git a/ui/home/LatestBlocksItem.tsx b/ui/home/LatestBlocksItem.tsx index b47c766261..43ad3e2b7f 100644 --- a/ui/home/LatestBlocksItem.tsx +++ b/ui/home/LatestBlocksItem.tsx @@ -2,17 +2,19 @@ import { Box, Flex, Grid } from '@chakra-ui/react'; import { capitalize } from 'es-toolkit'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import getBlockTotalReward from 'client/slices/block/utils/get-block-total-reward'; + +import getChainValidatorTitle from 'client/shared/chain/get-chain-validator-title'; +import { currencyUnits } from 'client/shared/chain/units'; import config from 'configs/app'; -import getBlockTotalReward from 'lib/block/getBlockTotalReward'; -import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; -import { currencyUnits } from 'lib/units'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { thinsp } from 'toolkit/utils/htmlEntities'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import IconSvg from 'ui/shared/IconSvg'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import SimpleValue from 'ui/shared/value/SimpleValue'; @@ -77,7 +79,7 @@ const LatestBlocksItem = ({ block, isLoading, animation }: Props) => { { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.miner && ( <> - { capitalize(getNetworkValidatorTitle()) } + { capitalize(getChainValidatorTitle()) } { @@ -15,7 +18,12 @@ test.describe('all items', () => { [ 'NEXT_PUBLIC_STATS_API_HOST', '' ], ]); await mockApiResponse('general:stats', statsMock.withBtcLocked); - component = await render(); + await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); + component = await render( + + + , + ); }); test('+@mobile +@dark-mode', async() => { @@ -28,7 +36,12 @@ test('no gas info', async({ render, mockApiResponse, mockEnvs }) => { [ 'NEXT_PUBLIC_STATS_API_HOST', '' ], ]); await mockApiResponse('general:stats', statsMock.withoutGasInfo); - const component = await render(); + await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); + const component = await render( + + + , + ); await expect(component).toHaveScreenshot(); }); @@ -39,7 +52,12 @@ test('4 items default view +@mobile -@default', async({ render, mockApiResponse, [ 'NEXT_PUBLIC_STATS_API_HOST', '' ], ]); await mockApiResponse('general:stats', statsMock.base); - const component = await render(); + await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); + const component = await render( + + + , + ); await expect(component).toHaveScreenshot(); }); @@ -49,6 +67,11 @@ test('3 items default view +@mobile -@default', async({ render, mockApiResponse, [ 'NEXT_PUBLIC_STATS_API_HOST', '' ], ]); await mockApiResponse('general:stats', statsMock.base); - const component = await render(); + await mockApiResponse('general:homepage_blocks', [ blockMock.base, blockMock.base2 ]); + const component = await render( + + + , + ); await expect(component).toHaveScreenshot(); }); diff --git a/ui/home/Stats.tsx b/ui/home/Stats.tsx index b448824eff..0ef725ed83 100644 --- a/ui/home/Stats.tsx +++ b/ui/home/Stats.tsx @@ -2,8 +2,9 @@ import { Grid } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { layerLabels } from 'lib/rollups/utils'; import { HOMEPAGE_STATS, HOMEPAGE_STATS_MICROSERVICE } from 'stubs/stats'; import GasInfoTooltip from 'ui/shared/gas/GasInfoTooltip'; @@ -13,8 +14,11 @@ import StatsWidget from 'ui/shared/stats/StatsWidget'; import { WEI } from 'ui/shared/value/utils'; import StatsDegraded from './fallbacks/StatsDegraded'; +import { useHomeDataContext } from './homeDataContext'; +import LatestBatchStatsWidget from './LatestBatchStatsWidget'; +import LatestBlockStatsWidget from './LatestBlockStatsWidget'; import type { HomeStatsItem } from './utils'; -import { isHomeStatsItemEnabled, sortHomeStatsItems } from './utils'; +import { homeStatsWidgetCommonStyles, isHomeStatsItemEnabled, sortHomeStatsItems } from './utils'; const rollupFeature = config.features.rollup; const isOptimisticRollup = rollupFeature.isEnabled && rollupFeature.type === 'optimistic'; @@ -23,6 +27,7 @@ const isStatsFeatureEnabled = config.features.stats.isEnabled; const Stats = () => { const [ hasGasTracker, setHasGasTracker ] = React.useState(config.features.gasTracker.isEnabled); + const { blocksQuery, latestBatchQuery } = useHomeDataContext(); // data from stats microservice is prioritized over data from stats api const statsQuery = useApiQuery('stats:pages_main', { @@ -40,7 +45,7 @@ const Stats = () => { }, }); - const isPlaceholderData = statsQuery.isPlaceholderData || apiQuery.isPlaceholderData; + const isPlaceholderData = statsQuery.isPlaceholderData || apiQuery.isPlaceholderData || blocksQuery?.isPlaceholderData; React.useEffect(() => { if (!isPlaceholderData && !apiQuery.data?.gas_prices?.average) { @@ -50,43 +55,9 @@ const Stats = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [ isPlaceholderData ]); - const zkEvmLatestBatchQuery = useApiQuery('general:homepage_zkevm_latest_batch', { - queryOptions: { - placeholderData: 12345, - enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkEvm' && config.UI.homepage.stats.includes('latest_batch'), - }, - }); - - const zkSyncLatestBatchQuery = useApiQuery('general:homepage_zksync_latest_batch', { - queryOptions: { - placeholderData: 12345, - enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && config.UI.homepage.stats.includes('latest_batch'), - }, - }); - - const arbitrumLatestBatchQuery = useApiQuery('general:homepage_arbitrum_latest_batch', { - queryOptions: { - placeholderData: 12345, - enabled: rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && config.UI.homepage.stats.includes('latest_batch'), - }, - }); + const hasStatsError = apiQuery.isError || statsQuery.isError || blocksQuery?.isError || latestBatchQuery?.isError; - const latestBatchQuery = (() => { - if (!rollupFeature.isEnabled || !config.UI.homepage.stats.includes('latest_batch')) { - return; - } - - switch (rollupFeature.type) { - case 'zkEvm': - return zkEvmLatestBatchQuery; - case 'zkSync': - return zkSyncLatestBatchQuery; - case 'arbitrum': - return arbitrumLatestBatchQuery; - } - })(); - - if (apiQuery.isError || statsQuery.isError || latestBatchQuery?.isError) { + if (hasStatsError) { return ; } @@ -117,19 +88,17 @@ const Stats = () => { return [ latestBatchQuery?.data !== undefined && { id: 'latest_batch' as const, - icon: 'txn_batches' as const, - label: 'Latest batch', - value: latestBatchQuery.data.toLocaleString(), - href: { pathname: '/batches' as const }, - isLoading, + component: , }, - (statsData?.total_blocks?.value || apiData?.total_blocks) && { + (blocksQuery?.data?.[0]?.height ?? statsData?.total_blocks?.value ?? apiData?.total_blocks) && { id: 'total_blocks' as const, - icon: 'block' as const, - label: statsData?.total_blocks?.title || 'Total blocks', - value: Number(statsData?.total_blocks?.value || apiData?.total_blocks).toLocaleString(), - href: { pathname: '/blocks' as const }, - isLoading, + component: ( + + ), }, (statsData?.average_block_time?.value || apiData?.average_block_time) && { id: 'average_block_time' as const, @@ -221,16 +190,21 @@ const Stats = () => { flexBasis="50%" flexGrow={ 1 } > - { items.map((item, index) => ( - - ), - ) } + { items.map((item) => { + if ('component' in item) { + return { item.component }; + } + + return ( + + ); + }) } - ); }; diff --git a/ui/home/Transactions.tsx b/ui/home/Transactions.tsx index 315abca318..0a01cdfe6f 100644 --- a/ui/home/Transactions.tsx +++ b/ui/home/Transactions.tsx @@ -1,9 +1,10 @@ import { HStack } from '@chakra-ui/react'; import React from 'react'; +import { SocketProvider } from 'client/api/socket/context'; + import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; -import { SocketProvider } from 'lib/socket/context'; import { Heading } from 'toolkit/chakra/heading'; import AdaptiveTabs from 'toolkit/components/AdaptiveTabs/AdaptiveTabs'; import LatestOptimisticDeposits from 'ui/home/latestDeposits/LatestOptimisticDeposits'; diff --git a/ui/home/__screenshots__/HeroBanner.pw.tsx_dark-color-mode_customization-dark-mode-1.png b/ui/home/__screenshots__/HeroBanner.pw.tsx_dark-color-mode_customization-dark-mode-1.png index 5c1ac5e60f..15474c9d5a 100644 Binary files a/ui/home/__screenshots__/HeroBanner.pw.tsx_dark-color-mode_customization-dark-mode-1.png and b/ui/home/__screenshots__/HeroBanner.pw.tsx_dark-color-mode_customization-dark-mode-1.png differ diff --git a/ui/home/__screenshots__/HeroBanner.pw.tsx_default_customization-dark-mode-1.png b/ui/home/__screenshots__/HeroBanner.pw.tsx_default_customization-dark-mode-1.png index d551fed38f..588d704a7e 100644 Binary files a/ui/home/__screenshots__/HeroBanner.pw.tsx_default_customization-dark-mode-1.png and b/ui/home/__screenshots__/HeroBanner.pw.tsx_default_customization-dark-mode-1.png differ diff --git a/ui/home/__screenshots__/Stats.pw.tsx_dark-color-mode_all-items-mobile-dark-mode-1.png b/ui/home/__screenshots__/Stats.pw.tsx_dark-color-mode_all-items-mobile-dark-mode-1.png index 81a6d98710..7fdd6bc0c7 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_dark-color-mode_all-items-mobile-dark-mode-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_dark-color-mode_all-items-mobile-dark-mode-1.png differ diff --git a/ui/home/__screenshots__/Stats.pw.tsx_default_all-items-mobile-dark-mode-1.png b/ui/home/__screenshots__/Stats.pw.tsx_default_all-items-mobile-dark-mode-1.png index 1b4522d7b8..17a5eaa0e8 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_default_all-items-mobile-dark-mode-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_default_all-items-mobile-dark-mode-1.png differ diff --git a/ui/home/__screenshots__/Stats.pw.tsx_default_no-gas-info-1.png b/ui/home/__screenshots__/Stats.pw.tsx_default_no-gas-info-1.png index d71bc08c9b..7e34ce5977 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_default_no-gas-info-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_default_no-gas-info-1.png differ diff --git a/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png b/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png index f2ad4897a5..797e95154d 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_mobile_3-items-default-view-mobile---default-1.png differ diff --git a/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png b/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png index ca92bddc54..65be4e5653 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_mobile_4-items-default-view-mobile---default-1.png differ diff --git a/ui/home/__screenshots__/Stats.pw.tsx_mobile_all-items-mobile-dark-mode-1.png b/ui/home/__screenshots__/Stats.pw.tsx_mobile_all-items-mobile-dark-mode-1.png index 46b73c4cda..a6f0bf72aa 100644 Binary files a/ui/home/__screenshots__/Stats.pw.tsx_mobile_all-items-mobile-dark-mode-1.png and b/ui/home/__screenshots__/Stats.pw.tsx_mobile_all-items-mobile-dark-mode-1.png differ diff --git a/ui/home/fallbacks/LatestBlocksDegraded.tsx b/ui/home/fallbacks/LatestBlocksDegraded.tsx index 05362a2032..886c3f7e7f 100644 --- a/ui/home/fallbacks/LatestBlocksDegraded.tsx +++ b/ui/home/fallbacks/LatestBlocksDegraded.tsx @@ -1,13 +1,16 @@ import { Box, Flex, VStack } from '@chakra-ui/react'; import React from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; import { route } from 'nextjs-routes'; -import useInitialList from 'lib/hooks/useInitialList'; -import { publicClient } from 'lib/web3/client'; -import { BLOCK } from 'stubs/block'; +import { BLOCK } from 'client/slices/block/stubs/block'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + +import useInitialList from 'client/shared/lists/useInitialList'; + import { Link } from 'toolkit/chakra/link'; import LatestBlocksItem from '../LatestBlocksItem'; diff --git a/ui/home/fallbacks/LatestTxsDegraded.tsx b/ui/home/fallbacks/LatestTxsDegraded.tsx index 4995479a0f..3dfcf318a3 100644 --- a/ui/home/fallbacks/LatestTxsDegraded.tsx +++ b/ui/home/fallbacks/LatestTxsDegraded.tsx @@ -4,10 +4,12 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; +import { TX } from 'client/slices/tx/stubs/tx'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + import config from 'configs/app'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import { publicClient } from 'lib/web3/client'; -import { TX } from 'stubs/tx'; import { Link } from 'toolkit/chakra/link'; import LatestTxsItem from '../LatestTxsItem'; diff --git a/ui/home/fallbacks/LatestTxsDegradedNewItems.tsx b/ui/home/fallbacks/LatestTxsDegradedNewItems.tsx index 395e07071d..58d2efabf2 100644 --- a/ui/home/fallbacks/LatestTxsDegradedNewItems.tsx +++ b/ui/home/fallbacks/LatestTxsDegradedNewItems.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import useGradualIncrement from 'lib/hooks/useGradualIncrement'; +import useGradualIncrement from 'client/shared/hooks/useGradualIncrement'; + import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; interface Props { diff --git a/ui/home/fallbacks/StatsDegraded.tsx b/ui/home/fallbacks/StatsDegraded.tsx index 67472bfe18..301751e7fd 100644 --- a/ui/home/fallbacks/StatsDegraded.tsx +++ b/ui/home/fallbacks/StatsDegraded.tsx @@ -3,16 +3,17 @@ import { useQuery } from '@tanstack/react-query'; import BigNumber from 'bignumber.js'; import React from 'react'; +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; + import dayjs from 'lib/date/dayjs'; -import { publicClient } from 'lib/web3/client'; import { mdash } from 'toolkit/utils/htmlEntities'; import FallbackRpcIcon from 'ui/shared/fallbacks/FallbackRpcIcon'; import GasPrice from 'ui/shared/gas/GasPrice'; import StatsWidget from 'ui/shared/stats/StatsWidget'; import { GWEI } from 'ui/shared/value/utils'; -import type { HomeStatsItem } from '../utils'; -import { isHomeStatsItemEnabled, sortHomeStatsItems } from '../utils'; +import type { HomeStatsWidgetItem } from '../utils'; +import { homeStatsWidgetCommonStyles, isHomeStatsItemEnabled, sortHomeStatsItems } from '../utils'; import { useHomeRpcDataContext } from './rpcDataContext'; const StatsDegraded = () => { @@ -72,7 +73,7 @@ const StatsDegraded = () => { } }, [ blocks ]); - const items: Array = (() => { + const items: Array = (() => { return [ { id: 'latest_batch' as const, @@ -84,7 +85,7 @@ const StatsDegraded = () => { { id: 'total_blocks' as const, icon: 'block' as const, - label: 'Total blocks', + label: 'Latest block', value: blocks[0] ? blocks[0].height.toLocaleString() : mdash, isFallback: blocks[0] === undefined, hint: blocks[0] && !isLoading ? : undefined, @@ -165,12 +166,12 @@ const StatsDegraded = () => { flexBasis="50%" flexGrow={ 1 } > - { items.map((item, index) => ( + { items.map((item) => ( + { ...homeStatsWidgetCommonStyles }/> ), ) } diff --git a/ui/home/fallbacks/rpcDataContext.tsx b/ui/home/fallbacks/rpcDataContext.tsx index eb2b2951f5..4e2885ce66 100644 --- a/ui/home/fallbacks/rpcDataContext.tsx +++ b/ui/home/fallbacks/rpcDataContext.tsx @@ -1,12 +1,14 @@ import { useQueries, useQuery } from '@tanstack/react-query'; import React from 'react'; -import type { Block } from 'types/api/block'; -import type { Transaction } from 'types/api/transaction'; +import type { Block } from 'client/slices/block/types/api'; +import type { Transaction } from 'client/slices/tx/types/api'; + +import formatBlockRpcData from 'client/slices/block/utils/format-rpc-data'; +import formatTxRpcData from 'client/slices/tx/utils/format-rpc-data'; + +import { publicClient } from 'client/features/connect-wallet/utils/public-client'; -import { publicClient } from 'lib/web3/client'; -import formatBlockData from 'lib/web3/rpc/formatBlockData'; -import formatTxData from 'lib/web3/rpc/formatTxData'; import { SECOND } from 'toolkit/utils/consts'; export type SubscriptionId = 'latest-blocks' | 'latest-txs' | 'stats-widgets'; @@ -46,7 +48,7 @@ export function HomeRpcDataContextProvider({ children }: { children: React.React onBlock: (block) => { setTxs((prevTxs) => { try { - const newTxs = block.transactions.map((tx) => formatTxData(tx, null, null, block)).filter(Boolean); + const newTxs = block.transactions.map((tx) => formatTxRpcData(tx, null, null, block)).filter(Boolean); const nextTxs = prevTxs.length < ITEMS_LIMIT ? [ ...prevTxs, ...newTxs ].slice(0, ITEMS_LIMIT) : prevTxs; const totalTxs = prevTxs.length + newTxs.length; @@ -61,7 +63,7 @@ export function HomeRpcDataContextProvider({ children }: { children: React.React setBlocks((prev) => { try { return [ - formatBlockData({ + formatBlockRpcData({ ...block, transactions: block.transactions.map((tx) => tx.hash), }), diff --git a/ui/home/homeDataContext.tsx b/ui/home/homeDataContext.tsx new file mode 100644 index 0000000000..bef63aeead --- /dev/null +++ b/ui/home/homeDataContext.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +import useHomeBlocksData, { type HomeBlocksQueryResult } from './useHomeBlocksData'; +import useHomeLatestBatchData, { type HomeLatestBatchQueryResult } from './useHomeLatestBatchData'; + +type HomeDataContextValue = { + blocksQuery: HomeBlocksQueryResult | undefined; + latestBatchQuery: HomeLatestBatchQueryResult | undefined; +}; + +const HomeDataContext = React.createContext(null); + +export function HomeDataContextProvider({ children }: { children: React.ReactNode }) { + const blocksQuery = useHomeBlocksData(); + const latestBatchQuery = useHomeLatestBatchData(); + + const value = React.useMemo(() => ({ + blocksQuery, + latestBatchQuery, + }), [ blocksQuery, latestBatchQuery ]); + + return ( + + { children } + + ); +} + +export function useHomeDataContext(): HomeDataContextValue { + const ctx = React.useContext(HomeDataContext); + if (!ctx) { + throw new Error('useHomeDataContext must be used within HomeDataContextProvider'); + } + return ctx; +} diff --git a/ui/home/indicators/ChainIndicatorChartContainer.tsx b/ui/home/indicators/ChainIndicatorChartContainer.tsx index e240f5c44e..7913c6f3c4 100644 --- a/ui/home/indicators/ChainIndicatorChartContainer.tsx +++ b/ui/home/indicators/ChainIndicatorChartContainer.tsx @@ -1,7 +1,7 @@ import { chakra, Box } from '@chakra-ui/react'; import React from 'react'; -import type { TimeChartData } from 'toolkit/components/charts/types'; +import type { LineChartData } from 'toolkit/components/charts/line/types'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; @@ -9,7 +9,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert'; import ChainIndicatorChartContent from './ChainIndicatorChartContent'; type Props = { - data: TimeChartData; + data: LineChartData; isError: boolean; isPending: boolean; }; diff --git a/ui/home/indicators/ChainIndicatorChartContent.tsx b/ui/home/indicators/ChainIndicatorChartContent.tsx index 80aa331ddc..0f73f2587a 100644 --- a/ui/home/indicators/ChainIndicatorChartContent.tsx +++ b/ui/home/indicators/ChainIndicatorChartContent.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import type { TimeChartData } from 'toolkit/components/charts/types'; +import type { LineChartData } from 'toolkit/components/charts/line/types'; -import { ChartArea, ChartLine, ChartOverlay, ChartTooltip, useTimeChartController } from 'toolkit/components/charts'; +import { LineChartArea, LineChartLine, LineChartOverlay, LineChartTooltip, useLineChartController } from 'toolkit/components/charts/line'; import { useDefaultGradient, useDefaultLineColor } from 'ui/shared/chart/config'; interface Props { - data: TimeChartData; + data: LineChartData; caption?: string; } @@ -24,7 +24,7 @@ const ChainIndicatorChartContent = ({ data }: Props) => { }; }, [ ]); - const { rect, ref, axes, innerWidth, innerHeight, chartMargin } = useTimeChartController({ + const { rect, ref, axes, innerWidth, innerHeight, chartMargin } = useLineChartController({ data, margin: CHART_MARGIN, axesConfig, @@ -33,14 +33,14 @@ const ChainIndicatorChartContent = ({ data }: Props) => { return ( - - { strokeWidth={ 3 } animation="left" /> - - + { yScale={ axes.y.scale } data={ data } /> - + ); diff --git a/ui/home/indicators/ChainIndicators.tsx b/ui/home/indicators/ChainIndicators.tsx index a925986368..78714144ce 100644 --- a/ui/home/indicators/ChainIndicators.tsx +++ b/ui/home/indicators/ChainIndicators.tsx @@ -2,8 +2,9 @@ import React from 'react'; import type { TChainIndicator } from './types'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { HOMEPAGE_STATS, HOMEPAGE_STATS_MICROSERVICE } from 'stubs/stats'; import IconSvg from 'ui/shared/IconSvg'; import NativeTokenIcon from 'ui/shared/NativeTokenIcon'; diff --git a/ui/home/indicators/useChartDataQuery.tsx b/ui/home/indicators/useChartDataQuery.tsx index e31faec892..f56d100da1 100644 --- a/ui/home/indicators/useChartDataQuery.tsx +++ b/ui/home/indicators/useChartDataQuery.tsx @@ -1,8 +1,9 @@ -import type { TimeChartData } from 'toolkit/components/charts/types'; +import type { LineChartData } from 'toolkit/components/charts/line/types'; import type { ChainIndicatorId } from 'types/homepage'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { getChartData } from './utils/chart'; @@ -15,7 +16,7 @@ const isStatsFeatureEnabled = config.features.stats.isEnabled; export type UseFetchChartDataResult = { isError: boolean; isPending: boolean; - data: TimeChartData; + data: LineChartData; }; export default function useChartDataQuery(indicatorId: ChainIndicatorId): UseFetchChartDataResult { diff --git a/ui/home/indicators/utils/chart.ts b/ui/home/indicators/utils/chart.ts index 53baf46edb..1a63116b9d 100644 --- a/ui/home/indicators/utils/chart.ts +++ b/ui/home/indicators/utils/chart.ts @@ -1,10 +1,10 @@ -import type { TimeChartData, TimeChartDataItem, TimeChartItemRaw, TimeChartItem } from 'toolkit/components/charts/types'; import type { ChainIndicatorId } from 'types/homepage'; import config from 'configs/app'; -import { sortByDateDesc } from 'ui/shared/chart/utils'; +import type { LineChartData, LineChartDataItem, LineChartItemRaw, LineChartItem } from 'toolkit/components/charts/line'; +import { sortByDateAsc } from 'ui/shared/chart/utils'; -const CHART_ITEMS: Record> = { +const CHART_ITEMS: Record> = { daily_txs: { name: 'Tx/day', valueFormatter: (x: number) => x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }), @@ -31,7 +31,7 @@ const CHART_ITEMS: Record, item: TimeChartItemRaw) => { +const nonNullTailReducer = (result: Array, item: LineChartItemRaw) => { if (item.value === null && result.length === 0) { return result; } @@ -39,16 +39,16 @@ const nonNullTailReducer = (result: Array, item: TimeChartItem return result; }; -const mapNullToZero: (item: TimeChartItemRaw) => TimeChartItem = (item) => ({ ...item, value: Number(item.value) }); +const mapNullToZero: (item: LineChartItemRaw) => LineChartItem = (item) => ({ ...item, value: Number(item.value) }); -export function prepareChartItems(items: Array) { +export function prepareChartItems(items: Array) { return items - .sort(sortByDateDesc) - .reduceRight(nonNullTailReducer, [] as Array) + .sort(sortByDateAsc) + .reduceRight(nonNullTailReducer, [] as Array) .map(mapNullToZero); } -export function getChartData(indicatorId: ChainIndicatorId, data: Array): TimeChartData { +export function getChartData(indicatorId: ChainIndicatorId, data: Array): LineChartData { return [ { id: indicatorId.replace(' ', '_'), charts: [], diff --git a/ui/home/latestBatches/LatestArbitrumL2Batches.tsx b/ui/home/latestBatches/LatestArbitrumL2Batches.tsx index 9d2430c780..ad7c3da194 100644 --- a/ui/home/latestBatches/LatestArbitrumL2Batches.tsx +++ b/ui/home/latestBatches/LatestArbitrumL2Batches.tsx @@ -3,16 +3,18 @@ import { useQueryClient } from '@tanstack/react-query'; // import { AnimatePresence } from 'framer-motion'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; +import type { SocketMessage } from 'client/api/socket/types'; import type { ArbitrumL2TxnBatchesItem } from 'types/api/arbitrumL2'; import { route } from 'nextjs-routes'; -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -import useInitialList from 'lib/hooks/useInitialList'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useInitialList from 'client/shared/lists/useInitialList'; + import { ARBITRUM_L2_TXN_BATCHES_ITEM } from 'stubs/arbitrumL2'; import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; diff --git a/ui/home/latestBatches/LatestBatchItem.tsx b/ui/home/latestBatches/LatestBatchItem.tsx index 3b7dae7242..4be6ae6246 100644 --- a/ui/home/latestBatches/LatestBatchItem.tsx +++ b/ui/home/latestBatches/LatestBatchItem.tsx @@ -3,9 +3,10 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import BatchEntityL2 from 'client/features/rollup/common/components/BatchEntityL2'; + import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import BatchEntityL2 from 'ui/shared/entities/block/BatchEntityL2'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; type Props = { diff --git a/ui/home/latestBatches/LatestZkEvmL2Batches.pw.tsx b/ui/home/latestBatches/LatestZkEvmL2Batches.pw.tsx deleted file mode 100644 index 69a2f0bff2..0000000000 --- a/ui/home/latestBatches/LatestZkEvmL2Batches.pw.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import { txnBatchesData } from 'mocks/zkEvm/txnBatches'; -import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; -import { test, expect } from 'playwright/lib'; - -import LatestZkEvmL2Batches from './LatestZkEvmL2Batches'; - -test('default view +@mobile +@dark-mode', async({ render, mockEnvs, mockApiResponse }) => { - await mockEnvs(ENVS_MAP.zkEvmRollup); - await mockApiResponse('general:homepage_zkevm_l2_batches', txnBatchesData); - - const component = await render(); - await expect(component).toHaveScreenshot(); -}); diff --git a/ui/home/latestBatches/LatestZkEvmL2Batches.tsx b/ui/home/latestBatches/LatestZkEvmL2Batches.tsx deleted file mode 100644 index c936f2751c..0000000000 --- a/ui/home/latestBatches/LatestZkEvmL2Batches.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { Box, Flex, VStack } from '@chakra-ui/react'; -import { useQueryClient } from '@tanstack/react-query'; -import React from 'react'; - -import type { SocketMessage } from 'lib/socket/types'; -import type { ZkEvmL2TxnBatchesItem } from 'types/api/zkEvmL2'; - -import { route } from 'nextjs-routes'; - -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -import useInitialList from 'lib/hooks/useInitialList'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; -import { ZKEVM_L2_TXN_BATCHES_ITEM } from 'stubs/zkEvmL2'; -import { Heading } from 'toolkit/chakra/heading'; -import { Link } from 'toolkit/chakra/link'; -import ZkEvmL2TxnBatchStatus from 'ui/shared/statusTag/ZkEvmL2TxnBatchStatus'; - -import LatestBlocksFallback from '../fallbacks/LatestBlocksFallback'; -import LatestBatchItem from './LatestBatchItem'; - -const LatestZkEvmL2Batches = () => { - const isMobile = useIsMobile(); - const batchesMaxCount = isMobile ? 2 : 6; - const queryClient = useQueryClient(); - - const { data, isPlaceholderData, isError } = useApiQuery('general:homepage_zkevm_l2_batches', { - queryOptions: { - placeholderData: { items: Array(batchesMaxCount).fill(ZKEVM_L2_TXN_BATCHES_ITEM) }, - }, - }); - - const initialList = useInitialList({ - data: data?.items ?? [], - idFn: (batch) => batch.number, - enabled: !isPlaceholderData, - }); - - const handleNewBatchMessage: SocketMessage.NewZkEvmL2Batch['handler'] = React.useCallback((payload) => { - queryClient.setQueryData(getResourceKey('general:homepage_zkevm_l2_batches'), (prevData: { items: Array } | undefined) => { - const newItems = prevData?.items ? [ ...prevData.items ] : []; - - if (newItems.some((batch => batch.number === payload.batch.number))) { - return { items: newItems }; - } - - return { items: [ payload.batch, ...newItems ].sort((b1, b2) => b2.number - b1.number).slice(0, batchesMaxCount) }; - }); - }, [ queryClient, batchesMaxCount ]); - - const channel = useSocketChannel({ - topic: 'zkevm_batches:new_zkevm_confirmed_batch', - isDisabled: isPlaceholderData || isError, - }); - useSocketMessage({ - channel, - event: 'new_zkevm_confirmed_batch', - handler: handleNewBatchMessage, - }); - - const content = (() => { - if (isError) { - return ; - } - if (data && data.items.length > 0) { - const dataToShow = data.items.slice(0, batchesMaxCount); - - return ( - <> - - { dataToShow.map(((batch, index) => { - const status = ; - return ( - - ); - })) } - - - View all batches - - - ); - } - return No latest batches found.; - })(); - - return ( - - Latest batches - { content } - - ); -}; - -export default LatestZkEvmL2Batches; diff --git a/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png b/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png deleted file mode 100644 index 9878262f88..0000000000 Binary files a/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png b/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png deleted file mode 100644 index b3448e877a..0000000000 Binary files a/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png b/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png deleted file mode 100644 index 7f24e3a98d..0000000000 Binary files a/ui/home/latestBatches/__screenshots__/LatestZkEvmL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/ui/home/latestCrossChainTxs/LatestCrossChainTxs.tsx b/ui/home/latestCrossChainTxs/LatestCrossChainTxs.tsx index 29014781e8..91913a640b 100644 --- a/ui/home/latestCrossChainTxs/LatestCrossChainTxs.tsx +++ b/ui/home/latestCrossChainTxs/LatestCrossChainTxs.tsx @@ -3,8 +3,10 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import useApiQuery from 'lib/api/useApiQuery'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { INTERCHAIN_MESSAGE } from 'stubs/interchainIndexer'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; diff --git a/ui/home/latestCrossChainTxs/LatestCrossChainTxsItemDesktop.tsx b/ui/home/latestCrossChainTxs/LatestCrossChainTxsItemDesktop.tsx index 26fe57b0ae..3c309b17fb 100644 --- a/ui/home/latestCrossChainTxs/LatestCrossChainTxsItemDesktop.tsx +++ b/ui/home/latestCrossChainTxs/LatestCrossChainTxsItemDesktop.tsx @@ -3,11 +3,12 @@ import React from 'react'; import type { InterchainMessage } from '@blockscout/interchain-indexer-types'; +import TxEntityInterchain from 'client/slices/tx/components/entity/TxEntityInterchain'; + import { TableCell, TableRow } from 'toolkit/chakra/table'; import { mdash } from 'toolkit/utils/htmlEntities'; import CrossChainBridgeLink from 'ui/shared/crossChain/CrossChainBridgeLink'; import CrossChainMessageEntity from 'ui/shared/entities/crossChainMessage/CrossChainMessageEntity'; -import TxEntityInterchain from 'ui/shared/entities/tx/TxEntityInterchain'; import ChainLabel from 'ui/shared/externalChains/ChainLabel'; import CrossChainTxsStatusTag from 'ui/shared/statusTag/CrossChainTxsStatusTag'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/home/latestDeposits/LatestArbitrumDeposits.tsx b/ui/home/latestDeposits/LatestArbitrumDeposits.tsx index 90e9f28b53..c1d39544bf 100644 --- a/ui/home/latestDeposits/LatestArbitrumDeposits.tsx +++ b/ui/home/latestDeposits/LatestArbitrumDeposits.tsx @@ -1,13 +1,15 @@ import { Text } from '@chakra-ui/react'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; +import type { SocketMessage } from 'client/api/socket/types'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import useGradualIncrement from 'client/shared/hooks/useGradualIncrement'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; -import useApiQuery from 'lib/api/useApiQuery'; -import useGradualIncrement from 'lib/hooks/useGradualIncrement'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; import { ARBITRUM_MESSAGES_ITEM } from 'stubs/arbitrumL2'; import LatestTxsFallback from '../fallbacks/LatestTxsFallback'; diff --git a/ui/home/latestDeposits/LatestDeposits.tsx b/ui/home/latestDeposits/LatestDeposits.tsx index ca99d79095..9d875642bf 100644 --- a/ui/home/latestDeposits/LatestDeposits.tsx +++ b/ui/home/latestDeposits/LatestDeposits.tsx @@ -8,13 +8,16 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { layerLabels } from 'lib/rollups/utils'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/home/latestDeposits/LatestDepositsItem.tsx b/ui/home/latestDeposits/LatestDepositsItem.tsx index 1ee0bc7e47..7aeeba97ac 100644 --- a/ui/home/latestDeposits/LatestDepositsItem.tsx +++ b/ui/home/latestDeposits/LatestDepositsItem.tsx @@ -5,12 +5,15 @@ import { } from '@chakra-ui/react'; import React from 'react'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { layerLabels } from 'lib/rollups/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; type Props = { diff --git a/ui/home/latestDeposits/LatestOptimisticDeposits.tsx b/ui/home/latestDeposits/LatestOptimisticDeposits.tsx index d3aac91170..c23a4a69f6 100644 --- a/ui/home/latestDeposits/LatestOptimisticDeposits.tsx +++ b/ui/home/latestDeposits/LatestOptimisticDeposits.tsx @@ -1,13 +1,15 @@ import { Text } from '@chakra-ui/react'; import React from 'react'; -import type { SocketMessage } from 'lib/socket/types'; +import type { SocketMessage } from 'client/api/socket/types'; + +import useApiQuery from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import useGradualIncrement from 'client/shared/hooks/useGradualIncrement'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; -import useApiQuery from 'lib/api/useApiQuery'; -import useGradualIncrement from 'lib/hooks/useGradualIncrement'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; import { L2_DEPOSIT_ITEM } from 'stubs/L2'; import LatestTxsFallback from '../fallbacks/LatestTxsFallback'; diff --git a/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXItem.tsx b/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXItem.tsx index 52f98d44bf..6084a20bf3 100644 --- a/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXItem.tsx +++ b/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXItem.tsx @@ -3,9 +3,11 @@ import React from 'react'; import type { CctxListItem } from '@blockscout/zetachain-cctx-types'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; + +import TxEntityZetaChainCC from 'client/features/chain-variants/zeta-chain/TxEntityZetaChainCC'; + import { SECOND } from 'toolkit/utils/consts'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; -import TxEntityZetaChainCC from 'ui/shared/entities/tx/TxEntityZetaChainCC'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import ZetaChainCCTXReducedStatus from 'ui/shared/zetaChain/ZetaChainCCTXReducedStatus'; import ZetaChainCCTXValue from 'ui/shared/zetaChain/ZetaChainCCTXValue'; diff --git a/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXs.tsx b/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXs.tsx index baa4d59d43..86ceea850d 100644 --- a/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXs.tsx +++ b/ui/home/latestZetaChainCCTX/LatestZetaChainCCTXs.tsx @@ -4,16 +4,19 @@ import React from 'react'; import { Direction } from '@blockscout/zetachain-cctx-types'; import type { ListCctxsResponse } from '@blockscout/zetachain-cctx-types'; -import type { SocketMessage } from 'lib/socket/types'; +import type { SocketMessage } from 'client/api/socket/types'; import { route } from 'nextjs-routes'; -import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; -import useInitialList from 'lib/hooks/useInitialList'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import useSocketMessage from 'lib/socket/useSocketMessage'; +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useInitialList from 'client/shared/lists/useInitialList'; + import { generateListStub } from 'stubs/utils'; import { ZETA_CHAIN_CCTX_LIST_ITEM } from 'stubs/zetaChainCCTX'; import { Link } from 'toolkit/chakra/link'; diff --git a/ui/home/useHomeBlocksData.ts b/ui/home/useHomeBlocksData.ts new file mode 100644 index 0000000000..4b00cdd368 --- /dev/null +++ b/ui/home/useHomeBlocksData.ts @@ -0,0 +1,67 @@ +import { useQueryClient } from '@tanstack/react-query'; +import type { UseQueryResult } from '@tanstack/react-query'; +import React from 'react'; + +import type { SocketMessage } from 'client/api/socket/types'; +import type { Block } from 'client/slices/block/types/api'; + +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; +import type { ResourceError, ResourcePayload } from 'client/api/resources'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import { BLOCK } from 'client/slices/block/stubs/block'; + +import config from 'configs/app'; + +/** Max blocks kept in React Query cache for `general:homepage_blocks` (fetch + socket). */ +const HOME_BLOCKS_QUERY_LIMIT = 5; + +const isHomepageBlocksDataEnabled = (() => { + const rollupFeature = config.features.rollup; + const isLatestBlocksReplacedByBatches = rollupFeature.isEnabled && + !rollupFeature.homepage.showLatestBlocks && + [ 'arbitrum' ].includes(rollupFeature.type); + + return !isLatestBlocksReplacedByBatches || config.UI.homepage.stats.includes('total_blocks'); +})(); + +export type HomeBlocksQueryResult = UseQueryResult< + ResourcePayload<'general:homepage_blocks'>, + ResourceError +>; + +export default function useHomeBlocksData(): HomeBlocksQueryResult | undefined { + const queryClient = useQueryClient(); + + const blocksQuery = useApiQuery('general:homepage_blocks', { + queryOptions: { + enabled: isHomepageBlocksDataEnabled, + placeholderData: Array(HOME_BLOCKS_QUERY_LIMIT).fill(BLOCK), + }, + }); + + const handleNewBlockMessage: SocketMessage.NewBlock['handler'] = React.useCallback((payload) => { + queryClient.setQueryData(getResourceKey('general:homepage_blocks'), (prevData: Array | undefined) => { + const newData = prevData ? [ ...prevData ] : []; + + if (newData.some((block) => block.height === payload.block.height)) { + return newData; + } + + return [ payload.block, ...newData ].sort((b1, b2) => b2.height - b1.height).slice(0, HOME_BLOCKS_QUERY_LIMIT); + }); + }, [ queryClient ]); + + const channel = useSocketChannel({ + topic: 'blocks:new_block', + isDisabled: !isHomepageBlocksDataEnabled || blocksQuery.isPlaceholderData || blocksQuery.isError, + }); + useSocketMessage({ + channel, + event: 'new_block', + handler: handleNewBlockMessage, + }); + + return isHomepageBlocksDataEnabled ? blocksQuery : undefined; +} diff --git a/ui/home/useHomeLatestBatchData.ts b/ui/home/useHomeLatestBatchData.ts new file mode 100644 index 0000000000..c7978395e1 --- /dev/null +++ b/ui/home/useHomeLatestBatchData.ts @@ -0,0 +1,94 @@ +import { useQueryClient } from '@tanstack/react-query'; +import type { UseQueryResult } from '@tanstack/react-query'; +import React from 'react'; + +import type { SocketMessage } from 'client/api/socket/types'; + +import useApiQuery, { getResourceKey } from 'client/api/hooks/useApiQuery'; +import type { ResourceError } from 'client/api/resources'; +import useSocketChannel from 'client/api/socket/useSocketChannel'; +import useSocketMessage from 'client/api/socket/useSocketMessage'; + +import config from 'configs/app'; + +export type HomeLatestBatchQueryResult = UseQueryResult>; + +type LatestBatchSocketEventMessage = SocketMessage.NewArbitrumL2Batch; +type LatestBatchPayload = Parameters[0]; +type LatestBatchSocketMessage = LatestBatchSocketEventMessage | SocketMessage.Unknown; + +const shouldShowLatestBatchStat = config.UI.homepage.stats.includes('latest_batch'); + +export default function useHomeLatestBatchData(): HomeLatestBatchQueryResult | undefined { + const queryClient = useQueryClient(); + const rollupFeature = config.features.rollup; + + const zkSyncLatestBatchQuery = useApiQuery('general:homepage_zksync_latest_batch', { + queryOptions: { + placeholderData: 12345, + enabled: rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && shouldShowLatestBatchStat, + }, + }); + + const arbitrumLatestBatchQuery = useApiQuery('general:homepage_arbitrum_latest_batch', { + queryOptions: { + placeholderData: 12345, + enabled: rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && shouldShowLatestBatchStat, + }, + }); + + const [ latestBatchQuery, latestBatchSocketConfig ] = (() => { + if (!rollupFeature.isEnabled || !shouldShowLatestBatchStat) { + return [ undefined, undefined ] as const; + } + + switch (rollupFeature.type) { + case 'zkSync': + return [ zkSyncLatestBatchQuery, undefined ] as const; + case 'arbitrum': + return [ + arbitrumLatestBatchQuery, + { + topic: 'arbitrum:new_batch', + event: 'new_arbitrum_batch', + resource: 'general:homepage_arbitrum_latest_batch', + }, + ] as const; + default: + return [ undefined, undefined ] as const; + } + })(); + + const latestBatchSocketDisabled = + !latestBatchSocketConfig || + latestBatchQuery?.isError || + latestBatchQuery?.isPlaceholderData || + latestBatchQuery?.data === undefined; + + const handleNewLatestBatchMessage = React.useCallback((payload: LatestBatchPayload) => { + if (!latestBatchSocketConfig) { + return; + } + + queryClient.setQueryData(getResourceKey(latestBatchSocketConfig.resource), (prev: number | undefined) => { + const nextBatchNumber = payload.batch.number; + if (prev === undefined) { + return nextBatchNumber; + } + + return Math.max(prev, nextBatchNumber); + }); + }, [ queryClient, latestBatchSocketConfig ]); + + const latestBatchChannel = useSocketChannel({ + topic: latestBatchSocketConfig?.topic, + isDisabled: Boolean(latestBatchSocketDisabled), + }); + useSocketMessage({ + channel: latestBatchChannel, + event: latestBatchSocketConfig?.event, + handler: handleNewLatestBatchMessage, + } as LatestBatchSocketMessage); + + return latestBatchQuery; +} diff --git a/ui/home/utils.ts b/ui/home/utils.ts index 235bffc86d..30178153db 100644 --- a/ui/home/utils.ts +++ b/ui/home/utils.ts @@ -1,15 +1,26 @@ +import type { ReactElement } from 'react'; + import type { HomeStatsWidgetId } from 'types/homepage'; import config from 'configs/app'; import type { Props as StatsWidgetProps } from 'ui/shared/stats/StatsWidget'; -export interface HomeStatsItem extends StatsWidgetProps { - id: HomeStatsWidgetId; -} +export type HomeStatsComponentItem = { id: HomeStatsWidgetId; component: ReactElement }; +export type HomeStatsWidgetItem = StatsWidgetProps & { id: HomeStatsWidgetId; component?: undefined }; + +export type HomeStatsItem = HomeStatsComponentItem | HomeStatsWidgetItem; + +export const homeStatsWidgetCommonStyles = { + _odd: { + _last: { + gridColumn: 'span 2', + }, + }, +} as const; -export const isHomeStatsItemEnabled = (item: HomeStatsItem) => config.UI.homepage.stats.includes(item.id); +export const isHomeStatsItemEnabled = (item: { id: HomeStatsWidgetId }) => config.UI.homepage.stats.includes(item.id); -export const sortHomeStatsItems = (a: HomeStatsItem, b: HomeStatsItem) => { +export const sortHomeStatsItems = (a: { id: HomeStatsWidgetId }, b: { id: HomeStatsWidgetId }) => { const indexA = config.UI.homepage.stats.indexOf(a.id); const indexB = config.UI.homepage.stats.indexOf(b.id); if (indexA > indexB) { diff --git a/ui/hotContracts/HotContractsIntervalSelect.tsx b/ui/hotContracts/HotContractsIntervalSelect.tsx index 03df177090..b93a8301c1 100644 --- a/ui/hotContracts/HotContractsIntervalSelect.tsx +++ b/ui/hotContracts/HotContractsIntervalSelect.tsx @@ -3,7 +3,8 @@ import React from 'react'; import type { HotContractsInterval } from 'types/api/contracts'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; + import type { SelectOption } from 'toolkit/chakra/select'; import { Select } from 'toolkit/chakra/select'; import TagGroupSelect from 'ui/shared/tagGroupSelect/TagGroupSelect'; diff --git a/ui/hotContracts/HotContractsListItem.tsx b/ui/hotContracts/HotContractsListItem.tsx index d832acca0a..e7d51e0601 100644 --- a/ui/hotContracts/HotContractsListItem.tsx +++ b/ui/hotContracts/HotContractsListItem.tsx @@ -4,8 +4,9 @@ import React from 'react'; import type { HotContract } from 'types/api/contracts'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import { Reputation } from 'ui/shared/entities/token/TokenEntity'; import EntityTags from 'ui/shared/EntityTags/EntityTags'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; diff --git a/ui/hotContracts/HotContractsTable.tsx b/ui/hotContracts/HotContractsTable.tsx index 9450720fe3..4798012047 100644 --- a/ui/hotContracts/HotContractsTable.tsx +++ b/ui/hotContracts/HotContractsTable.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type { HotContract, HotContractsSortingField, HotContractsSortingValue } from 'types/api/contracts'; -import { currencyUnits } from 'lib/units'; +import { currencyUnits } from 'client/shared/chain/units'; + import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import getNextSortValue from 'ui/shared/sort/getNextSortValue'; diff --git a/ui/hotContracts/HotContractsTableItem.tsx b/ui/hotContracts/HotContractsTableItem.tsx index f8ccda38ef..0d584beea3 100644 --- a/ui/hotContracts/HotContractsTableItem.tsx +++ b/ui/hotContracts/HotContractsTableItem.tsx @@ -4,9 +4,10 @@ import React from 'react'; import type { HotContract } from 'types/api/contracts'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { TableCell, TableRow } from 'toolkit/chakra/table'; import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import { Reputation } from 'ui/shared/entities/token/TokenEntity'; import EntityTags from 'ui/shared/EntityTags/EntityTags'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/hotContracts/utils.ts b/ui/hotContracts/utils.ts index 4d244642a8..25850a2e0d 100644 --- a/ui/hotContracts/utils.ts +++ b/ui/hotContracts/utils.ts @@ -1,6 +1,7 @@ import type { HotContractsSortingValue, HotContractsSortingField, HotContractsInterval } from 'types/api/contracts'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import type { SelectOption } from 'toolkit/chakra/select'; export const SORT_OPTIONS: Array> = [ diff --git a/ui/internalTxs/InternalTxsListItem.tsx b/ui/internalTxs/InternalTxsListItem.tsx index d1fda5ff57..8bebdc3a03 100644 --- a/ui/internalTxs/InternalTxsListItem.tsx +++ b/ui/internalTxs/InternalTxsListItem.tsx @@ -4,17 +4,19 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; import type { ClusterChainConfig } from 'types/multichain'; -import { currencyUnits } from 'lib/units'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import { TX_INTERNALS_ITEMS } from 'client/slices/internal-tx/utils/utils'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import TxStatus from 'client/slices/tx/components/TxStatus'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { Badge } from 'toolkit/chakra/badge'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; -import TxStatus from 'ui/shared/statusTag/TxStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; -import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; type Props = InternalTransaction & { currentAddress?: string; isLoading?: boolean; showBlockInfo?: boolean; chainData?: ClusterChainConfig }; diff --git a/ui/internalTxs/InternalTxsTable.tsx b/ui/internalTxs/InternalTxsTable.tsx index 7f17603c77..67f876d131 100644 --- a/ui/internalTxs/InternalTxsTable.tsx +++ b/ui/internalTxs/InternalTxsTable.tsx @@ -2,9 +2,11 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import { currencyUnits } from 'lib/units'; import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/internalTxs/InternalTxsTableItem.tsx b/ui/internalTxs/InternalTxsTableItem.tsx index b5ed3afa52..cb467305bb 100644 --- a/ui/internalTxs/InternalTxsTableItem.tsx +++ b/ui/internalTxs/InternalTxsTableItem.tsx @@ -4,16 +4,17 @@ import React from 'react'; import type { InternalTransaction } from 'types/api/internalTransaction'; import type { ClusterChainConfig } from 'types/multichain'; +import AddressFromTo from 'client/slices/address/components/from-to/AddressFromTo'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; +import { TX_INTERNALS_ITEMS } from 'client/slices/internal-tx/utils/utils'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import TxStatus from 'client/slices/tx/components/TxStatus'; + import { Badge } from 'toolkit/chakra/badge'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressFromTo from 'ui/shared/address/AddressFromTo'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import ChainIcon from 'ui/shared/externalChains/ChainIcon'; -import TxStatus from 'ui/shared/statusTag/TxStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; -import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; type Props = InternalTransaction & { currentAddress?: string; isLoading?: boolean; showBlockInfo?: boolean; chainData?: ClusterChainConfig }; diff --git a/ui/internalTxs/useInternalTxsQuery.tsx b/ui/internalTxs/useInternalTxsQuery.tsx index 56f2161247..2b10d903af 100644 --- a/ui/internalTxs/useInternalTxsQuery.tsx +++ b/ui/internalTxs/useInternalTxsQuery.tsx @@ -1,8 +1,9 @@ import { useRouter } from 'next/router'; import React from 'react'; -import useDebounce from 'lib/hooks/useDebounce'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useDebounce from 'client/shared/hooks/useDebounce'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { INTERNAL_TX } from 'stubs/internalTx'; import { generateListStub } from 'stubs/utils'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; diff --git a/ui/interopMessages/InteropMessageDestinationTx.tsx b/ui/interopMessages/InteropMessageDestinationTx.tsx index ec7a42c83d..2d3461b00d 100644 --- a/ui/interopMessages/InteropMessageDestinationTx.tsx +++ b/ui/interopMessages/InteropMessageDestinationTx.tsx @@ -2,9 +2,10 @@ import React from 'react'; import type { ChainInfo } from 'types/api/interop'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import type { EntityProps } from 'ui/shared/entities/tx/TxEntity'; -import TxEntityInterop from 'ui/shared/entities/tx/TxEntityInterop'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; +import type { EntityProps } from 'client/slices/tx/components/entity/TxEntity'; + +import TxEntityInterop from 'client/features/op-interop/components/TxEntityInterop'; type Props = { relay_transaction_hash?: string | null; diff --git a/ui/interopMessages/InteropMessageSourceTx.tsx b/ui/interopMessages/InteropMessageSourceTx.tsx index 0928fd459f..7774679d79 100644 --- a/ui/interopMessages/InteropMessageSourceTx.tsx +++ b/ui/interopMessages/InteropMessageSourceTx.tsx @@ -2,9 +2,10 @@ import React from 'react'; import type { ChainInfo } from 'types/api/interop'; -import type { EntityProps } from 'ui/shared/entities/tx/TxEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityInterop from 'ui/shared/entities/tx/TxEntityInterop'; +import type { EntityProps } from 'client/slices/tx/components/entity/TxEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import TxEntityInterop from 'client/features/op-interop/components/TxEntityInterop'; type Props = { init_transaction_hash?: string | null; diff --git a/ui/interopMessages/InteropMessagesListItem.tsx b/ui/interopMessages/InteropMessagesListItem.tsx index 2578f7107a..c9f199bea8 100644 --- a/ui/interopMessages/InteropMessagesListItem.tsx +++ b/ui/interopMessages/InteropMessagesListItem.tsx @@ -3,9 +3,11 @@ import React from 'react'; import type { InteropMessage } from 'types/api/interop'; -import AddressFromToIcon from 'ui/shared/address/AddressFromToIcon'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import AddressEntityInterop from 'ui/shared/entities/address/AddressEntityInterop'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import AddressFromToIcon from 'client/slices/address/components/from-to/AddressFromToIcon'; + +import AddressEntityInterop from 'client/features/op-interop/components/AddressEntityInterop'; + import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import InteropMessageStatus from 'ui/shared/statusTag/InteropMessageStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/interopMessages/InteropMessagesTableItem.tsx b/ui/interopMessages/InteropMessagesTableItem.tsx index dd1ddaa2c0..2f7b612d97 100644 --- a/ui/interopMessages/InteropMessagesTableItem.tsx +++ b/ui/interopMessages/InteropMessagesTableItem.tsx @@ -2,11 +2,13 @@ import React from 'react'; import type { InteropMessage } from 'types/api/interop'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import AddressFromToIcon from 'client/slices/address/components/from-to/AddressFromToIcon'; + +import AddressEntityInterop from 'client/features/op-interop/components/AddressEntityInterop'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressFromToIcon from 'ui/shared/address/AddressFromToIcon'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import AddressEntityInterop from 'ui/shared/entities/address/AddressEntityInterop'; import InteropMessageStatus from 'ui/shared/statusTag/InteropMessageStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/marketplace/Banner.tsx b/ui/marketplace/Banner.tsx index ac6dec09ce..5f3074cf10 100644 --- a/ui/marketplace/Banner.tsx +++ b/ui/marketplace/Banner.tsx @@ -4,8 +4,9 @@ import React from 'react'; import type { MarketplaceApp } from 'types/client/marketplace'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { apps as appsMock } from 'mocks/apps/apps'; import AdBanner from 'ui/shared/ad/AdBanner'; diff --git a/ui/marketplace/Banner/FeaturedApp.tsx b/ui/marketplace/Banner/FeaturedApp.tsx index 5bd21ea2c8..edac518180 100644 --- a/ui/marketplace/Banner/FeaturedApp.tsx +++ b/ui/marketplace/Banner/FeaturedApp.tsx @@ -4,8 +4,9 @@ import React, { useCallback } from 'react'; import type { MarketplaceApp } from 'types/client/marketplace'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import * as mixpanel from 'lib/mixpanel/index'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { useColorModeValue } from 'toolkit/chakra/color-mode'; import { Heading } from 'toolkit/chakra/heading'; import { IconButton } from 'toolkit/chakra/icon-button'; diff --git a/ui/marketplace/Banner/IframeBanner.tsx b/ui/marketplace/Banner/IframeBanner.tsx index 48b27e9f91..25c0c8a570 100644 --- a/ui/marketplace/Banner/IframeBanner.tsx +++ b/ui/marketplace/Banner/IframeBanner.tsx @@ -1,7 +1,8 @@ import { chakra } from '@chakra-ui/react'; import React, { useCallback, useState } from 'react'; -import * as mixpanel from 'lib/mixpanel/index'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/marketplace/MarketplaceAppGraphLinks.tsx b/ui/marketplace/MarketplaceAppGraphLinks.tsx index 574e94c15f..4a17f9128f 100644 --- a/ui/marketplace/MarketplaceAppGraphLinks.tsx +++ b/ui/marketplace/MarketplaceAppGraphLinks.tsx @@ -6,7 +6,8 @@ import { } from '@chakra-ui/react'; import React from 'react'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { Link } from 'toolkit/chakra/link'; import { Tooltip } from 'toolkit/chakra/tooltip'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/ui/marketplace/MarketplaceAppIframe.tsx b/ui/marketplace/MarketplaceAppIframe.tsx index c49f566d44..d858ae2c55 100644 --- a/ui/marketplace/MarketplaceAppIframe.tsx +++ b/ui/marketplace/MarketplaceAppIframe.tsx @@ -2,9 +2,10 @@ import { Center, chakra } from '@chakra-ui/react'; import { DappscoutIframeProvider, useDappscoutIframe } from 'dappscout-iframe'; import React, { useCallback, useEffect, useState, useMemo } from 'react'; +import useWeb3Wallet from 'client/features/connect-wallet/hooks/useWallet'; + import config from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; -import useWeb3Wallet from 'lib/web3/useWallet'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import useMarketplaceWallet from '../marketplace/useMarketplaceWallet'; diff --git a/ui/marketplace/MarketplaceAppTopBar.tsx b/ui/marketplace/MarketplaceAppTopBar.tsx index 45892dd9b4..3b12b9dcf5 100644 --- a/ui/marketplace/MarketplaceAppTopBar.tsx +++ b/ui/marketplace/MarketplaceAppTopBar.tsx @@ -5,10 +5,11 @@ import type { MarketplaceApp } from 'types/client/marketplace'; import { route } from 'nextjs-routes'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; import { useAppContext } from 'lib/contexts/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import * as mixpanel from 'lib/mixpanel/index'; import { Link } from 'toolkit/chakra/link'; import { BackToButton } from 'toolkit/components/buttons/BackToButton'; import { makePrettyLink } from 'toolkit/utils/url'; diff --git a/ui/marketplace/MarketplaceDisclaimerModal.tsx b/ui/marketplace/MarketplaceDisclaimerModal.tsx index 26ef140da4..dd6923bf0d 100644 --- a/ui/marketplace/MarketplaceDisclaimerModal.tsx +++ b/ui/marketplace/MarketplaceDisclaimerModal.tsx @@ -3,7 +3,8 @@ import React from 'react'; import { route } from 'nextjs-routes'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { Button } from 'toolkit/chakra/button'; import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; import { Link } from 'toolkit/chakra/link'; @@ -12,15 +13,18 @@ type Props = { isOpen: boolean; onClose: () => void; appId: string; + external?: boolean; + url: string; }; -const MarketplaceDisclaimerModal = ({ isOpen, onClose, appId }: Props) => { +const MarketplaceDisclaimerModal = ({ isOpen, onClose, appId, external, url }: Props) => { const isMobile = useIsMobile(); const handleContinueClick = React.useCallback(() => { window.localStorage.setItem('marketplace-disclaimer-shown', 'true'); - }, [ ]); + onClose(); + }, [ onClose ]); return ( { flexDirection="row" alignItems="center" > - + diff --git a/ui/marketplace/MarketplaceList.tsx b/ui/marketplace/MarketplaceList.tsx index 1cf5b45c05..ba001628bb 100644 --- a/ui/marketplace/MarketplaceList.tsx +++ b/ui/marketplace/MarketplaceList.tsx @@ -5,8 +5,8 @@ import type { MouseEvent } from 'react'; import type { MarketplaceApp } from 'types/client/marketplace'; -import useLazyRenderedList from 'lib/hooks/useLazyRenderedList'; -import * as mixpanel from 'lib/mixpanel/index'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useLazyRenderedList from 'client/shared/lists/useLazyRenderedList'; import EmptySearchResult from './EmptySearchResult'; import MarketplaceAppCard from './MarketplaceAppCard'; diff --git a/ui/marketplace/Rating/PopoverContent.tsx b/ui/marketplace/Rating/PopoverContent.tsx index 48da83320a..2fa622544e 100644 --- a/ui/marketplace/Rating/PopoverContent.tsx +++ b/ui/marketplace/Rating/PopoverContent.tsx @@ -2,10 +2,12 @@ import { Text, Flex, Spinner } from '@chakra-ui/react'; import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; +import useApiFetch from 'client/api/hooks/useApiFetch'; + +import type { EventTypes, EventPayload } from 'client/shared/analytics/mixpanel'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import config from 'configs/app'; -import useApiFetch from 'lib/api/useApiFetch'; -import type { EventTypes, EventPayload } from 'lib/mixpanel/index'; -import * as mixpanel from 'lib/mixpanel/index'; import { Rating } from 'toolkit/chakra/rating'; import { toaster } from 'toolkit/chakra/toaster'; import IconSvg from 'ui/shared/IconSvg'; @@ -29,7 +31,7 @@ const PopoverContent = ({ appId, userRating, source }: Props) => { try { await apiFetch('admin:marketplace_rate_dapp', { - pathParams: { chainId: config.chain.id, dappId: appId }, + pathParams: { instanceId: config.apis.admin?.instanceId, dappId: appId }, fetchParams: { method: 'POST', body: { rating: value }, diff --git a/ui/marketplace/Rating/Rating.tsx b/ui/marketplace/Rating/Rating.tsx index c0ca0af2d0..82188d9784 100644 --- a/ui/marketplace/Rating/Rating.tsx +++ b/ui/marketplace/Rating/Rating.tsx @@ -1,8 +1,9 @@ import { Text } from '@chakra-ui/react'; import React from 'react'; +import type { EventTypes, EventPayload } from 'client/shared/analytics/mixpanel'; + import config from 'configs/app'; -import type { EventTypes, EventPayload } from 'lib/mixpanel/index'; import type { PopoverContentProps } from 'toolkit/chakra/popover'; import { PopoverBody, PopoverContent, PopoverRoot } from 'toolkit/chakra/popover'; import { Rating as RatingComponent } from 'toolkit/chakra/rating'; diff --git a/ui/marketplace/Rating/TriggerButton.tsx b/ui/marketplace/Rating/TriggerButton.tsx index 6a4e043566..3a82d635ea 100644 --- a/ui/marketplace/Rating/TriggerButton.tsx +++ b/ui/marketplace/Rating/TriggerButton.tsx @@ -3,9 +3,10 @@ import React from 'react'; import { getFeaturePayload } from 'configs/app/features/types'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import usePreventFocusAfterModalClosing from 'client/shared/hooks/usePreventFocusAfterModalClosing'; + import config from 'configs/app'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing'; import type { ButtonProps } from 'toolkit/chakra/button'; import { Button } from 'toolkit/chakra/button'; import { PopoverTrigger } from 'toolkit/chakra/popover'; diff --git a/ui/marketplace/essentialDapps/multisend/Multisend.tsx b/ui/marketplace/essentialDapps/multisend/Multisend.tsx index c22faf2f26..39889e1ced 100644 --- a/ui/marketplace/essentialDapps/multisend/Multisend.tsx +++ b/ui/marketplace/essentialDapps/multisend/Multisend.tsx @@ -4,9 +4,10 @@ import React from 'react'; import { getFeaturePayload } from 'configs/app/features/types'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; -import useIsMobile from 'lib/hooks/useIsMobile'; import AdBanner from 'ui/shared/ad/AdBanner'; const feature = getFeaturePayload(config.features.marketplace); diff --git a/ui/marketplace/essentialDapps/revoke/Revoke.tsx b/ui/marketplace/essentialDapps/revoke/Revoke.tsx index 773ae4f538..cbef94a65b 100644 --- a/ui/marketplace/essentialDapps/revoke/Revoke.tsx +++ b/ui/marketplace/essentialDapps/revoke/Revoke.tsx @@ -6,13 +6,15 @@ import { mainnet } from 'viem/chains'; import { getEnsAddress, normalize } from 'viem/ens'; import { useAccount } from 'wagmi'; +import useWeb3Wallet from 'client/features/connect-wallet/hooks/useWallet'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import { useQueryParams } from 'client/shared/router/useQueryParams'; + import config from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import * as mixpanel from 'lib/mixpanel'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { useQueryParams } from 'lib/router/useQueryParams'; -import useWeb3Wallet from 'lib/web3/useWallet'; import { Button } from 'toolkit/chakra/button'; import { EmptyState } from 'toolkit/chakra/empty-state'; import { Tooltip } from 'toolkit/chakra/tooltip'; diff --git a/ui/marketplace/essentialDapps/revoke/components/AddressEntity.tsx b/ui/marketplace/essentialDapps/revoke/components/AddressEntity.tsx index 443a0e2196..35f701caec 100644 --- a/ui/marketplace/essentialDapps/revoke/components/AddressEntity.tsx +++ b/ui/marketplace/essentialDapps/revoke/components/AddressEntity.tsx @@ -2,8 +2,8 @@ import { chakra } from '@chakra-ui/react'; import { mainnet } from 'viem/chains'; import { useEnsName } from 'wagmi'; -import type { EntityProps } from 'ui/shared/entities/address/AddressEntity'; -import AddressEntityBase from 'ui/shared/entities/address/AddressEntity'; +import type { EntityProps } from 'client/slices/address/components/entity/AddressEntity'; +import AddressEntityBase from 'client/slices/address/components/entity/AddressEntity'; function AddressEntity({ address, ...props }: EntityProps) { const ensQuery = useEnsName({ diff --git a/ui/marketplace/essentialDapps/revoke/components/ApprovalsListItem.tsx b/ui/marketplace/essentialDapps/revoke/components/ApprovalsListItem.tsx index a0286bc261..a36ba424f8 100644 --- a/ui/marketplace/essentialDapps/revoke/components/ApprovalsListItem.tsx +++ b/ui/marketplace/essentialDapps/revoke/components/ApprovalsListItem.tsx @@ -4,10 +4,11 @@ import { useCallback, useState } from 'react'; import type { EssentialDappsChainConfig } from 'types/client/marketplace'; import type { AllowanceType } from 'types/client/revoke'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import dayjs from 'lib/date/dayjs'; import { Button } from 'toolkit/chakra/button'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import NumberEntity from 'ui/shared/NumberEntity'; @@ -38,12 +39,12 @@ export default function ApprovalsListItem({ const handleRevoke = useCallback(async() => { setIsPending(true); - const success = await revoke(approval, Number(selectedChain?.id)); + const success = await revoke(approval, selectedChain); if (success) { hideApproval(approval); } setIsPending(false); - }, [ revoke, hideApproval, approval, selectedChain?.id ]); + }, [ revoke, hideApproval, approval, selectedChain ]); return ( { setIsPending(true); - const success = await revoke(approval, Number(selectedChain?.id)); + const success = await revoke(approval, selectedChain); if (success) { hideApproval(approval); } setIsPending(false); - }, [ revoke, hideApproval, approval, selectedChain?.id ]); + }, [ revoke, hideApproval, approval, selectedChain ]); return ( diff --git a/ui/marketplace/essentialDapps/revoke/components/Content.tsx b/ui/marketplace/essentialDapps/revoke/components/Content.tsx index 414815cdd1..66ffd5e4ff 100644 --- a/ui/marketplace/essentialDapps/revoke/components/Content.tsx +++ b/ui/marketplace/essentialDapps/revoke/components/Content.tsx @@ -6,7 +6,8 @@ import type { AllowanceType } from 'types/client/revoke'; import { route } from 'nextjs/routes'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { Badge } from 'toolkit/chakra/badge'; import { Heading } from 'toolkit/chakra/heading'; import { Image } from 'toolkit/chakra/image'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useCoinBalanceQuery.tsx b/ui/marketplace/essentialDapps/revoke/hooks/useCoinBalanceQuery.tsx index a03cb64034..416e1e2811 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useCoinBalanceQuery.tsx +++ b/ui/marketplace/essentialDapps/revoke/hooks/useCoinBalanceQuery.tsx @@ -3,7 +3,7 @@ import { formatUnits } from 'viem'; import type { EssentialDappsChainConfig } from 'types/client/marketplace'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; const PLACEHOLDER_DATA = { balance: '10000', diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useGetBlockTimestamp.tsx b/ui/marketplace/essentialDapps/revoke/hooks/useGetBlockTimestamp.tsx index 9f57a69a68..23ea64930c 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useGetBlockTimestamp.tsx +++ b/ui/marketplace/essentialDapps/revoke/hooks/useGetBlockTimestamp.tsx @@ -1,9 +1,9 @@ import { useCallback, useEffect } from 'react'; -import type { Block } from 'types/api/block'; +import type { Block } from 'client/slices/block/types/api'; import type { EssentialDappsChainConfig } from 'types/client/marketplace'; -import useApiFetch from 'lib/api/useApiFetch'; +import useApiFetch from 'client/api/hooks/useApiFetch'; // Cache for block timestamp requests across the session const timestampCache = new Map>(); diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx b/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx index d698dec84e..45f9ff8070 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx +++ b/ui/marketplace/essentialDapps/revoke/hooks/useRevoke.tsx @@ -1,13 +1,18 @@ +import { Flex, Text } from '@chakra-ui/react'; import ERC20Artifact from '@openzeppelin/contracts/build/contracts/ERC20.json'; import NftArtifact from '@openzeppelin/contracts/build/contracts/ERC721.json'; import { useCallback } from 'react'; import { waitForTransactionReceipt } from 'viem/actions'; import { useAccount, useWriteContract, useSwitchChain } from 'wagmi'; +import type { EssentialDappsChainConfig } from 'types/client/marketplace'; import type { AllowanceType } from 'types/client/revoke'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import useRewardsActivity from 'lib/hooks/useRewardsActivity'; -import * as mixpanel from 'lib/mixpanel/index'; import { toaster } from 'toolkit/chakra/toaster'; import createPublicClient from '../lib/createPublicClient'; @@ -18,13 +23,13 @@ export default function useRevoke() { const { writeContractAsync } = useWriteContract(); const { trackTransaction, trackTransactionConfirm } = useRewardsActivity(); - return useCallback(async(approval: AllowanceType, chainId: number) => { + return useCallback(async(approval: AllowanceType, chain?: EssentialDappsChainConfig) => { try { if (!userAddress) return; - await switchChainAsync({ chainId }); + await switchChainAsync({ chainId: Number(chain?.id) }); - const activityResponse = await trackTransaction(userAddress, approval.address, String(chainId)); + const activityResponse = await trackTransaction(userAddress, approval.address, chain?.id); const isErc20 = approval.type === 'ERC-20'; @@ -34,7 +39,7 @@ export default function useRevoke() { abi: isErc20 ? ERC20Artifact.abi : NftArtifact.abi, functionName: isErc20 ? 'approve' : 'setApprovalForAll', args: [ approval.spender, isErc20 ? 0 : false ], - chainId, + chainId: Number(chain?.id), }); mixpanel.logEvent(mixpanel.EventTypes.WALLET_ACTION, { @@ -42,14 +47,14 @@ export default function useRevoke() { Address: userAddress, AppId: 'revoke', Source: 'Essential dapps', - ChainId: String(chainId), + ChainId: chain?.id, }); if (activityResponse?.token) { await trackTransactionConfirm(hash, activityResponse.token); } - const publicClient = createPublicClient(String(chainId)); + const publicClient = createPublicClient(chain?.id); if (!publicClient) { throw new Error('Public client not found'); } @@ -62,7 +67,21 @@ export default function useRevoke() { toaster.success({ title: 'Success', - description: 'Approval revoked successfully.', + meta: { + renderDescription: () => ( + + Approval revoked successfully. + + + ), + }, }); return true; @@ -70,7 +89,10 @@ export default function useRevoke() { } catch (error) { toaster.error({ title: 'Error', - description: (error as Error)?.message || 'Something went wrong. Try again later.', + description: + (error as { shortMessage?: string })?.shortMessage || + (error as Error)?.message || + 'Something went wrong. Try again later.', }); return false; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useSearchErc20Allowances.tsx b/ui/marketplace/essentialDapps/revoke/hooks/useSearchErc20Allowances.tsx index c3c3be536b..36ad114c45 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useSearchErc20Allowances.tsx +++ b/ui/marketplace/essentialDapps/revoke/hooks/useSearchErc20Allowances.tsx @@ -4,12 +4,12 @@ import { useCallback } from 'react'; import { getAddress, formatUnits, slice } from 'viem'; import type { PublicClient, Log } from 'viem'; -import type { AddressTokenBalancesResponse } from 'types/api/address'; +import type { AddressTokenBalancesResponse } from 'client/slices/address/types/api'; import type { TokenInfo } from 'types/api/token'; import type { EssentialDappsChainConfig } from 'types/client/marketplace'; import type { AllowanceType, ContractAllowanceType } from 'types/client/revoke'; -import useApiFetch from 'lib/api/useApiFetch'; +import useApiFetch from 'client/api/hooks/useApiFetch'; import useGetBlockTimestamp from './useGetBlockTimestamp'; diff --git a/ui/marketplace/essentialDapps/revoke/hooks/useSearchNftAllowances.tsx b/ui/marketplace/essentialDapps/revoke/hooks/useSearchNftAllowances.tsx index 78d9ffce4b..fb96afcdae 100644 --- a/ui/marketplace/essentialDapps/revoke/hooks/useSearchNftAllowances.tsx +++ b/ui/marketplace/essentialDapps/revoke/hooks/useSearchNftAllowances.tsx @@ -7,7 +7,8 @@ import type { TokenInfo } from 'types/api/token'; import type { EssentialDappsChainConfig } from 'types/client/marketplace'; import type { AllowanceType, ContractAllowanceType } from 'types/client/revoke'; -import useApiFetch from 'lib/api/useApiFetch'; +import useApiFetch from 'client/api/hooks/useApiFetch'; + import { ZERO_ADDRESS } from 'toolkit/utils/consts'; import getLogs from '../lib/getLogs'; diff --git a/ui/marketplace/essentialDapps/revoke/lib/createPublicClient.ts b/ui/marketplace/essentialDapps/revoke/lib/createPublicClient.ts index 22265d0a54..50d50e4d69 100644 --- a/ui/marketplace/essentialDapps/revoke/lib/createPublicClient.ts +++ b/ui/marketplace/essentialDapps/revoke/lib/createPublicClient.ts @@ -1,8 +1,9 @@ import { createPublicClient as createPublicClientDefault, http } from 'viem'; import type { PublicClient } from 'viem'; +import { chains } from 'client/features/connect-wallet/utils/chains'; + import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; -import { chains } from 'lib/web3/chains'; export default function createPublicClient(chainId: string | undefined): PublicClient | undefined { const chain = chains.find((chain) => chain.id === Number(chainId)); diff --git a/ui/marketplace/essentialDapps/swap/Swap.tsx b/ui/marketplace/essentialDapps/swap/Swap.tsx index 23830dcce0..da9e2891ab 100644 --- a/ui/marketplace/essentialDapps/swap/Swap.tsx +++ b/ui/marketplace/essentialDapps/swap/Swap.tsx @@ -3,9 +3,10 @@ import React, { useMemo } from 'react'; import { getFeaturePayload } from 'configs/app/features/types'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import config from 'configs/app'; import essentialDappsChainsConfig from 'configs/essential-dapps-chains'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { useColorMode } from 'toolkit/chakra/color-mode'; import { BODY_TYPEFACE } from 'toolkit/theme/foundations/typography'; import MarketplaceAppIframe from 'ui/marketplace/MarketplaceAppIframe'; diff --git a/ui/marketplace/useAutoConnectWallet.tsx b/ui/marketplace/useAutoConnectWallet.tsx index 52b5a0c8ee..efb71f0919 100644 --- a/ui/marketplace/useAutoConnectWallet.tsx +++ b/ui/marketplace/useAutoConnectWallet.tsx @@ -1,9 +1,10 @@ import { useRouter } from 'next/router'; import { useEffect, useRef } from 'react'; -import removeQueryParam from 'lib/router/removeQueryParam'; -import updateQueryParam from 'lib/router/updateQueryParam'; -import useWeb3Wallet from 'lib/web3/useWallet'; +import useWeb3Wallet from 'client/features/connect-wallet/hooks/useWallet'; + +import removeQueryParam from 'client/shared/router/remove-query-param'; +import updateQueryParam from 'client/shared/router/update-query-param'; export default function useAutoConnectWallet() { const router = useRouter(); diff --git a/ui/marketplace/useMarketplace.tsx b/ui/marketplace/useMarketplace.tsx index 2764e2983e..8d7d557634 100644 --- a/ui/marketplace/useMarketplace.tsx +++ b/ui/marketplace/useMarketplace.tsx @@ -3,10 +3,10 @@ import React from 'react'; import { MarketplaceCategory } from 'types/client/marketplace'; -import useDebounce from 'lib/hooks/useDebounce'; -import * as mixpanel from 'lib/mixpanel/index'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import removeQueryParam from 'lib/router/removeQueryParam'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import useDebounce from 'client/shared/hooks/useDebounce'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import removeQueryParam from 'client/shared/router/remove-query-param'; import useMarketplaceApps from './useMarketplaceApps'; import useMarketplaceCategories from './useMarketplaceCategories'; diff --git a/ui/marketplace/useMarketplaceApps.tsx b/ui/marketplace/useMarketplaceApps.tsx index 3fade8847a..ba120f4dfa 100644 --- a/ui/marketplace/useMarketplaceApps.tsx +++ b/ui/marketplace/useMarketplaceApps.tsx @@ -4,10 +4,11 @@ import React from 'react'; import type { MarketplaceApp } from 'types/client/marketplace'; import { MarketplaceCategory } from 'types/client/marketplace'; +import useApiFetch from 'client/api/hooks/useApiFetch'; +import useFetch from 'client/api/hooks/useFetch'; +import type { ResourceError } from 'client/api/resources'; + import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useApiFetch from 'lib/api/useApiFetch'; -import useFetch from 'lib/hooks/useFetch'; import { MARKETPLACE_APP } from 'stubs/marketplace'; import useIsAuth from 'ui/snippets/auth/useIsAuth'; @@ -82,7 +83,7 @@ export default function useMarketplaceApps( } else if ('configUrl' in feature) { return fetch, unknown>(feature.configUrl, undefined, { resource: 'marketplace-dapps' }); } else { - return apiFetch('admin:marketplace_dapps', { pathParams: { chainId: config.chain.id } }); + return apiFetch('admin:marketplace_dapps', { pathParams: { instanceId: config.apis.admin?.instanceId } }); } }, select: (data) => sortApps(data as Array, snapshotFavoriteApps), diff --git a/ui/marketplace/useMarketplaceCategories.tsx b/ui/marketplace/useMarketplaceCategories.tsx index 671c368dc4..ee77ef9384 100644 --- a/ui/marketplace/useMarketplaceCategories.tsx +++ b/ui/marketplace/useMarketplaceCategories.tsx @@ -3,9 +3,10 @@ import React from 'react'; import type { MarketplaceApp } from 'types/client/marketplace'; +import useApiFetch from 'client/api/hooks/useFetch'; +import type { ResourceError } from 'client/api/resources'; + import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; -import useApiFetch from 'lib/hooks/useFetch'; import { CATEGORIES } from 'stubs/marketplace'; const feature = config.features.marketplace; diff --git a/ui/marketplace/useMarketplaceWallet.tsx b/ui/marketplace/useMarketplaceWallet.tsx index 12fdd6142f..1cd6ac3e98 100644 --- a/ui/marketplace/useMarketplaceWallet.tsx +++ b/ui/marketplace/useMarketplaceWallet.tsx @@ -3,9 +3,10 @@ import { useCallback } from 'react'; import type { Account, SignTypedDataParameters } from 'viem'; import { useAccount, useSendTransaction, useSwitchChain, useSignMessage, useSignTypedData } from 'wagmi'; +import * as mixpanel from 'client/shared/analytics/mixpanel'; + import config from 'configs/app'; import useRewardsActivity from 'lib/hooks/useRewardsActivity'; -import * as mixpanel from 'lib/mixpanel/index'; type SendTransactionArgs = { chainId?: number; diff --git a/ui/marketplace/utils.ts b/ui/marketplace/utils.ts index 96451b396f..e2baa31dd4 100644 --- a/ui/marketplace/utils.ts +++ b/ui/marketplace/utils.ts @@ -1,8 +1,9 @@ import type { NextRouter } from 'next/router'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import removeQueryParam from 'client/shared/router/remove-query-param'; + import config from 'configs/app'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import removeQueryParam from 'lib/router/removeQueryParam'; import type { SelectOption } from 'toolkit/chakra/select'; const feature = config.features.marketplace; diff --git a/ui/megaEth/uptime/Uptime.tsx b/ui/megaEth/uptime/Uptime.tsx index 013ae852bc..18af5cd838 100644 --- a/ui/megaEth/uptime/Uptime.tsx +++ b/ui/megaEth/uptime/Uptime.tsx @@ -18,7 +18,7 @@ const Uptime = () => { contentAfter={ } /> - + ); }; diff --git a/ui/megaEth/uptime/UptimeCharts.tsx b/ui/megaEth/uptime/UptimeCharts.tsx index 9e26d665c1..79a0bf5e5c 100644 --- a/ui/megaEth/uptime/UptimeCharts.tsx +++ b/ui/megaEth/uptime/UptimeCharts.tsx @@ -2,12 +2,12 @@ import { Box, Flex, Grid, GridItem } from '@chakra-ui/react'; import * as d3 from 'd3'; import React from 'react'; -import type { AxesConfigFn } from 'toolkit/components/charts/types'; +import type { LineChartAxesConfigFn } from 'toolkit/components/charts/line/types'; import type { UptimeHistoryFull, UptimeHistoryItem } from 'types/api/megaEth'; import { useSettingsContext } from 'lib/contexts/settings'; import { Heading } from 'toolkit/chakra/heading'; -import { ChartWidget } from 'toolkit/components/charts/ChartWidget'; +import { LineChartWidget } from 'toolkit/components/charts/line'; import { DAY, HOUR, SECOND } from 'toolkit/utils/consts'; import { useChartsConfig } from 'ui/shared/chart/config'; import TagGroupSelect from 'ui/shared/tagGroupSelect/TagGroupSelect'; @@ -22,7 +22,7 @@ type IntervalId = (typeof INTERVALS)[number]['id']; const TIME_FORMAT = '%e %b %Y, %H:%M:%S'; -const AXES_CONFIG_BASE: (isLocalTime: boolean) => AxesConfigFn = (isLocalTime) => ({ isEnlarged, isMobile }) => ({ +const AXES_CONFIG_BASE: (isLocalTime: boolean) => LineChartAxesConfigFn = (isLocalTime) => ({ isEnlarged, isMobile }) => ({ y: { scale: { min: 0 }, }, @@ -34,7 +34,7 @@ const AXES_CONFIG_BASE: (isLocalTime: boolean) => AxesConfigFn = (isLocalTime) = }, }); -const AXES_CONFIG_LONG: AxesConfigFn = () => ({ +const AXES_CONFIG_LONG: LineChartAxesConfigFn = () => ({ y: { scale: { min: 0 }, }, @@ -75,9 +75,10 @@ const smoothData = (data: Array, windowSize: number): Array { +const UptimeCharts = ({ historyData, isLoading }: Props) => { const [ interval, setInterval ] = React.useState('3h'); const chartsConfig = useChartsConfig(); @@ -233,30 +234,33 @@ const UptimeCharts = ({ historyData }: Props) => { gap={ 4 } > - - - diff --git a/ui/messages/ArbitrumL2Messages.tsx b/ui/messages/ArbitrumL2Messages.tsx index 62daa67f84..59e8fc4698 100644 --- a/ui/messages/ArbitrumL2Messages.tsx +++ b/ui/messages/ArbitrumL2Messages.tsx @@ -1,7 +1,8 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { layerLabels } from 'lib/rollups/utils'; import { ARBITRUM_MESSAGES_ITEM } from 'stubs/arbitrumL2'; import { generateListStub } from 'stubs/utils'; diff --git a/ui/messages/ArbitrumL2MessagesListItem.tsx b/ui/messages/ArbitrumL2MessagesListItem.tsx index c2c9a5d2ee..10bb06dff9 100644 --- a/ui/messages/ArbitrumL2MessagesListItem.tsx +++ b/ui/messages/ArbitrumL2MessagesListItem.tsx @@ -5,14 +5,16 @@ import type { ArbitrumL2MessagesItem } from 'types/api/arbitrumL2'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ArbitrumL2MessageStatus from 'ui/shared/statusTag/ArbitrumL2MessageStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/messages/ArbitrumL2MessagesTableItem.tsx b/ui/messages/ArbitrumL2MessagesTableItem.tsx index 9e7b78c99d..f81492050f 100644 --- a/ui/messages/ArbitrumL2MessagesTableItem.tsx +++ b/ui/messages/ArbitrumL2MessagesTableItem.tsx @@ -5,14 +5,16 @@ import type { ArbitrumL2MessagesItem } from 'types/api/arbitrumL2'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import BlockEntityL1 from 'client/features/rollup/common/components/BlockEntityL1'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import ArbitrumL2MessageStatus from 'ui/shared/statusTag/ArbitrumL2MessageStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/mudWorlds/MudWorldsListItem.tsx b/ui/mudWorlds/MudWorldsListItem.tsx index 55ceafdb75..155557b435 100644 --- a/ui/mudWorlds/MudWorldsListItem.tsx +++ b/ui/mudWorlds/MudWorldsListItem.tsx @@ -3,9 +3,11 @@ import React from 'react'; import type { MudWorldItem } from 'types/api/mudWorlds'; -import { currencyUnits } from 'lib/units'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + +import { currencyUnits } from 'client/shared/chain/units'; + import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; diff --git a/ui/mudWorlds/MudWorldsTable.tsx b/ui/mudWorlds/MudWorldsTable.tsx index 4b4d0367d4..d92ba91ab5 100644 --- a/ui/mudWorlds/MudWorldsTable.tsx +++ b/ui/mudWorlds/MudWorldsTable.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type { MudWorldItem } from 'types/api/mudWorlds'; -import { currencyUnits } from 'lib/units'; +import { currencyUnits } from 'client/shared/chain/units'; + import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import MudWorldsTableItem from './MudWorldsTableItem'; diff --git a/ui/mudWorlds/MudWorldsTableItem.tsx b/ui/mudWorlds/MudWorldsTableItem.tsx index 5a64a9c72c..139169d6f1 100644 --- a/ui/mudWorlds/MudWorldsTableItem.tsx +++ b/ui/mudWorlds/MudWorldsTableItem.tsx @@ -2,9 +2,10 @@ import React from 'react'; import type { MudWorldItem } from 'types/api/mudWorlds'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import NativeCoinValue from 'ui/shared/value/NativeCoinValue'; type Props = { item: MudWorldItem; isLoading?: boolean }; diff --git a/ui/multichain/accounts/MultichainAccounts.tsx b/ui/multichain/accounts/MultichainAccounts.tsx index de1e0bf89f..2820d1b432 100644 --- a/ui/multichain/accounts/MultichainAccounts.tsx +++ b/ui/multichain/accounts/MultichainAccounts.tsx @@ -2,12 +2,14 @@ import { Box } from '@chakra-ui/react'; import { BigNumber } from 'bignumber.js'; import React from 'react'; +import AddressesListItem from 'client/slices/address/pages/index/AddressesListItem'; +import AddressesTable from 'client/slices/address/pages/index/AddressesTable'; +import { TOP_ADDRESS } from 'client/slices/address/stubs/address'; + +import getItemIndex from 'client/shared/lists/get-item-index'; + import { MultichainProvider } from 'lib/contexts/multichain'; -import getItemIndex from 'lib/getItemIndex'; -import { TOP_ADDRESS } from 'stubs/address'; import { generateListStub } from 'stubs/utils'; -import AddressesListItem from 'ui/addresses/AddressesListItem'; -import AddressesTable from 'ui/addresses/AddressesTable'; import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/multichain/address/MultichainAddress.tsx b/ui/multichain/address/MultichainAddress.tsx index 067c0afe17..2bb339e72f 100644 --- a/ui/multichain/address/MultichainAddress.tsx +++ b/ui/multichain/address/MultichainAddress.tsx @@ -4,18 +4,21 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; -import getCheckedSummedAddress from 'lib/address/getCheckedSummedAddress'; -import useApiQuery from 'lib/api/useApiQuery'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import AddressQrCode from 'client/slices/address/pages/details/info/AddressQrCode'; +import getCheckedSummedAddress from 'client/slices/address/utils/get-checked-summed-address'; + +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import * as contract from 'lib/multichain/contract'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { ADDRESS } from 'stubs/multichain'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import { CONTRACT_TAB_IDS } from 'ui/address/contract/utils'; -import AddressQrCode from 'ui/address/details/AddressQrCode'; import ClusterChainsPopover from 'ui/multichain/components/ClusterChainsPopover'; import TextAd from 'ui/shared/ad/TextAd'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import PageTitle from 'ui/shared/Page/PageTitle'; diff --git a/ui/multichain/address/MultichainAddressCoinBalanceHistory.tsx b/ui/multichain/address/MultichainAddressCoinBalanceHistory.tsx index bf02f49542..a5c3b9f44a 100644 --- a/ui/multichain/address/MultichainAddressCoinBalanceHistory.tsx +++ b/ui/multichain/address/MultichainAddressCoinBalanceHistory.tsx @@ -2,12 +2,14 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import getSocketUrl from 'client/api/get-socket-url'; +import { SocketProvider } from 'client/api/socket/context'; + +import AddressCoinBalance from 'client/slices/address/pages/details/coin-balance/AddressCoinBalance'; + import multichainConfig from 'configs/multichain'; -import getSocketUrl from 'lib/api/getSocketUrl'; import { MultichainProvider } from 'lib/contexts/multichain'; import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import { SocketProvider } from 'lib/socket/context'; -import AddressCoinBalance from 'ui/address/AddressCoinBalance'; import ChainSelect from 'ui/multichain/components/ChainSelect'; import getAvailableChainIds from './getAvailableChainIds'; diff --git a/ui/multichain/address/MultichainAddressContract.tsx b/ui/multichain/address/MultichainAddressContract.tsx index 673f40e339..d72a21a684 100644 --- a/ui/multichain/address/MultichainAddressContract.tsx +++ b/ui/multichain/address/MultichainAddressContract.tsx @@ -2,14 +2,17 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import getSocketUrl from 'client/api/get-socket-url'; +import useApiQuery from 'client/api/hooks/useApiQuery'; +import { SocketProvider } from 'client/api/socket/context'; + +import { ADDRESS_INFO } from 'client/slices/address/stubs/address'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import multichainConfig from 'configs/multichain'; -import getSocketUrl from 'lib/api/getSocketUrl'; -import useApiQuery from 'lib/api/useApiQuery'; import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import { SocketProvider } from 'lib/socket/context'; -import { ADDRESS_INFO } from 'stubs/address'; import AddressContract from 'ui/address/AddressContract'; import ChainSelect from 'ui/multichain/components/ChainSelect'; diff --git a/ui/multichain/address/MultichainAddressInternalTxs.tsx b/ui/multichain/address/MultichainAddressInternalTxs.tsx index b585068246..1e7bb4ca40 100644 --- a/ui/multichain/address/MultichainAddressInternalTxs.tsx +++ b/ui/multichain/address/MultichainAddressInternalTxs.tsx @@ -3,11 +3,13 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import useAddressInternalTxsQuery from 'client/slices/address/pages/details/internal-txs/useAddressInternalTxsQuery'; +import AddressTxsFilter from 'client/slices/address/pages/details/txs/AddressTxsFilter'; + +import CsvExport from 'client/features/csv-export/components/CsvExport'; + import multichainConfig from 'configs/multichain'; import { MultichainProvider } from 'lib/contexts/multichain'; -import AddressCsvExportLink from 'ui/address/AddressCsvExportLink'; -import AddressTxsFilter from 'ui/address/AddressTxsFilter'; -import useAddressInternalTxsQuery from 'ui/address/useAddressInternalTxsQuery'; import InternalTxsList from 'ui/internalTxs/InternalTxsList'; import InternalTxsTable from 'ui/internalTxs/InternalTxsTable'; import ChainSelect from 'ui/multichain/components/ChainSelect'; @@ -67,14 +69,19 @@ const MultichainAddressInternalTxs = ({ addressData, isLoading }: Props) => { chainIds={ chainIds } ml={ 2 } /> - - + ); diff --git a/ui/multichain/address/MultichainAddressLogs.tsx b/ui/multichain/address/MultichainAddressLogs.tsx index 0f4616a571..200e31850b 100644 --- a/ui/multichain/address/MultichainAddressLogs.tsx +++ b/ui/multichain/address/MultichainAddressLogs.tsx @@ -3,13 +3,16 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import { LOG } from 'client/slices/log/stubs/log'; + +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { LOG } from 'stubs/log'; import { generateListStub } from 'stubs/utils'; -import AddressCsvExportLink from 'ui/address/AddressCsvExportLink'; import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; @@ -61,15 +64,16 @@ const MultichainAddressLogs = ({ addressData, isLoading }: Props) => { mode={ isMobile ? 'compact' : 'default' } /> { (data?.items.length ?? 0) > 0 && ( - ) } - + ); diff --git a/ui/multichain/address/MultichainAddressPortfolio.tsx b/ui/multichain/address/MultichainAddressPortfolio.tsx index d7f1574b70..06b11029e8 100644 --- a/ui/multichain/address/MultichainAddressPortfolio.tsx +++ b/ui/multichain/address/MultichainAddressPortfolio.tsx @@ -5,15 +5,17 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import AddressCollections from 'client/slices/token/pages/address/AddressCollections'; +import AddressNftDisplayTypeRadio from 'client/slices/token/pages/address/AddressNftDisplayTypeRadio'; +import AddressNFTs from 'client/slices/token/pages/address/AddressNFTs'; +import AddressNftTypeFilter from 'client/slices/token/pages/address/AddressNftTypeFilter'; +import useAddressNftQuery from 'client/slices/token/pages/address/useAddressNftQuery'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import AddressCollections from 'ui/address/tokens/AddressCollections'; -import AddressNftDisplayTypeRadio from 'ui/address/tokens/AddressNftDisplayTypeRadio'; -import AddressNFTs from 'ui/address/tokens/AddressNFTs'; -import AddressNftTypeFilter from 'ui/address/tokens/AddressNftTypeFilter'; -import useAddressNftQuery from 'ui/address/tokens/useAddressNftQuery'; import ChainSelect from 'ui/multichain/components/ChainSelect'; import Pagination from 'ui/shared/pagination/Pagination'; diff --git a/ui/multichain/address/MultichainAddressTokenTransfers.tsx b/ui/multichain/address/MultichainAddressTokenTransfers.tsx index cbfea18947..6596ba36e7 100644 --- a/ui/multichain/address/MultichainAddressTokenTransfers.tsx +++ b/ui/multichain/address/MultichainAddressTokenTransfers.tsx @@ -5,19 +5,23 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import getSocketUrl from 'client/api/get-socket-url'; +import { SocketProvider } from 'client/api/socket/context'; + +import useAddressCountersQuery from 'client/slices/address/hooks/useAddressCountersQuery'; +import AddressTokenTransfersLocal from 'client/slices/address/pages/details/token-transfers/AddressTokenTransfersLocal'; +import useAddressTokenTransfersQuery from 'client/slices/address/pages/details/token-transfers/useAddressTokenTransfersQuery'; +import AddressAdvancedFilterLink from 'client/slices/address/pages/details/txs/AddressAdvancedFilterLink'; + +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import getSocketUrl from 'lib/api/getSocketUrl'; import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { SocketProvider } from 'lib/socket/context'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import AddressAdvancedFilterLink from 'ui/address/AddressAdvancedFilterLink'; -import AddressCsvExportLink from 'ui/address/AddressCsvExportLink'; -import AddressTokenTransfersLocal from 'ui/address/AddressTokenTransfersLocal'; -import useAddressTokenTransfersQuery from 'ui/address/useAddressTokenTransfersQuery'; -import useAddressCountersQuery from 'ui/address/utils/useAddressCountersQuery'; import ChainSelect from 'ui/multichain/components/ChainSelect'; import Pagination from 'ui/shared/pagination/Pagination'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; @@ -30,6 +34,7 @@ export const ADDRESS_MULTICHAIN_TOKEN_TRANSFERS_TAB_IDS = [ 'token_transfers_cro const TABS_RIGHT_SLOT_PROPS = { display: 'flex', justifyContent: { base: 'flex-end', lg: 'space-between' }, + alignItems: 'center', ml: { base: 0, lg: 8 }, widthAllocation: 'available' as const, }; @@ -132,9 +137,17 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { chainConfig={ chainData?.app_config } /> { chainSelect } - { countersText } - - + { directionFilter={ transfersQueryLocal.filters.filter } chainData={ chainData } /> - - + { countersText } + ); } diff --git a/ui/multichain/address/MultichainAddressTxs.pw.tsx b/ui/multichain/address/MultichainAddressTxs.pw.tsx index 52bd3a3318..1bf14002fd 100644 --- a/ui/multichain/address/MultichainAddressTxs.pw.tsx +++ b/ui/multichain/address/MultichainAddressTxs.pw.tsx @@ -2,10 +2,11 @@ import { Box } from '@chakra-ui/react'; import type { Locator } from '@playwright/test'; import React from 'react'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import * as countersMock from 'mocks/address/counters'; import * as addressMock from 'mocks/multichain/address'; import * as chainDataMock from 'mocks/multichain/chains'; -import * as txMock from 'mocks/txs/tx'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/address/MultichainAddressTxs.tsx b/ui/multichain/address/MultichainAddressTxs.tsx index 863bea9845..19ec7e9757 100644 --- a/ui/multichain/address/MultichainAddressTxs.tsx +++ b/ui/multichain/address/MultichainAddressTxs.tsx @@ -5,22 +5,26 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import getSocketUrl from 'client/api/get-socket-url'; +import { SocketProvider } from 'client/api/socket/context'; + +import useAddressCountersQuery from 'client/slices/address/hooks/useAddressCountersQuery'; +import AddressTxsFilter from 'client/slices/address/pages/details/txs/AddressTxsFilter'; +import useAddressTxsQuery from 'client/slices/address/pages/details/txs/useAddressTxsQuery'; +import TxsWithApiSorting from 'client/slices/tx/pages/index/list/TxsWithApiSorting'; + +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import getSocketUrl from 'lib/api/getSocketUrl'; import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { SocketProvider } from 'lib/socket/context'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import AddressCsvExportLink from 'ui/address/AddressCsvExportLink'; -import AddressTxsFilter from 'ui/address/AddressTxsFilter'; -import useAddressTxsQuery from 'ui/address/useAddressTxsQuery'; -import useAddressCountersQuery from 'ui/address/utils/useAddressCountersQuery'; import ChainSelect from 'ui/multichain/components/ChainSelect'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import Pagination from 'ui/shared/pagination/Pagination'; -import TxsWithAPISorting from 'ui/txs/TxsWithAPISorting'; import ListCounterText from '../components/ListCounterText'; import getAvailableChainIds from './getAvailableChainIds'; @@ -35,6 +39,7 @@ const TAB_LIST_PROPS = { const TABS_RIGHT_SLOT_PROPS = { display: 'flex', justifyContent: { base: 'flex-end', lg: 'space-between' }, + alignItems: 'center', ml: { base: 0, lg: 8 }, widthAllocation: 'available' as const, }; @@ -119,17 +124,20 @@ const MultichainAddressTxs = ({ addressData, isLoading }: Props) => { { txsLocalFilter } { chainSelect } - { countersText } - - - - + { countersText } + ); } @@ -150,7 +158,7 @@ const MultichainAddressTxs = ({ addressData, isLoading }: Props) => { { isMobile && countersText } - ; diff --git a/ui/multichain/block/MultichainBlock.tsx b/ui/multichain/block/MultichainBlock.tsx index c6d2d1ff32..af4efe5e93 100644 --- a/ui/multichain/block/MultichainBlock.tsx +++ b/ui/multichain/block/MultichainBlock.tsx @@ -1,15 +1,17 @@ import { useRouter } from 'next/router'; import React from 'react'; +import Block from 'client/slices/block/pages/details/Block'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; import { MultichainProvider } from 'lib/contexts/multichain'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import Block from 'ui/pages/Block'; const MultichainBlock = () => { const router = useRouter(); - const chainSlug = getQueryParamString(router.query.chain_slug); - const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlug); + const chainSlugOrId = getQueryParamString(router.query.chain_slug_or_id); + const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlugOrId || chain.id === chainSlugOrId); return ( diff --git a/ui/multichain/blocks/MultichainBlocks.tsx b/ui/multichain/blocks/MultichainBlocks.tsx index 35a149767a..f9ac330c24 100644 --- a/ui/multichain/blocks/MultichainBlocks.tsx +++ b/ui/multichain/blocks/MultichainBlocks.tsx @@ -6,10 +6,12 @@ import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; import { route } from 'nextjs/routes'; +import { BLOCK } from 'client/slices/block/stubs/block'; + +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { BLOCK } from 'stubs/block'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; diff --git a/ui/multichain/blocks/MultichainBlocksContent.tsx b/ui/multichain/blocks/MultichainBlocksContent.tsx index c03012aebf..87f63e0308 100644 --- a/ui/multichain/blocks/MultichainBlocksContent.tsx +++ b/ui/multichain/blocks/MultichainBlocksContent.tsx @@ -1,11 +1,13 @@ import React from 'react'; +import getSocketUrl from 'client/api/get-socket-url'; +import { SocketProvider } from 'client/api/socket/context'; + +import type { Props as BlocksContentProps } from 'client/slices/block/pages/index/BlocksContent'; +import BlocksContent from 'client/slices/block/pages/index/BlocksContent'; + import multichainConfig from 'configs/multichain'; -import getSocketUrl from 'lib/api/getSocketUrl'; import { MultichainProvider } from 'lib/contexts/multichain'; -import { SocketProvider } from 'lib/socket/context'; -import type { Props as BlocksContentProps } from 'ui/blocks/BlocksContent'; -import BlocksContent from 'ui/blocks/BlocksContent'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; interface Props extends BlocksContentProps { diff --git a/ui/multichain/components/ListCounterText.tsx b/ui/multichain/components/ListCounterText.tsx index bff3bd37e5..7b615ec1c9 100644 --- a/ui/multichain/components/ListCounterText.tsx +++ b/ui/multichain/components/ListCounterText.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; + import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; interface Props { @@ -25,7 +26,7 @@ const ListCounterText = ({ isLoading, value, type }: Props) => { loading={ isInitialLoading } textStyle={{ base: 'md', lg: 'sm' }} color="text.secondary" - ml={{ base: 0, lg: 4 }} + ml={{ base: 0, lg: 6 }} mr={{ base: 0, lg: 8 }} mb={{ base: 4, lg: 0 }} /> diff --git a/ui/multichain/ecosystems/MultichainEcosystems.tsx b/ui/multichain/ecosystems/MultichainEcosystems.tsx index 8a2f584866..2216109388 100644 --- a/ui/multichain/ecosystems/MultichainEcosystems.tsx +++ b/ui/multichain/ecosystems/MultichainEcosystems.tsx @@ -4,8 +4,9 @@ import React from 'react'; import type { ChainMetricsSorting, ChainMetricsSortingField, ChainMetricsSortingValue } from 'types/client/multichainAggregator'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import multichainConfig from 'configs/multichain'; -import useApiQuery from 'lib/api/useApiQuery'; import { CHAIN_METRICS } from 'stubs/multichain'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/multichain/ecosystems/MultichainEcosystemsListItem.tsx b/ui/multichain/ecosystems/MultichainEcosystemsListItem.tsx index 91f093171d..95ce4df4a1 100644 --- a/ui/multichain/ecosystems/MultichainEcosystemsListItem.tsx +++ b/ui/multichain/ecosystems/MultichainEcosystemsListItem.tsx @@ -4,11 +4,8 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { ClusterChainConfig } from 'types/multichain'; -import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; -import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import ChainIcon from 'ui/shared/externalChains/ChainIcon'; +import ChainSnippetList from 'ui/shared/externalChains/ChainSnippetList'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; interface Props { @@ -35,23 +32,7 @@ const MultichainEcosystemsListItem = ({ data, chainInfo, isLoading }: Props) => return ( - - - - - - - - - { data.chain_id } - - - + { chainInfo && } Active addresses diff --git a/ui/multichain/ecosystems/MultichainEcosystemsTableItem.tsx b/ui/multichain/ecosystems/MultichainEcosystemsTableItem.tsx index 99bb0b7f7f..6b6817363e 100644 --- a/ui/multichain/ecosystems/MultichainEcosystemsTableItem.tsx +++ b/ui/multichain/ecosystems/MultichainEcosystemsTableItem.tsx @@ -4,17 +4,15 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { ClusterChainConfig } from 'types/multichain'; -import useAddChainClick from 'lib/web3/useAddChainClick'; -import useProvider from 'lib/web3/useProvider'; -import { WALLETS_INFO } from 'lib/web3/wallets'; +import useAddChainClick from 'client/shared/web3/useAddChainClick'; +import useProvider from 'client/shared/web3/useProvider'; +import { WALLETS_INFO } from 'client/shared/web3/wallets'; + import { IconButton } from 'toolkit/chakra/icon-button'; -import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { Tooltip } from 'toolkit/chakra/tooltip'; -import { TruncatedText } from 'toolkit/components/truncation/TruncatedText'; -import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import ChainIcon from 'ui/shared/externalChains/ChainIcon'; +import ChainSnippetList from 'ui/shared/externalChains/ChainSnippetList'; import IconSvg from 'ui/shared/IconSvg'; interface Props { @@ -48,24 +46,7 @@ const MultichainEcosystemsTableItem = ({ data, isLoading, chainInfo }: Props) => return ( - - - - - - - - - { data.chain_id } - - - + { chainInfo && } diff --git a/ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png b/ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png index c6a56f1c28..7e10d5ce5e 100644 Binary files a/ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png and b/ui/multichain/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/multichain/home/ChainIndicators.tsx b/ui/multichain/home/ChainIndicators.tsx index 2293a7192b..451be54a24 100644 --- a/ui/multichain/home/ChainIndicators.tsx +++ b/ui/multichain/home/ChainIndicators.tsx @@ -4,7 +4,8 @@ import React from 'react'; import type { HomeStats } from 'types/api/stats'; import type { TChainIndicator } from 'ui/home/indicators/types'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { HOMEPAGE_STATS } from 'stubs/multichain'; import ChainIndicatorsChart from 'ui/home/indicators/ChainIndicatorsChart'; import ChainIndicatorsContainer from 'ui/home/indicators/ChainIndicatorsContainer'; diff --git a/ui/multichain/home/ChainWidget.tsx b/ui/multichain/home/ChainWidget.tsx index acb5ecd7b0..4946ac246c 100644 --- a/ui/multichain/home/ChainWidget.tsx +++ b/ui/multichain/home/ChainWidget.tsx @@ -4,10 +4,11 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { ClusterChainConfig } from 'types/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import useAddChainClick from 'lib/web3/useAddChainClick'; -import useProvider from 'lib/web3/useProvider'; -import { WALLETS_INFO } from 'lib/web3/wallets'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import useAddChainClick from 'client/shared/web3/useAddChainClick'; +import useProvider from 'client/shared/web3/useProvider'; +import { WALLETS_INFO } from 'client/shared/web3/wallets'; + import { Heading } from 'toolkit/chakra/heading'; import { IconButton } from 'toolkit/chakra/icon-button'; import { LinkBox, LinkOverlay } from 'toolkit/chakra/link'; diff --git a/ui/multichain/home/LatestTxs.tsx b/ui/multichain/home/LatestTxs.tsx index 2afbbf875e..5cd98dbcab 100644 --- a/ui/multichain/home/LatestTxs.tsx +++ b/ui/multichain/home/LatestTxs.tsx @@ -1,9 +1,10 @@ import { useRouter } from 'next/router'; import React from 'react'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { MultichainProvider } from 'lib/contexts/multichain'; import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import ChainSelect from 'ui/multichain/components/ChainSelect'; diff --git a/ui/multichain/home/LatestTxsLocal.tsx b/ui/multichain/home/LatestTxsLocal.tsx index 117628f708..fa85a8aec7 100644 --- a/ui/multichain/home/LatestTxsLocal.tsx +++ b/ui/multichain/home/LatestTxsLocal.tsx @@ -5,13 +5,15 @@ import type { PaginationParams } from 'ui/shared/pagination/types'; import { route } from 'nextjs/routes'; -import getSocketUrl from 'lib/api/getSocketUrl'; -import useApiQuery from 'lib/api/useApiQuery'; +import getSocketUrl from 'client/api/get-socket-url'; +import useApiQuery from 'client/api/hooks/useApiQuery'; +import { SocketProvider } from 'client/api/socket/context'; + +import TxsContent from 'client/slices/tx/pages/index/list/TxsContent'; +import { TX } from 'client/slices/tx/stubs/tx'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import { SocketProvider } from 'lib/socket/context'; -import { TX } from 'stubs/tx'; import { Link } from 'toolkit/chakra/link'; -import TxsContent from 'ui/txs/TxsContent'; const PAGINATION_PARAMS: PaginationParams = { page: 1, diff --git a/ui/multichain/home/MultichainHome.pw.tsx b/ui/multichain/home/MultichainHome.pw.tsx index 50be6e2450..57cd4f8652 100644 --- a/ui/multichain/home/MultichainHome.pw.tsx +++ b/ui/multichain/home/MultichainHome.pw.tsx @@ -1,10 +1,11 @@ import React from 'react'; +import * as txMock from 'client/slices/tx/mocks/tx'; + import * as chainDataMock from 'mocks/multichain/chains'; import * as metricsMock from 'mocks/multichain/metrics'; import * as statsMock from 'mocks/multichain/stats'; import * as chainStatsMock from 'mocks/stats/index'; -import * as txMock from 'mocks/txs/tx'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { test, expect } from 'playwright/lib'; import * as pwConfig from 'playwright/utils/config'; diff --git a/ui/multichain/home/MultichainHome.tsx b/ui/multichain/home/MultichainHome.tsx index eb54e2b5b5..6335ba7d72 100644 --- a/ui/multichain/home/MultichainHome.tsx +++ b/ui/multichain/home/MultichainHome.tsx @@ -3,8 +3,9 @@ import React from 'react'; import { route } from 'nextjs-routes'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import multichainConfig from 'configs/multichain'; -import useApiQuery from 'lib/api/useApiQuery'; import { MultichainProvider } from 'lib/contexts/multichain'; import { Link } from 'toolkit/chakra/link'; import HeroBanner from 'ui/home/HeroBanner'; diff --git a/ui/multichain/home/Stats.tsx b/ui/multichain/home/Stats.tsx index a207bdbdf0..2ed74a1c31 100644 --- a/ui/multichain/home/Stats.tsx +++ b/ui/multichain/home/Stats.tsx @@ -1,9 +1,10 @@ import { Flex } from '@chakra-ui/react'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { HOMEPAGE_STATS } from 'stubs/multichain'; -import type { HomeStatsItem } from 'ui/home/utils'; +import type { HomeStatsWidgetItem } from 'ui/home/utils'; import { sortHomeStatsItems, isHomeStatsItemEnabled } from 'ui/home/utils'; import StatsWidget from 'ui/shared/stats/StatsWidget'; @@ -17,7 +18,7 @@ const Stats = () => { }, }); - const items: Array = React.useMemo(() => { + const items: Array = React.useMemo(() => { return [ statsQuery.data?.total_multichain_txns && { id: 'total_txs' as const, diff --git a/ui/multichain/home/useChartDataQuery.ts b/ui/multichain/home/useChartDataQuery.ts index fe89ccc44a..f5c1785abe 100644 --- a/ui/multichain/home/useChartDataQuery.ts +++ b/ui/multichain/home/useChartDataQuery.ts @@ -2,7 +2,8 @@ import { useQuery } from '@tanstack/react-query'; import type { ChartMarketResponse } from 'types/api/charts'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { HOMEPAGE_STATS } from 'stubs/multichain'; import { getChartData } from 'ui/home/indicators/utils/chart'; diff --git a/ui/multichain/home/useFetchParentChainApi.tsx b/ui/multichain/home/useFetchParentChainApi.tsx index 85691fe4bf..afcc3e7b6b 100644 --- a/ui/multichain/home/useFetchParentChainApi.tsx +++ b/ui/multichain/home/useFetchParentChainApi.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import useFetch from 'lib/hooks/useFetch'; +import useFetch from 'client/api/hooks/useFetch'; interface Params { path: string; diff --git a/ui/multichain/internalTxs/MultichainInternalTxs.tsx b/ui/multichain/internalTxs/MultichainInternalTxs.tsx index 5b82365317..8172a33c79 100644 --- a/ui/multichain/internalTxs/MultichainInternalTxs.tsx +++ b/ui/multichain/internalTxs/MultichainInternalTxs.tsx @@ -1,8 +1,9 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import InternalTxsList from 'ui/internalTxs/InternalTxsList'; import InternalTxsTable from 'ui/internalTxs/InternalTxsTable'; diff --git a/ui/multichain/searchResults/SearchResults.tsx b/ui/multichain/searchResults/SearchResults.tsx index 0e42a1d756..3e78382da1 100644 --- a/ui/multichain/searchResults/SearchResults.tsx +++ b/ui/multichain/searchResults/SearchResults.tsx @@ -1,7 +1,8 @@ import { chakra } from '@chakra-ui/react'; import React from 'react'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; diff --git a/ui/multichain/searchResults/SearchResultsList.tsx b/ui/multichain/searchResults/SearchResultsList.tsx index 648bea02cf..dea154848f 100644 --- a/ui/multichain/searchResults/SearchResultsList.tsx +++ b/ui/multichain/searchResults/SearchResultsList.tsx @@ -4,8 +4,9 @@ import { useInView } from 'react-intersection-observer'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import multichainConfig from 'configs/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { ContentLoader } from 'toolkit/components/loaders/ContentLoader'; import SearchResultItemAddress from './items/SearchResultItemAddress'; diff --git a/ui/multichain/searchResults/items/SearchResultItemAddress.tsx b/ui/multichain/searchResults/items/SearchResultItemAddress.tsx index 29cad056ed..8e53d59da4 100644 --- a/ui/multichain/searchResults/items/SearchResultItemAddress.tsx +++ b/ui/multichain/searchResults/items/SearchResultItemAddress.tsx @@ -5,8 +5,9 @@ import type * as multichain from '@blockscout/multichain-aggregator-types'; import { route } from 'nextjs/routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import * as contract from 'lib/multichain/contract'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import SearchResultListItem from '../SearchResultListItem'; diff --git a/ui/multichain/searchResults/items/SearchResultItemBlock.tsx b/ui/multichain/searchResults/items/SearchResultItemBlock.tsx index ce63f58ac4..c7185a65b3 100644 --- a/ui/multichain/searchResults/items/SearchResultItemBlock.tsx +++ b/ui/multichain/searchResults/items/SearchResultItemBlock.tsx @@ -5,7 +5,7 @@ import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs/routes'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; import SearchResultListItem from '../SearchResultListItem'; diff --git a/ui/multichain/searchResults/items/SearchResultItemBlockNumber.tsx b/ui/multichain/searchResults/items/SearchResultItemBlockNumber.tsx index 9883f5cbe3..237f523b75 100644 --- a/ui/multichain/searchResults/items/SearchResultItemBlockNumber.tsx +++ b/ui/multichain/searchResults/items/SearchResultItemBlockNumber.tsx @@ -5,7 +5,7 @@ import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs/routes'; -import BlockEntity from 'ui/shared/entities/block/BlockEntity'; +import BlockEntity from 'client/slices/block/components/entity/BlockEntity'; import SearchResultListItem from '../SearchResultListItem'; diff --git a/ui/multichain/searchResults/items/SearchResultItemToken.tsx b/ui/multichain/searchResults/items/SearchResultItemToken.tsx index 22567e94d5..70fb2899d8 100644 --- a/ui/multichain/searchResults/items/SearchResultItemToken.tsx +++ b/ui/multichain/searchResults/items/SearchResultItemToken.tsx @@ -8,8 +8,9 @@ import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs/routes'; +import shortenString from 'client/shared/text/shorten-string'; + import * as contract from 'lib/multichain/contract'; -import shortenString from 'lib/shortenString'; import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import IconSvg from 'ui/shared/IconSvg'; diff --git a/ui/multichain/searchResults/items/SearchResultItemTx.tsx b/ui/multichain/searchResults/items/SearchResultItemTx.tsx index c8ef08c03e..adf11d4565 100644 --- a/ui/multichain/searchResults/items/SearchResultItemTx.tsx +++ b/ui/multichain/searchResults/items/SearchResultItemTx.tsx @@ -5,7 +5,7 @@ import type { ClusterChainConfig } from 'types/multichain'; import { route } from 'nextjs/routes'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; import SearchResultListItem from '../SearchResultListItem'; diff --git a/ui/multichain/searchResults/useSearchQuery.tsx b/ui/multichain/searchResults/useSearchQuery.tsx index 9fee44de34..07fbf8d55c 100644 --- a/ui/multichain/searchResults/useSearchQuery.tsx +++ b/ui/multichain/searchResults/useSearchQuery.tsx @@ -1,10 +1,11 @@ import { useRouter } from 'next/router'; import React from 'react'; -import useApiInfiniteQuery from 'lib/api/useApiInfiniteQuery'; -import useApiQuery from 'lib/api/useApiQuery'; -import useDebounce from 'lib/hooks/useDebounce'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useApiInfiniteQuery from 'client/api/hooks/useApiInfiniteQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import useDebounce from 'client/shared/hooks/useDebounce'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; interface Props { chainId: string | undefined; diff --git a/ui/multichain/searchResults/useSearchRedirect.tsx b/ui/multichain/searchResults/useSearchRedirect.tsx index 98fa976047..b4d48a1131 100644 --- a/ui/multichain/searchResults/useSearchRedirect.tsx +++ b/ui/multichain/searchResults/useSearchRedirect.tsx @@ -4,10 +4,12 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; +import type { ResourceError } from 'client/api/resources'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import removeQueryParam from 'client/shared/router/remove-query-param'; + import multichainConfig from 'configs/multichain'; -import type { ResourceError } from 'lib/api/resources'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import removeQueryParam from 'lib/router/removeQueryParam'; interface Params { checkRedirectQuery: UseQueryResult>; @@ -36,8 +38,8 @@ export default function useSearchRedirect({ checkRedirectQuery, hasSearchTerm }: const chainInfo = multichainConfig()?.chains.find((chain) => chain.id === checkRedirectQuery.data.chain_id); if (chainInfo) { router.replace({ - pathname: '/chain/[chain_slug]/block/[height_or_hash]', - query: { height_or_hash: checkRedirectQuery.data.parameter, chain_slug: chainInfo?.slug }, + pathname: '/chain/[chain_slug_or_id]/block/[height_or_hash]', + query: { height_or_hash: checkRedirectQuery.data.parameter, chain_slug_or_id: chainInfo.slug }, }); return; } @@ -51,10 +53,10 @@ export default function useSearchRedirect({ checkRedirectQuery, hasSearchTerm }: const chainInfo = multichainConfig()?.chains.find((chain) => chain.id === checkRedirectQuery.data.chain_id); if (chainInfo) { router.replace({ - pathname: '/chain/[chain_slug]/tx/[hash]', + pathname: '/chain/[chain_slug_or_id]/tx/[hash]', query: { hash: checkRedirectQuery.data.parameter, - chain_slug: chainInfo?.slug, + chain_slug_or_id: chainInfo.slug, }, }); return; diff --git a/ui/multichain/searchResults/utils.ts b/ui/multichain/searchResults/utils.ts index ea9b2e8c19..55cef222d0 100644 --- a/ui/multichain/searchResults/utils.ts +++ b/ui/multichain/searchResults/utils.ts @@ -1,4 +1,4 @@ -import type { ReturnType } from 'lib/api/useApiInfiniteQuery'; +import type { ReturnType } from 'client/api/hooks/useApiInfiniteQuery'; export interface SearchQueries { addresses: ReturnType<'multichainAggregator:search_addresses'>; diff --git a/ui/multichain/stats/MultichainStats.tsx b/ui/multichain/stats/MultichainStats.tsx index 803d30ecd0..65a6b391d6 100644 --- a/ui/multichain/stats/MultichainStats.tsx +++ b/ui/multichain/stats/MultichainStats.tsx @@ -1,15 +1,17 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; +import useChainStats from 'client/features/chain-stats/hooks/useChainStats'; +import ChainStatsCounters from 'client/features/chain-stats/pages/index/ChainStatsCounters'; +import ChainStatsFilters from 'client/features/chain-stats/pages/index/ChainStatsFilters'; +import ChainStatsSections from 'client/features/chain-stats/pages/index/ChainStatsSections'; + +import useEtherscanRedirects from 'client/shared/router/useEtherscanRedirects'; + import multichainConfig from 'configs/multichain'; import { MultichainProvider } from 'lib/contexts/multichain'; import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import useEtherscanRedirects from 'lib/router/useEtherscanRedirects'; import PageTitle from 'ui/shared/Page/PageTitle'; -import ChartsWidgetsList from 'ui/stats/ChartsWidgetsList'; -import NumberWidgetsList from 'ui/stats/NumberWidgetsList'; -import StatsFilters from 'ui/stats/StatsFilters'; -import useStats from 'ui/stats/useStats'; import ChainSelect from '../components/ChainSelect'; @@ -29,17 +31,17 @@ const MultichainStats = () => { }, [ chainSelect.value ]); const { - isPlaceholderData, + isLoading, isError, sections, - currentSection, - handleSectionChange, + sectionId, interval, - handleIntervalChange, - handleFilterChange, - displayedCharts, + onSectionChange, + onIntervalChange, + onFilterChange, + displayedSections, initialFilterQuery, - } = useStats({ chain }); + } = useChainStats({ chain }); return ( <> @@ -53,31 +55,32 @@ const MultichainStats = () => { /> - + - - + diff --git a/ui/multichain/token/MultichainToken.tsx b/ui/multichain/token/MultichainToken.tsx index 393c2f9c34..189d3113a9 100644 --- a/ui/multichain/token/MultichainToken.tsx +++ b/ui/multichain/token/MultichainToken.tsx @@ -1,17 +1,19 @@ import { useRouter } from 'next/router'; import React from 'react'; +import getSocketUrl from 'client/api/get-socket-url'; +import { SocketProvider } from 'client/api/socket/context'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import getSocketUrl from 'lib/api/getSocketUrl'; import { MultichainProvider } from 'lib/contexts/multichain'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { SocketProvider } from 'lib/socket/context'; import Token from 'ui/pages/Token'; const MultichainToken = () => { const router = useRouter(); - const chainSlug = getQueryParamString(router.query.chain_slug); - const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlug); + const chainSlugOrId = getQueryParamString(router.query.chain_slug_or_id); + const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlugOrId || chain.id === chainSlugOrId); return ( diff --git a/ui/multichain/tokenInstance/MultichainTokenInstance.tsx b/ui/multichain/tokenInstance/MultichainTokenInstance.tsx index 1561a86fa2..5eaf89cb49 100644 --- a/ui/multichain/tokenInstance/MultichainTokenInstance.tsx +++ b/ui/multichain/tokenInstance/MultichainTokenInstance.tsx @@ -1,17 +1,19 @@ import { useRouter } from 'next/router'; import React from 'react'; +import getSocketUrl from 'client/api/get-socket-url'; +import { SocketProvider } from 'client/api/socket/context'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import getSocketUrl from 'lib/api/getSocketUrl'; import { MultichainProvider } from 'lib/contexts/multichain'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { SocketProvider } from 'lib/socket/context'; import TokenInstance from 'ui/pages/TokenInstance'; const MultichainTokenInstance = () => { const router = useRouter(); - const chainSlug = getQueryParamString(router.query.chain_slug); - const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlug); + const chainSlugOrId = getQueryParamString(router.query.chain_slug_or_id); + const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlugOrId || chain.id === chainSlugOrId); return ( diff --git a/ui/multichain/tokenTransfers/MultichainTokenTransfers.tsx b/ui/multichain/tokenTransfers/MultichainTokenTransfers.tsx index b6e0d4bae6..cf4baf4d91 100644 --- a/ui/multichain/tokenTransfers/MultichainTokenTransfers.tsx +++ b/ui/multichain/tokenTransfers/MultichainTokenTransfers.tsx @@ -4,10 +4,11 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; import type { TokenType } from 'types/api/token'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import ChainSelect from 'ui/multichain/components/ChainSelect'; diff --git a/ui/multichain/tokenTransfers/MultichainTokenTransfersLocal.tsx b/ui/multichain/tokenTransfers/MultichainTokenTransfersLocal.tsx index c4e62453af..c252a073c1 100644 --- a/ui/multichain/tokenTransfers/MultichainTokenTransfersLocal.tsx +++ b/ui/multichain/tokenTransfers/MultichainTokenTransfersLocal.tsx @@ -3,8 +3,9 @@ import React from 'react'; import type { TokenType } from 'types/api/token'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import ActionBar from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; import PopoverFilter from 'ui/shared/filters/PopoverFilter'; diff --git a/ui/multichain/tokens/MultichainTokens.tsx b/ui/multichain/tokens/MultichainTokens.tsx index 2ba76313c8..4a17f97c16 100644 --- a/ui/multichain/tokens/MultichainTokens.tsx +++ b/ui/multichain/tokens/MultichainTokens.tsx @@ -4,10 +4,11 @@ import React from 'react'; import type { TokenType } from 'types/api/token'; +import useDebounce from 'client/shared/hooks/useDebounce'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; -import useDebounce from 'lib/hooks/useDebounce'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { TOKEN } from 'stubs/multichain'; import { generateListStub } from 'stubs/utils'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; diff --git a/ui/multichain/tx/MultichainTx.tsx b/ui/multichain/tx/MultichainTx.tsx index e3fca37877..d46236e38a 100644 --- a/ui/multichain/tx/MultichainTx.tsx +++ b/ui/multichain/tx/MultichainTx.tsx @@ -1,15 +1,17 @@ import { useRouter } from 'next/router'; import React from 'react'; +import Transaction from 'client/slices/tx/pages/details/Transaction'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import multichainConfig from 'configs/multichain'; import { MultichainProvider } from 'lib/contexts/multichain'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import Transaction from 'ui/pages/Transaction'; const MultichainTx = () => { const router = useRouter(); - const chainSlug = getQueryParamString(router.query.chain_slug); - const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlug); + const chainSlugOrId = getQueryParamString(router.query.chain_slug_or_id); + const chainData = multichainConfig()?.chains.find(chain => chain.slug === chainSlugOrId || chain.id === chainSlugOrId); return ( diff --git a/ui/multichain/txs/MultichainTxs.tsx b/ui/multichain/txs/MultichainTxs.tsx index da7c59a291..c02f167b8d 100644 --- a/ui/multichain/txs/MultichainTxs.tsx +++ b/ui/multichain/txs/MultichainTxs.tsx @@ -3,9 +3,10 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { MultichainProvider } from 'lib/contexts/multichain'; import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { EmptyState } from 'toolkit/chakra/empty-state'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; import ChainSelect from 'ui/multichain/components/ChainSelect'; diff --git a/ui/multichain/txs/MultichainTxsLocal.tsx b/ui/multichain/txs/MultichainTxsLocal.tsx index 1a27e45e0c..b9fd152894 100644 --- a/ui/multichain/txs/MultichainTxsLocal.tsx +++ b/ui/multichain/txs/MultichainTxsLocal.tsx @@ -1,12 +1,14 @@ import { Box } from '@chakra-ui/react'; import React from 'react'; -import getSocketUrl from 'lib/api/getSocketUrl'; +import getSocketUrl from 'client/api/get-socket-url'; +import { SocketProvider } from 'client/api/socket/context'; + +import TxsTabs, { getTabId } from 'client/slices/tx/pages/index/list/TxsTabs'; +import TxsStats from 'client/slices/tx/pages/index/stats/TxsStats'; + import { useMultichainContext } from 'lib/contexts/multichain'; -import { SocketProvider } from 'lib/socket/context'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; -import TxsStats from 'ui/txs/TxsStats'; -import TxsTabs, { getTabId } from 'ui/txs/TxsTabs'; const PARENT_TAB = 'txs_local'; export const MULTICHAIN_TXS_LOCAL_TAB_IDS = [ getTabId('validated', PARENT_TAB), getTabId('pending', PARENT_TAB), getTabId('blob_txs', PARENT_TAB) ]; diff --git a/ui/multichain/verifiedContracts/MultichainVerifiedContracts.tsx b/ui/multichain/verifiedContracts/MultichainVerifiedContracts.tsx index be24c54b64..121427ad1f 100644 --- a/ui/multichain/verifiedContracts/MultichainVerifiedContracts.tsx +++ b/ui/multichain/verifiedContracts/MultichainVerifiedContracts.tsx @@ -1,8 +1,9 @@ import { Box, createListCollection, HStack } from '@chakra-ui/react'; import React from 'react'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { MultichainProvider } from 'lib/contexts/multichain'; -import useIsMobile from 'lib/hooks/useIsMobile'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import ChainSelect from 'ui/multichain/components/ChainSelect'; import ActionBar from 'ui/shared/ActionBar'; diff --git a/ui/myProfile/MyProfileEmail.tsx b/ui/myProfile/MyProfileEmail.tsx index b71370059a..17c2715207 100644 --- a/ui/myProfile/MyProfileEmail.tsx +++ b/ui/myProfile/MyProfileEmail.tsx @@ -7,11 +7,13 @@ import { FormProvider, useForm } from 'react-hook-form'; import type { FormFields } from './types'; import type { UserInfo } from 'types/api/account'; +import useApiFetch from 'client/api/hooks/useApiFetch'; + +import * as mixpanel from 'client/shared/analytics/mixpanel'; +import getErrorMessage from 'client/shared/errors/get-error-message'; +import getErrorObjPayload from 'client/shared/errors/get-error-obj-payload'; + import config from 'configs/app'; -import useApiFetch from 'lib/api/useApiFetch'; -import getErrorMessage from 'lib/errors/getErrorMessage'; -import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; -import * as mixpanel from 'lib/mixpanel'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { toaster } from 'toolkit/chakra/toaster'; diff --git a/ui/myProfile/MyProfileWallet.tsx b/ui/myProfile/MyProfileWallet.tsx index 25815e3f52..d05fe7273a 100644 --- a/ui/myProfile/MyProfileWallet.tsx +++ b/ui/myProfile/MyProfileWallet.tsx @@ -4,11 +4,12 @@ import React from 'react'; import type { UserInfo } from 'types/api/account'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import config from 'configs/app'; import { Button } from 'toolkit/chakra/button'; import { Heading } from 'toolkit/chakra/heading'; import { Link } from 'toolkit/chakra/link'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; interface Props { profileQuery: UseQueryResult; diff --git a/ui/nameDomain/NameDomainDetails.tsx b/ui/nameDomain/NameDomainDetails.tsx index f145131816..c43edfa447 100644 --- a/ui/nameDomain/NameDomainDetails.tsx +++ b/ui/nameDomain/NameDomainDetails.tsx @@ -6,15 +6,17 @@ import * as bens from '@blockscout/bens-types'; import { route } from 'nextjs-routes'; +import type { ResourceError } from 'client/api/resources'; + +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import config from 'configs/app'; -import type { ResourceError } from 'lib/api/resources'; import { Link } from 'toolkit/chakra/link'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { Tooltip } from 'toolkit/chakra/tooltip'; import { stripTrailingSlash } from 'toolkit/utils/url'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import NftEntity from 'ui/shared/entities/nft/NftEntity'; import IconSvg from 'ui/shared/IconSvg'; import TextSeparator from 'ui/shared/TextSeparator'; diff --git a/ui/nameDomain/NameDomainHistory.tsx b/ui/nameDomain/NameDomainHistory.tsx index 71cc8e0528..f6f9a271dc 100644 --- a/ui/nameDomain/NameDomainHistory.tsx +++ b/ui/nameDomain/NameDomainHistory.tsx @@ -4,9 +4,11 @@ import React from 'react'; import type * as bens from '@blockscout/bens-types'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { ENS_DOMAIN_EVENT } from 'stubs/ENS'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/nameDomain/history/NameDomainHistoryListItem.tsx b/ui/nameDomain/history/NameDomainHistoryListItem.tsx index 8e1ddf0213..a48e0d2447 100644 --- a/ui/nameDomain/history/NameDomainHistoryListItem.tsx +++ b/ui/nameDomain/history/NameDomainHistoryListItem.tsx @@ -4,11 +4,12 @@ import type * as bens from '@blockscout/bens-types'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import config from 'configs/app'; import { Badge } from 'toolkit/chakra/badge'; import { stripTrailingSlash } from 'toolkit/utils/url'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/nameDomain/history/NameDomainHistoryTableItem.tsx b/ui/nameDomain/history/NameDomainHistoryTableItem.tsx index 9e202a83e8..a730498fa2 100644 --- a/ui/nameDomain/history/NameDomainHistoryTableItem.tsx +++ b/ui/nameDomain/history/NameDomainHistoryTableItem.tsx @@ -4,12 +4,13 @@ import type * as bens from '@blockscout/bens-types'; import { route } from 'nextjs-routes'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + import config from 'configs/app'; import { Badge } from 'toolkit/chakra/badge'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import { stripTrailingSlash } from 'toolkit/utils/url'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; interface Props { diff --git a/ui/nameServices/directories/Clusters.tsx b/ui/nameServices/directories/Clusters.tsx index b3885a2e0a..760e727a9e 100644 --- a/ui/nameServices/directories/Clusters.tsx +++ b/ui/nameServices/directories/Clusters.tsx @@ -2,6 +2,9 @@ import { Box, Text } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React, { useCallback } from 'react'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import { useQueryParams } from 'client/shared/router/useQueryParams'; + import { detectInputType } from 'lib/clusters/detectInputType'; import { shouldShowDirectoryView, @@ -15,8 +18,6 @@ import type { ViewMode } from 'lib/clusters/pageUtils'; import { useClusterPagination } from 'lib/clusters/useClusterPagination'; import { useClustersData } from 'lib/clusters/useClustersData'; import { useClusterSearch } from 'lib/clusters/useClusterSearch'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { useQueryParams } from 'lib/router/useQueryParams'; import { Link } from 'toolkit/chakra/link'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import DataListDisplay from 'ui/shared/DataListDisplay'; diff --git a/ui/nameServices/directories/ClustersActionBar.tsx b/ui/nameServices/directories/ClustersActionBar.tsx index 1e2076b3a7..d1efa909a2 100644 --- a/ui/nameServices/directories/ClustersActionBar.tsx +++ b/ui/nameServices/directories/ClustersActionBar.tsx @@ -3,11 +3,12 @@ import React from 'react'; import type { PaginationParams } from 'ui/shared/pagination/types'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; + import { getSearchPlaceholder, shouldShowActionBar, } from 'lib/clusters/actionBarUtils'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import { Button, ButtonGroupRadio } from 'toolkit/chakra/button'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import ActionBar from 'ui/shared/ActionBar'; diff --git a/ui/nameServices/directories/ClustersDirectoryListItem.tsx b/ui/nameServices/directories/ClustersDirectoryListItem.tsx index 44041c5683..eb59bf9a70 100644 --- a/ui/nameServices/directories/ClustersDirectoryListItem.tsx +++ b/ui/nameServices/directories/ClustersDirectoryListItem.tsx @@ -2,10 +2,11 @@ import React from 'react'; import type { ClustersDirectoryObject } from 'types/api/clusters'; -import { isEvmAddress } from 'lib/address/isEvmAddress'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; + import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import Time from 'ui/shared/time/Time'; diff --git a/ui/nameServices/directories/ClustersDirectoryTable.tsx b/ui/nameServices/directories/ClustersDirectoryTable.tsx index b6b08a8adc..5333c270a1 100644 --- a/ui/nameServices/directories/ClustersDirectoryTable.tsx +++ b/ui/nameServices/directories/ClustersDirectoryTable.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type { ClustersDirectoryObject } from 'types/api/clusters'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { TableBody, TableHeaderSticky, TableRow, TableColumnHeader, TableRoot } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/nameServices/directories/ClustersDirectoryTableItem.tsx b/ui/nameServices/directories/ClustersDirectoryTableItem.tsx index 552c25b877..89c963527c 100644 --- a/ui/nameServices/directories/ClustersDirectoryTableItem.tsx +++ b/ui/nameServices/directories/ClustersDirectoryTableItem.tsx @@ -2,10 +2,11 @@ import React from 'react'; import type { ClustersDirectoryObject } from 'types/api/clusters'; -import { isEvmAddress } from 'lib/address/isEvmAddress'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; +import { isEvmAddress } from 'client/slices/address/utils/is-evm-address'; + import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import ClustersEntity from 'ui/shared/entities/clusters/ClustersEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/nameServices/domains/NameDomains.tsx b/ui/nameServices/domains/NameDomains.tsx index 6a37e8f5c1..8664947582 100644 --- a/ui/nameServices/domains/NameDomains.tsx +++ b/ui/nameServices/domains/NameDomains.tsx @@ -4,10 +4,12 @@ import React from 'react'; import type { EnsDomainLookupFiltersOptions, EnsLookupSorting } from 'types/api/ens'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import useDebounce from 'client/shared/hooks/useDebounce'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; -import useDebounce from 'lib/hooks/useDebounce'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { ENS_DOMAIN } from 'stubs/ENS'; import { generateListStub } from 'stubs/utils'; import { ADDRESS_REGEXP } from 'toolkit/utils/regexp'; diff --git a/ui/nameServices/domains/NameDomainsActionBar.tsx b/ui/nameServices/domains/NameDomainsActionBar.tsx index f1cc73730b..5fad83c2b8 100644 --- a/ui/nameServices/domains/NameDomainsActionBar.tsx +++ b/ui/nameServices/domains/NameDomainsActionBar.tsx @@ -5,7 +5,8 @@ import type * as bens from '@blockscout/bens-types'; import type { EnsDomainLookupFiltersOptions } from 'types/api/ens'; import type { PaginationParams } from 'ui/shared/pagination/types'; -import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; +import useIsInitialLoading from 'client/shared/hooks/useIsInitialLoading'; + import { Button } from 'toolkit/chakra/button'; import { Checkbox, CheckboxGroup } from 'toolkit/chakra/checkbox'; import { Image } from 'toolkit/chakra/image'; diff --git a/ui/nameServices/domains/NameDomainsListItem.tsx b/ui/nameServices/domains/NameDomainsListItem.tsx index e8bf613d3e..5fa11fa691 100644 --- a/ui/nameServices/domains/NameDomainsListItem.tsx +++ b/ui/nameServices/domains/NameDomainsListItem.tsx @@ -2,10 +2,11 @@ import React from 'react'; import type * as bens from '@blockscout/bens-types'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import dayjs from 'lib/date/dayjs'; import { Skeleton } from 'toolkit/chakra/skeleton'; import NameDomainExpiryStatus from 'ui/nameDomain/NameDomainExpiryStatus'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import Time from 'ui/shared/time/Time'; diff --git a/ui/nameServices/domains/NameDomainsTable.tsx b/ui/nameServices/domains/NameDomainsTable.tsx index 76f42df859..38d2627cbd 100644 --- a/ui/nameServices/domains/NameDomainsTable.tsx +++ b/ui/nameServices/domains/NameDomainsTable.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type * as bens from '@blockscout/bens-types'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/nameServices/domains/NameDomainsTableItem.tsx b/ui/nameServices/domains/NameDomainsTableItem.tsx index 7bd37916cd..bb2d524d0d 100644 --- a/ui/nameServices/domains/NameDomainsTableItem.tsx +++ b/ui/nameServices/domains/NameDomainsTableItem.tsx @@ -2,9 +2,10 @@ import React from 'react'; import type * as bens from '@blockscout/bens-types'; +import AddressEntity from 'client/slices/address/components/entity/AddressEntity'; + import dayjs from 'lib/date/dayjs'; import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import EnsEntity from 'ui/shared/entities/ens/EnsEntity'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/operation/tac/TacOperationDetails.tsx b/ui/operation/tac/TacOperationDetails.tsx index e33953894e..ca45917dc3 100644 --- a/ui/operation/tac/TacOperationDetails.tsx +++ b/ui/operation/tac/TacOperationDetails.tsx @@ -2,10 +2,11 @@ import React from 'react'; import type * as tac from '@blockscout/tac-operation-lifecycle-types'; +import AddressEntityTacTon from 'client/features/chain-variants/tac/components/AddressEntityTacTon'; + import { sortStatusHistory } from 'lib/operations/tac'; import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo'; import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import AddressEntityTacTon from 'ui/shared/entities/address/AddressEntityTacTon'; import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus'; import TacOperationLifecycleAccordion from './TacOperationLifecycleAccordion'; diff --git a/ui/operation/tac/TacOperationLifecycleAccordionItemContent.tsx b/ui/operation/tac/TacOperationLifecycleAccordionItemContent.tsx index 4369cea239..ad220945de 100644 --- a/ui/operation/tac/TacOperationLifecycleAccordionItemContent.tsx +++ b/ui/operation/tac/TacOperationLifecycleAccordionItemContent.tsx @@ -3,9 +3,11 @@ import React from 'react'; import * as tac from '@blockscout/tac-operation-lifecycle-types'; +import TxEntity from 'client/slices/tx/components/entity/TxEntity'; + +import TxEntityTon from 'client/features/chain-variants/tac/components/TxEntityTon'; + import DetailedInfoTimestamp from 'ui/shared/DetailedInfo/DetailedInfoTimestamp'; -import TxEntity from 'ui/shared/entities/tx/TxEntity'; -import TxEntityTon from 'ui/shared/entities/tx/TxEntityTon'; import { ItemContent, ItemBody, ItemRow } from 'ui/shared/lifecycle/LifecycleAccordion'; import StatusTag from 'ui/shared/statusTag/StatusTag'; diff --git a/ui/operations/tac/TacOperationsListItem.tsx b/ui/operations/tac/TacOperationsListItem.tsx index 2ec3a38dec..84f1471306 100644 --- a/ui/operations/tac/TacOperationsListItem.tsx +++ b/ui/operations/tac/TacOperationsListItem.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type * as tac from '@blockscout/tac-operation-lifecycle-types'; -import AddressEntityTacTon from 'ui/shared/entities/address/AddressEntityTacTon'; +import AddressEntityTacTon from 'client/features/chain-variants/tac/components/AddressEntityTacTon'; + import OperationEntity from 'ui/shared/entities/operation/OperationEntity'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus'; diff --git a/ui/operations/tac/TacOperationsTable.tsx b/ui/operations/tac/TacOperationsTable.tsx index 896a961a5d..3ea8eb8adc 100644 --- a/ui/operations/tac/TacOperationsTable.tsx +++ b/ui/operations/tac/TacOperationsTable.tsx @@ -2,7 +2,8 @@ import React from 'react'; import type * as tac from '@blockscout/tac-operation-lifecycle-types'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table'; import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle'; diff --git a/ui/operations/tac/TacOperationsTableItem.tsx b/ui/operations/tac/TacOperationsTableItem.tsx index c9f7b76ca1..efd9048aae 100644 --- a/ui/operations/tac/TacOperationsTableItem.tsx +++ b/ui/operations/tac/TacOperationsTableItem.tsx @@ -2,8 +2,9 @@ import React from 'react'; import type * as tac from '@blockscout/tac-operation-lifecycle-types'; +import AddressEntityTacTon from 'client/features/chain-variants/tac/components/AddressEntityTacTon'; + import { TableCell, TableRow } from 'toolkit/chakra/table'; -import AddressEntityTacTon from 'ui/shared/entities/address/AddressEntityTacTon'; import OperationEntity from 'ui/shared/entities/operation/OperationEntity'; import TacOperationStatus from 'ui/shared/statusTag/TacOperationStatus'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsListItem.tsx b/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsListItem.tsx index 0cebf427e8..1032220d39 100644 --- a/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsListItem.tsx +++ b/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsListItem.tsx @@ -3,12 +3,13 @@ import React from 'react'; import type { OptimisticL2OutputRootsItem } from 'types/api/optimisticL2'; +import BlockEntityL2 from 'client/features/rollup/common/components/BlockEntityL2'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { layerLabels } from 'lib/rollups/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import HashStringShorten from 'ui/shared/HashStringShorten'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsTableItem.tsx b/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsTableItem.tsx index 136cf24285..08a3458146 100644 --- a/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsTableItem.tsx +++ b/ui/outputRoots/optimisticL2/OptimisticL2OutputRootsTableItem.tsx @@ -3,12 +3,13 @@ import React from 'react'; import type { OptimisticL2OutputRootsItem } from 'types/api/optimisticL2'; +import BlockEntityL2 from 'client/features/rollup/common/components/BlockEntityL2'; +import TxEntityL1 from 'client/features/rollup/common/components/TxEntityL1'; + import config from 'configs/app'; import { Skeleton } from 'toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'toolkit/chakra/table'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import BlockEntityL2 from 'ui/shared/entities/block/BlockEntityL2'; -import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import HashStringShorten from 'ui/shared/HashStringShorten'; import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip'; diff --git a/ui/pages/AccountsLabelSearch.pw.tsx b/ui/pages/AccountsLabelSearch.pw.tsx index c4846968c5..a654309ee4 100644 --- a/ui/pages/AccountsLabelSearch.pw.tsx +++ b/ui/pages/AccountsLabelSearch.pw.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { AddressesMetadataSearchResult } from 'types/api/addresses'; +import type { AddressesMetadataSearchResult } from 'client/features/address-metadata/types/api'; import * as addressMocks from 'mocks/address/address'; import { test, expect } from 'playwright/lib'; diff --git a/ui/pages/AccountsLabelSearch.tsx b/ui/pages/AccountsLabelSearch.tsx index b6c9d687cd..decd3dd3b5 100644 --- a/ui/pages/AccountsLabelSearch.tsx +++ b/ui/pages/AccountsLabelSearch.tsx @@ -4,8 +4,10 @@ import React from 'react'; import type { EntityTag as TEntityTag, EntityTagType } from 'ui/shared/EntityTags/types'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { TOP_ADDRESS } from 'stubs/address'; +import { TOP_ADDRESS } from 'client/slices/address/stubs/address'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; import AddressesLabelSearchListItem from 'ui/addressesLabelSearch/AddressesLabelSearchListItem'; diff --git a/ui/pages/AdvancedFilter.tsx b/ui/pages/AdvancedFilter.tsx index 10fec44882..268194fd04 100644 --- a/ui/pages/AdvancedFilter.tsx +++ b/ui/pages/AdvancedFilter.tsx @@ -12,14 +12,19 @@ import React from 'react'; import type { AdvancedFilterParams } from 'types/api/advancedFilter'; import { ADVANCED_FILTER_AGES, ADVANCED_FILTER_ADDRESS_RELATION } from 'types/api/advancedFilter'; -import useApiQuery from 'lib/api/useApiQuery'; -import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { AddressHighlightProvider } from 'client/slices/address/contexts/address-highlight'; + +import CsvExport from 'client/features/csv-export/components/CsvExport'; + +import getFilterValueFromQuery from 'client/shared/router/get-filter-value-from-query'; +import getFilterValuesFromQuery from 'client/shared/router/get-filter-values-from-query'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; +import getValuesArrayFromQuery from 'client/shared/router/get-values-array-from-query'; + import { useMultichainContext } from 'lib/contexts/multichain'; import dayjs from 'lib/date/dayjs'; -import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; -import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; -import getValuesArrayFromQuery from 'lib/getValuesArrayFromQuery'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { ADVANCED_FILTER_ITEM } from 'stubs/advancedFilter'; import { generateListStub } from 'stubs/utils'; import { Link } from 'toolkit/chakra/link'; @@ -28,7 +33,6 @@ import { Tag } from 'toolkit/chakra/tag'; import ColumnsButton from 'ui/advancedFilter/ColumnsButton'; import type { ColumnsIds } from 'ui/advancedFilter/constants'; import { getAdvancedFilterTypes, TABLE_COLUMNS } from 'ui/advancedFilter/constants'; -import ExportCSV from 'ui/advancedFilter/ExportCSV'; import FilterByColumn from 'ui/advancedFilter/FilterByColumn'; import ItemByColumn from 'ui/advancedFilter/ItemByColumn'; import { getDurationFromAge, getFilterTags } from 'ui/advancedFilter/lib'; @@ -230,8 +234,17 @@ const AdvancedFilter = () => { const actionBar = ( - + ); diff --git a/ui/pages/ApiKeys.tsx b/ui/pages/ApiKeys.tsx index 177fd902f1..3a0b765e77 100644 --- a/ui/pages/ApiKeys.tsx +++ b/ui/pages/ApiKeys.tsx @@ -3,8 +3,9 @@ import React, { useCallback, useState } from 'react'; import type { ApiKey } from 'types/api/account'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { API_KEY } from 'stubs/account'; import { Button } from 'toolkit/chakra/button'; import { Link } from 'toolkit/chakra/link'; @@ -60,7 +61,12 @@ const ApiKeysPage: React.FC = () => { deleteModalProps.onOpenChange({ open }); }, [ deleteModalProps ]); - const description = ( + const description = feature.isEnabled && feature.apiKeysButton === false ? ( + + Blockscout APIs require a key. Create a + free PRO API key to access all multichain endpoints. + + ) : ( Create API keys to use for your RPC and EthRPC API requests. For more information, see { space } "How to use a Blockscout API key". diff --git a/ui/pages/ArbitrumL2TxnBatch.tsx b/ui/pages/ArbitrumL2TxnBatch.tsx index 16c093d087..a747774a35 100644 --- a/ui/pages/ArbitrumL2TxnBatch.tsx +++ b/ui/pages/ArbitrumL2TxnBatch.tsx @@ -3,22 +3,24 @@ import React from 'react'; import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types'; -import throwOnAbsentParamError from 'lib/errors/throwOnAbsentParamError'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { BLOCK } from 'stubs/block'; -import { TX } from 'stubs/tx'; +import BlocksContent from 'client/slices/block/pages/index/BlocksContent'; +import { BLOCK } from 'client/slices/block/stubs/block'; +import TxsWithFrontendSorting from 'client/slices/tx/pages/index/list/TxsWithFrontendSorting'; +import { TX } from 'client/slices/tx/stubs/tx'; + +import throwOnAbsentParamError from 'client/shared/errors/throw-on-absent-param-error'; +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { generateListStub } from 'stubs/utils'; import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs'; -import BlocksContent from 'ui/blocks/BlocksContent'; import TextAd from 'ui/shared/ad/TextAd'; import PageTitle from 'ui/shared/Page/PageTitle'; import Pagination from 'ui/shared/pagination/Pagination'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import ArbitrumL2TxnBatchDetails from 'ui/txnBatches/arbitrumL2/ArbitrumL2TxnBatchDetails'; import useBatchQuery from 'ui/txnBatches/arbitrumL2/useBatchQuery'; -import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting'; const TAB_LIST_PROPS = { marginBottom: 0, diff --git a/ui/pages/ArbitrumL2TxnBatches.tsx b/ui/pages/ArbitrumL2TxnBatches.tsx index 2f9c6d1b73..58e8ee63b4 100644 --- a/ui/pages/ArbitrumL2TxnBatches.tsx +++ b/ui/pages/ArbitrumL2TxnBatches.tsx @@ -1,7 +1,8 @@ import { Box, Text } from '@chakra-ui/react'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import { ARBITRUM_L2_TXN_BATCHES_ITEM } from 'stubs/arbitrumL2'; import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/pages/ArbitrumL2TxnWithdrawals.tsx b/ui/pages/ArbitrumL2TxnWithdrawals.tsx index 97d7971c16..414dfc6ac8 100644 --- a/ui/pages/ArbitrumL2TxnWithdrawals.tsx +++ b/ui/pages/ArbitrumL2TxnWithdrawals.tsx @@ -2,9 +2,11 @@ import { Box, chakra, Text } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { layerLabels } from 'lib/rollups/utils'; -import getQueryParamString from 'lib/router/getQueryParamString'; import { ARBITRUM_L2_TXN_WITHDRAWALS_ITEM } from 'stubs/arbitrumL2'; import { FilterInput } from 'toolkit/components/filters/FilterInput'; import { FormFieldError } from 'toolkit/components/forms/components/FormFieldError'; diff --git a/ui/pages/BeaconChainDeposits.tsx b/ui/pages/BeaconChainDeposits.tsx index 320fbfe05b..e5401c527a 100644 --- a/ui/pages/BeaconChainDeposits.tsx +++ b/ui/pages/BeaconChainDeposits.tsx @@ -2,8 +2,9 @@ import { Box, Text } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; import { DEPOSIT } from 'stubs/deposits'; import { generateListStub } from 'stubs/utils'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/pages/BeaconChainWithdrawals.tsx b/ui/pages/BeaconChainWithdrawals.tsx index b3aafdad2f..d2e40d7538 100644 --- a/ui/pages/BeaconChainWithdrawals.tsx +++ b/ui/pages/BeaconChainWithdrawals.tsx @@ -2,9 +2,11 @@ import { Box, Text } from '@chakra-ui/react'; import BigNumber from 'bignumber.js'; import React from 'react'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import { currencyUnits } from 'client/shared/chain/units'; + import config from 'configs/app'; -import useApiQuery from 'lib/api/useApiQuery'; -import { currencyUnits } from 'lib/units'; import { generateListStub } from 'stubs/utils'; import { WITHDRAWAL } from 'stubs/withdrawals'; import { Skeleton } from 'toolkit/chakra/skeleton'; diff --git a/ui/pages/Blob.tsx b/ui/pages/Blob.tsx index be34e484c0..40e5fbc7ec 100644 --- a/ui/pages/Blob.tsx +++ b/ui/pages/Blob.tsx @@ -1,9 +1,11 @@ import { useRouter } from 'next/router'; import React from 'react'; -import useApiQuery from 'lib/api/useApiQuery'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import getQueryParamString from 'lib/router/getQueryParamString'; +import useApiQuery from 'client/api/hooks/useApiQuery'; + +import throwOnResourceLoadError from 'client/shared/errors/throw-on-resource-load-error'; +import getQueryParamString from 'client/shared/router/get-query-param-string'; + import { BLOB } from 'stubs/blobs'; import BlobInfo from 'ui/blob/BlobInfo'; import TextAd from 'ui/shared/ad/TextAd'; diff --git a/ui/pages/Chakra.tsx b/ui/pages/Chakra.tsx index 7fbec2ab99..82e27b40a1 100644 --- a/ui/pages/Chakra.tsx +++ b/ui/pages/Chakra.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import useIsMobile from 'lib/hooks/useIsMobile'; +import useIsMobile from 'client/shared/hooks/useIsMobile'; + import { useColorMode } from 'toolkit/chakra/color-mode'; import { Switch } from 'toolkit/chakra/switch'; import { TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs'; @@ -31,6 +32,7 @@ import RatingShowcase from 'ui/showcases/Rating'; import SelectShowcase from 'ui/showcases/Select'; import SkeletonShowcase from 'ui/showcases/Skeleton'; import SpinnerShowcase from 'ui/showcases/Spinner'; +import StatusShowcase from 'ui/showcases/Status'; import SwitchShowcase from 'ui/showcases/Switch'; import TableShowcase from 'ui/showcases/Table'; import TabsShowcase from 'ui/showcases/Tabs'; @@ -67,6 +69,7 @@ const tabs = [ { label: 'Select', value: 'select', component: }, { label: 'Skeleton', value: 'skeleton', component: }, { label: 'Spinner', value: 'spinner', component: }, + { label: 'Status', value: 'status', component: }, { label: 'Switch', value: 'switch', component: }, { label: 'Table', value: 'table', component: }, { label: 'Tabs', value: 'tabs', component: }, diff --git a/ui/pages/Chart.pw.tsx b/ui/pages/Chart.pw.tsx deleted file mode 100644 index 3997b96b5a..0000000000 --- a/ui/pages/Chart.pw.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; - -import * as statsLineMock from 'mocks/stats/line'; -import { test, expect } from 'playwright/lib'; -import { formatDate } from 'ui/shared/chart/utils'; - -import Chart from './Chart'; - -const CHART_ID = 'averageGasPrice'; - -test.beforeEach(async({ mockTextAd }) => { - await mockTextAd(); -}); - -const hooksConfig = { - router: { - query: { id: CHART_ID }, - }, -}; - -test('base view +@dark-mode +@mobile', async({ render, mockApiResponse, page }) => { - const date = new Date(); - date.setMonth(date.getMonth() - 1); - - const chartApiUrl = await mockApiResponse( - 'stats:line', - statsLineMock.averageGasPrice, - { - pathParams: { id: CHART_ID }, - queryParams: { - from: formatDate(date), - to: '2022-11-11', - resolution: 'DAY', - }, - }, - ); - - const component = await render(, { hooksConfig }); - await page.waitForResponse(chartApiUrl); - await page.waitForFunction(() => { - return document.querySelector('path[data-name="chart-fullscreen"]')?.getAttribute('opacity') === '1'; - }); - await expect(component).toHaveScreenshot(); -}); diff --git a/ui/pages/Chart.tsx b/ui/pages/Chart.tsx deleted file mode 100644 index f436245d29..0000000000 --- a/ui/pages/Chart.tsx +++ /dev/null @@ -1,298 +0,0 @@ -import { createListCollection, Flex, Text } from '@chakra-ui/react'; -import type { NextRouter } from 'next/router'; -import { useRouter } from 'next/router'; -import React from 'react'; - -import { Resolution } from '@blockscout/stats-types'; -import type { StatsIntervalIds } from 'types/client/stats'; -import { StatsIntervalId } from 'types/client/stats'; - -import config from 'configs/app'; -import { useMultichainContext } from 'lib/contexts/multichain'; -import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; -import useIsMobile from 'lib/hooks/useIsMobile'; -import * as metadata from 'lib/metadata'; -import * as mixpanel from 'lib/mixpanel/index'; -import useRoutedChainSelect from 'lib/multichain/useRoutedChainSelect'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import { Button } from 'toolkit/chakra/button'; -import type { SelectOption } from 'toolkit/chakra/select'; -import { Select } from 'toolkit/chakra/select'; -import { Skeleton } from 'toolkit/chakra/skeleton'; -import { ChartWidgetContent, useChartZoom } from 'toolkit/components/charts'; -import ChartMenu from 'toolkit/components/charts/parts/ChartMenu'; -import { isBrowser } from 'toolkit/utils/isBrowser'; -import ChainSelect from 'ui/multichain/components/ChainSelect'; -import isCustomAppError from 'ui/shared/AppError/isCustomAppError'; -import ChartIntervalSelect from 'ui/shared/chart/ChartIntervalSelect'; -import { useChartsConfig } from 'ui/shared/chart/config'; -import useChartQuery from 'ui/shared/chart/useChartQuery'; -import CopyToClipboard from 'ui/shared/CopyToClipboard'; -import IconSvg from 'ui/shared/IconSvg'; -import PageTitle from 'ui/shared/Page/PageTitle'; -import { STATS_RESOLUTIONS } from 'ui/stats/constants'; - -const DEFAULT_RESOLUTION = Resolution.DAY; - -const getIntervalByResolution = (resolution: Resolution): StatsIntervalIds => { - switch (resolution) { - case 'DAY': - return 'oneMonth'; - case 'WEEK': - return 'oneMonth'; - case 'MONTH': - return 'oneYear'; - case 'YEAR': - return 'all'; - default: - return 'oneMonth'; - } -}; - -const getIntervalFromQuery = (router: NextRouter): StatsIntervalIds | undefined => { - const intervalFromQuery = getQueryParamString(router.query.interval); - - if (!intervalFromQuery || !Object.values(StatsIntervalId).includes(intervalFromQuery as StatsIntervalIds)) { - return undefined; - } - - return intervalFromQuery as StatsIntervalIds; -}; - -const getResolutionFromQuery = (router: NextRouter) => { - const resolutionFromQuery = getQueryParamString(router.query.resolution); - - if (!resolutionFromQuery || !Resolution[resolutionFromQuery as keyof typeof Resolution]) { - return DEFAULT_RESOLUTION; - } - - return resolutionFromQuery as Resolution; -}; - -const Chart = () => { - const router = useRouter(); - const id = getQueryParamString(router.query.id); - const intervalFromQuery = getIntervalFromQuery(router); - const resolutionFromQuery = getResolutionFromQuery(router); - const defaultResolution = resolutionFromQuery || DEFAULT_RESOLUTION; - const [ intervalState, setIntervalState ] = React.useState(intervalFromQuery); - const [ resolution, setResolution ] = React.useState(defaultResolution); - const { zoomRange, handleZoom, handleZoomReset } = useChartZoom(); - - const interval = intervalState || getIntervalByResolution(resolution); - - const ref = React.useRef(null); - - const isMobile = useIsMobile(); - const isInBrowser = isBrowser(); - const chartsConfig = useChartsConfig(); - const chainSelect = useRoutedChainSelect(); - const multichainContext = useMultichainContext(); - - const onIntervalChange = React.useCallback((interval: StatsIntervalIds) => { - setIntervalState(interval); - router.push( - { - pathname: router.pathname, - query: { ...router.query, interval }, - }, - undefined, - { shallow: true }, - ); - }, [ setIntervalState, router ]); - - const onResolutionChange = React.useCallback(({ value }: { value: Array }) => { - setResolution(value[0] as Resolution); - router.push({ - pathname: router.pathname, - query: { ...router.query, resolution: value[0] }, - }, - undefined, - { shallow: true }, - ); - }, [ setResolution, router ]); - - const handleReset = React.useCallback(() => { - handleZoomReset(); - onResolutionChange({ value: [ DEFAULT_RESOLUTION ] }); - }, [ handleZoomReset, onResolutionChange ]); - - const { items, info, lineQuery } = useChartQuery(id, resolution, interval); - - const charts = React.useMemo(() => { - if (!info || !items) { - return []; - } - - return [ - { - id: info.id, - name: 'Value', - items, - charts: chartsConfig, - units: info.units, - }, - ]; - }, [ chartsConfig, info, items ]); - - const hasNonEmptyCharts = charts.some((chart) => chart.items.length > 2); - - React.useEffect(() => { - if (info && !config.meta.seo.enhancedDataEnabled) { - metadata.update({ pathname: '/stats/[id]', query: { id } }, info); - } - }, [ info, id ]); - - const onShare = React.useCallback(async() => { - mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Share chart', Info: id }); - try { - await window.navigator.share({ - title: info?.title, - text: info?.description, - url: window.location.href, - }); - } catch (error) {} - }, [ info, id ]); - - if (lineQuery.isError) { - if (isCustomAppError(lineQuery.error)) { - throwOnResourceLoadError({ resource: 'stats:line', error: lineQuery.error, isError: true }); - } - } - - const hasItems = (items && items.length > 2) || lineQuery.isPending; - - const isInfoLoading = !info && lineQuery.isPlaceholderData; - - const shareButton = ( - - ); - - const resolutionCollection = React.useMemo(() => { - const resolutions = lineQuery.data?.info?.resolutions || []; - const items = STATS_RESOLUTIONS - .filter((resolution) => resolutions.includes(resolution.id)) - .map((resolution) => ({ value: resolution.id, label: resolution.title })); - - return createListCollection({ items }); - }, [ lineQuery.data?.info?.resolutions ]); - - return ( - <> - - - - { multichainContext?.chain && ( - - ) } - - { !isMobile && Period } - - - { ( - (info?.resolutions && info?.resolutions.length > 1) || - (!info && lineQuery.data?.info?.resolutions && lineQuery.data?.info?.resolutions.length > 1) - ) && ( - - - { isMobile ? 'Res.' : 'Resolution' } - -