diff --git a/.env.example b/.env.example index 4bdc5535b1..6b91583db1 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,10 @@ NEXT_PUBLIC_SENTRY_DSN=https://sentry.io SENTRY_CSP_REPORT_URI=https://sentry.io NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx -NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx +NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx -FAVICON_GENERATOR_API_KEY=xxx -NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx \ No newline at end of file +NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=xxx \ No newline at end of file diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 37523e907c..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,8 +0,0 @@ -node_modules -node_modules_linux - -playwright/envs.js -deploy/tools/envs-validator/index.js -deploy/tools/feature-reporter/build/** -deploy/tools/feature-reporter/index.js -public/** \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 49e2ad4c43..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,341 +0,0 @@ -const RESTRICTED_MODULES = { - paths: [ - { name: 'dayjs', message: 'Please use lib/date/dayjs.ts instead of directly importing dayjs' }, - { name: '@chakra-ui/icons', message: 'Using @chakra-ui/icons is prohibited. Please use regular svg-icon instead (see examples in "icons/" folder)' }, - { name: '@metamask/providers', message: 'Please lazy-load @metamask/providers or use useProvider hook instead' }, - { name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' }, - { name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' }, - { - name: '@chakra-ui/react', - importNames: [ 'Popover', 'Menu', 'useToast' ], - message: 'Please use corresponding component or hook from ui/shared/chakra component instead', - }, - { - name: 'lodash', - message: 'Please use `import [package] from \'lodash/[package]\'` instead.', - }, - ], - patterns: [ - 'icons/*', - '!lodash/*', - ], -}; - -module.exports = { - env: { - es6: true, - browser: true, - node: true, - }, - 'extends': [ - 'next/core-web-vitals', - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:regexp/recommended', - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:jest/recommended', - 'plugin:playwright/playwright-test', - 'plugin:@tanstack/eslint-plugin-query/recommended', - ], - plugins: [ - 'es5', - 'react', - 'regexp', - '@typescript-eslint', - 'react-hooks', - 'jsx-a11y', - 'eslint-plugin-import-helpers', - 'jest', - 'eslint-plugin-no-cyrillic-string', - '@tanstack/query', - ], - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module', - ecmaFeatures: { - jsx: true, - }, - }, - settings: { - react: { - pragma: 'React', - version: 'detect', - }, - }, - rules: { - '@typescript-eslint/array-type': [ 'error', { - 'default': 'generic', - readonly: 'generic', - } ], - '@typescript-eslint/brace-style': [ 'error', '1tbs' ], - '@typescript-eslint/consistent-type-imports': [ 'error' ], - '@typescript-eslint/indent': [ 'error', 2 ], - '@typescript-eslint/member-delimiter-style': [ 'error' ], - '@typescript-eslint/naming-convention': [ 'error', - { - selector: 'default', - format: [ 'camelCase' ], - leadingUnderscore: 'allow', - trailingUnderscore: 'forbid', - }, - { - selector: 'class', - format: [ 'PascalCase' ], - }, - { - selector: 'enum', - format: [ 'PascalCase', 'UPPER_CASE' ], - }, - { - selector: 'enumMember', - format: [ 'camelCase', 'PascalCase', 'UPPER_CASE' ], - }, - { - selector: 'function', - format: [ 'camelCase', 'PascalCase' ], - }, - { - selector: 'interface', - format: [ 'PascalCase' ], - }, - { - selector: 'method', - format: [ 'camelCase', 'snake_case', 'UPPER_CASE' ], - leadingUnderscore: 'allow', - }, - { - selector: 'parameter', - format: [ 'camelCase', 'PascalCase' ], - leadingUnderscore: 'allow', - }, - { - selector: 'property', - format: null, - }, - { - selector: 'typeAlias', - format: [ 'PascalCase' ], - }, - { - selector: 'typeParameter', - format: [ 'PascalCase', 'UPPER_CASE' ], - }, - { - selector: 'variable', - format: [ 'camelCase', 'PascalCase', 'UPPER_CASE' ], - leadingUnderscore: 'allow', - }, - ], - '@typescript-eslint/no-duplicate-imports': [ 'error' ], - '@typescript-eslint/no-empty-function': [ 'off' ], - '@typescript-eslint/no-unused-vars': [ 'error', { ignoreRestSiblings: true } ], - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/no-useless-constructor': [ 'error' ], - '@typescript-eslint/type-annotation-spacing': 'error', - '@typescript-eslint/no-explicit-any': [ 'error', { ignoreRestArgs: true } ], - - // disabled in favor of @typescript-eslint - 'brace-style': 'off', - camelcase: 'off', - indent: 'off', - 'no-unused-vars': 'off', - 'no-use-before-define': 'off', - 'no-useless-constructor': 'off', - - 'array-bracket-spacing': [ 'error', 'always' ], - 'arrow-spacing': [ 'error', { before: true, after: true } ], - 'comma-dangle': [ 'error', 'always-multiline' ], - 'comma-spacing': [ 'error' ], - 'comma-style': [ 'error', 'last' ], - curly: [ 'error', 'all' ], - 'eol-last': 'error', - eqeqeq: [ 'error', 'allow-null' ], - 'id-match': [ 'error', '^[\\w$]+$' ], - 'jsx-quotes': [ 'error', 'prefer-double' ], - 'key-spacing': [ 'error', { - beforeColon: false, - afterColon: true, - } ], - 'keyword-spacing': 'error', - 'linebreak-style': [ 'error', 'unix' ], - 'lines-around-comment': [ 'error', { - beforeBlockComment: true, - allowBlockStart: true, - } ], - 'max-len': [ 'error', 160, 4 ], - 'no-console': 'error', - 'no-empty': [ 'error', { allowEmptyCatch: true } ], - 'no-implicit-coercion': [ 'error', { - number: true, - 'boolean': true, - string: true, - } ], - 'no-mixed-operators': [ 'error', { - groups: [ - [ '&&', '||' ], - ], - } ], - 'no-mixed-spaces-and-tabs': 'error', - 'no-multiple-empty-lines': [ 'error', { - max: 1, - maxEOF: 0, - maxBOF: 0, - } ], - 'no-multi-spaces': 'error', - 'no-multi-str': 'error', - 'no-nested-ternary': 'error', - 'no-trailing-spaces': 'error', - 'no-spaced-func': 'error', - 'no-with': 'error', - 'object-curly-spacing': [ 'error', 'always' ], - 'object-shorthand': 'off', - 'one-var': [ 'error', 'never' ], - 'operator-linebreak': [ 'error', 'after' ], - 'prefer-const': 'error', - 'quote-props': [ 'error', 'as-needed', { - keywords: true, - numbers: true, - } ], - quotes: [ 'error', 'single', { - allowTemplateLiterals: true, - } ], - 'space-before-function-paren': [ 'error', 'never' ], - 'space-before-blocks': [ 'error', 'always' ], - 'space-in-parens': [ 'error', 'never' ], - 'space-infix-ops': 'error', - 'space-unary-ops': 'off', - 'template-curly-spacing': [ 'error', 'always' ], - 'wrap-iife': [ 'error', 'inside' ], - semi: [ 'error', 'always' ], - - 'import-helpers/order-imports': [ - 'error', - { - newlinesBetween: 'always', - groups: [ - 'module', - '/types/', - [ - '/^nextjs/', - ], - [ - '/^configs/', - '/^data/', - '/^deploy/', - '/^icons/', - '/^jest/', - '/^lib/', - '/^mocks/', - '/^pages/', - '/^playwright/', - '/^stubs/', - '/^theme/', - '/^ui/', - ], - [ 'parent', 'sibling', 'index' ], - ], - alphabetize: { order: 'asc', ignoreCase: true }, - }, - ], - - 'no-restricted-imports': [ 'error', RESTRICTED_MODULES ], - 'no-restricted-properties': [ 2, { - object: 'process', - property: 'env', - // FIXME: restrict the rule only NEXT_PUBLIC variables - message: 'Please use configs/app/index.ts to import any NEXT_PUBLIC environment variables. For other properties please disable this rule for a while.', - } ], - - 'react/jsx-key': 'error', - 'react/jsx-no-bind': [ 'error', { - ignoreRefs: true, - } ], - 'react/jsx-curly-brace-presence': [ 'error', { - props: 'never', - children: 'never', - } ], - 'react/jsx-curly-spacing': [ 'error', { - when: 'always', - children: true, - spacing: { - objectLiterals: 'never', - }, - } ], - 'react/jsx-equals-spacing': [ 'error', 'never' ], - 'react/jsx-fragments': [ 'error', 'syntax' ], - 'react/jsx-no-duplicate-props': 'error', - 'react/jsx-no-target-blank': 'off', - 'react/jsx-no-useless-fragment': 'error', - 'react/jsx-tag-spacing': [ 'error', { - afterOpening: 'never', - beforeSelfClosing: 'never', - closingSlash: 'never', - } ], - 'react/jsx-wrap-multilines': [ 'error', { - declaration: 'parens-new-line', - assignment: 'parens-new-line', - 'return': 'parens-new-line', - arrow: 'parens-new-line', - condition: 'parens-new-line', - logical: 'parens-new-line', - prop: 'parens-new-line', - } ], - 'react/no-access-state-in-setstate': 'error', - 'react/no-deprecated': 'error', - 'react/no-direct-mutation-state': 'error', - 'react/no-find-dom-node': 'off', - 'react/no-redundant-should-component-update': 'error', - 'react/no-render-return-value': 'error', - 'react/no-string-refs': 'off', - 'react/no-unknown-property': 'error', - 'react/no-unused-state': 'error', - 'react/require-optimization': [ 'error' ], - 'react/void-dom-elements-no-children': 'error', - 'react-hooks/rules-of-hooks': 'error', - 'react-hooks/exhaustive-deps': 'error', - - 'regexp/confusing-quantifier': 'error', - 'regexp/control-character-escape': 'error', - 'regexp/negation': 'error', - 'regexp/no-dupe-disjunctions': 'error', - 'regexp/no-empty-alternative': 'error', - 'regexp/no-empty-capturing-group': 'error', - 'regexp/no-lazy-ends': 'error', - 'regexp/no-obscure-range': [ 'error', { - allowed: [ 'alphanumeric' ], - } ], - 'regexp/no-optional-assertion': 'error', - 'regexp/no-unused-capturing-group': [ 'error', { - fixable: true, - } ], - 'regexp/no-useless-character-class': 'error', - 'regexp/no-useless-dollar-replacements': 'error', - - 'no-cyrillic-string/no-cyrillic-string': 'error', - }, - overrides: [ - { - files: [ '*.js', '*.jsx' ], - rules: { - '@typescript-eslint/no-var-requires': 'off', - }, - }, - { - files: [ - '*.config.ts', - '*.config.js', - 'playwright/**', - 'deploy/tools/**', - 'middleware.ts', - 'nextjs/**', - 'instrumentation*.ts', - ], - rules: { - // for configs allow to consume env variables from process.env directly - 'no-restricted-properties': [ 0 ], - }, - }, - ], -}; diff --git a/.github/workflows/chakra-npm-publisher.yml b/.github/workflows/chakra-npm-publisher.yml new file mode 100644 index 0000000000..4ab143035a --- /dev/null +++ b/.github/workflows/chakra-npm-publisher.yml @@ -0,0 +1,52 @@ +name: Publish Chakra theme package to NPM + +on: + workflow_dispatch: + inputs: + version: + description: Package version + type: string + required: true + workflow_call: + inputs: + version: + description: Package version + type: string + required: true + +jobs: + publish: + runs-on: ubuntu-latest + name: Publish package to NPM registry + permissions: + id-token: write + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + # Also it will setup .npmrc file to publish to npm + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: '22.11.0' + registry-url: 'https://registry.npmjs.org' + + - name: Update package version + run: | + cd ./theme + npm version ${{ inputs.version }} + + - name: Build the package + run: | + yarn + cd ./theme + yarn + yarn build + + - name: Publish to NPM registry + run: | + cd ./theme + npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 9496bb37e6..54aa4a141c 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -30,7 +30,7 @@ jobs: - name: Setup node uses: actions/setup-node@v4 with: - node-version: 20.11.0 + node-version: 22.11.0 cache: 'yarn' - name: Cache node_modules @@ -43,7 +43,7 @@ jobs: - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile --ignore-optional + run: yarn --frozen-lockfile - name: Run ESLint run: yarn lint:eslint @@ -62,7 +62,7 @@ jobs: - name: Setup node uses: actions/setup-node@v4 with: - node-version: 20.11.0 + node-version: 22.11.0 cache: 'yarn' - name: Cache node_modules @@ -75,10 +75,10 @@ jobs: - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile --ignore-optional + run: yarn --frozen-lockfile - name: Install script dependencies - run: cd ./deploy/tools/envs-validator && yarn --frozen-lockfile --ignore-optional + run: cd ./deploy/tools/envs-validator && yarn --frozen-lockfile - name: Run validation tests run: | @@ -101,7 +101,7 @@ jobs: - name: Setup node uses: actions/setup-node@v4 with: - node-version: 20.11.0 + node-version: 22.11.0 cache: 'yarn' - name: Cache node_modules @@ -114,7 +114,7 @@ jobs: - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile --ignore-optional + run: yarn --frozen-lockfile - name: Run Jest run: yarn test:jest ${{ github.event_name == 'pull_request' && '--changedSince=origin/main' || '' }} --passWithNoTests @@ -133,7 +133,7 @@ jobs: - name: Setup node uses: actions/setup-node@v4 with: - node-version: 20.11.0 + node-version: 22.11.0 cache: 'yarn' - name: Cache node_modules @@ -146,7 +146,7 @@ jobs: - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile --ignore-optional + run: yarn --frozen-lockfile - name: Install script dependencies run: cd ./deploy/tools/affected-tests && yarn --frozen-lockfile @@ -171,7 +171,7 @@ jobs: (needs.pw_affected_tests.result == 'success' || needs.pw_affected_tests.result == 'skipped') runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright:v1.41.1-focal + image: mcr.microsoft.com/playwright:v1.49.0-noble strategy: fail-fast: false @@ -190,7 +190,7 @@ jobs: - name: Setup node uses: actions/setup-node@v4 with: - node-version: 20.11.0 + node-version: 22.11.0 cache: 'yarn' - name: Cache node_modules @@ -203,7 +203,7 @@ jobs: - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile --ignore-optional + run: yarn --frozen-lockfile - name: Download affected tests list if: ${{ needs.pw_affected_tests.result == 'success' }} diff --git a/.github/workflows/deploy-review-l2.yml b/.github/workflows/deploy-review-l2.yml index 02feb3ce64..1f2d980ab9 100644 --- a/.github/workflows/deploy-review-l2.yml +++ b/.github/workflows/deploy-review-l2.yml @@ -2,6 +2,35 @@ name: Deploy review environment (L2) on: workflow_dispatch: + inputs: + envs_preset: + description: ENVs preset + required: false + default: "" + type: choice + options: + - none + - arbitrum + - arbitrum_nova + - base + - celo_alfajores + - garnet + - gnosis + - eth + - eth_sepolia + - eth_goerli + - filecoin + - optimism + - optimism_celestia + - optimism_sepolia + - polygon + - rootstock + - shibarium + - stability + - zkevm + - zilliqa_prototestnet + - zksync + - zora jobs: make_slug: @@ -23,6 +52,7 @@ jobs: uses: './.github/workflows/publish-image.yml' with: tags: ghcr.io/blockscout/frontend:review-${{ needs.make_slug.outputs.REF_SLUG }} + build_args: ENVS_PRESET=${{ inputs.envs_preset }} secrets: inherit deploy_review_l2: diff --git a/.github/workflows/deploy-review.yml b/.github/workflows/deploy-review.yml index 4596a3293b..427144554d 100644 --- a/.github/workflows/deploy-review.yml +++ b/.github/workflows/deploy-review.yml @@ -11,19 +11,26 @@ on: options: - none - arbitrum + - arbitrum_nova - base - celo_alfajores + - garnet - gnosis - eth - eth_sepolia - eth_goerli + - filecoin - optimism + - optimism_celestia - optimism_sepolia - polygon - rootstock + - shibarium - stability - zkevm + - zilliqa_prototestnet - zksync + - zora jobs: make_slug: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df2c0433b1..7485ee7c82 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,6 +81,12 @@ jobs: with: platforms: linux/amd64,linux/arm64/v8 + sync_envs_docs: + name: Sync ENV variables docs + uses: './.github/workflows/sync-envs-docs.yml' + needs: publish_image + secrets: inherit + # Temporary disable this step because it is broken # There is an issue with building web3modal deps upload_source_maps: diff --git a/.github/workflows/sync-envs-docs.yml b/.github/workflows/sync-envs-docs.yml new file mode 100644 index 0000000000..462b22b314 --- /dev/null +++ b/.github/workflows/sync-envs-docs.yml @@ -0,0 +1,41 @@ +name: Sync ENV variables docs + +on: + workflow_dispatch: + workflow_call: + +jobs: + run: + name: Run + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Copy main ENV file to Blockscout Docs repository + uses: dmnemec/copy_file_to_another_repo_action@main + env: + API_TOKEN_GITHUB: ${{ secrets.GITHIB_BOT_TOKEN }} + with: + source_file: 'docs/ENVS.md' + destination_repo: 'blockscout/docs' + destination_folder: 'setup/env-variables/frontend-common-envs' + rename: 'envs.md' + destination_branch: 'master' + user_email: 'bot@blockscout.com' + user_name: 'blockscout-bot' + use_rsync: true + + - name: Copy deprecated ENV file to Blockscout Docs repository + uses: dmnemec/copy_file_to_another_repo_action@main + env: + API_TOKEN_GITHUB: ${{ secrets.GITHIB_BOT_TOKEN }} + with: + source_file: 'docs/DEPRECATED_ENVS.md' + destination_repo: 'blockscout/docs' + destination_folder: 'setup/env-variables/frontend-common-envs' + rename: 'deprecated-envs.md' + destination_branch: 'master' + user_email: 'bot@blockscout.com' + user_name: 'blockscout-bot' + use_rsync: true diff --git a/.github/workflows/upload-source-maps.yml b/.github/workflows/upload-source-maps.yml index 55b18067fe..4814ed6d8e 100644 --- a/.github/workflows/upload-source-maps.yml +++ b/.github/workflows/upload-source-maps.yml @@ -21,7 +21,7 @@ jobs: - name: Setup node uses: actions/setup-node@v4 with: - node-version: 20.11.0 + node-version: 22.11.0 cache: 'yarn' - name: Cache node_modules @@ -34,7 +34,7 @@ jobs: - name: Install dependencies if: steps.cache-node-modules.outputs.cache-hit != 'true' - run: yarn --frozen-lockfile --ignore-optional + run: yarn --frozen-lockfile - name: Make production build with source maps run: yarn build diff --git a/.nvmrc b/.nvmrc index 8b0beab16a..fdb2eaaff0 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.11.0 +22.11.0 \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a3b065514e..f847a6f356 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -362,17 +362,23 @@ "arbitrum", "base", "celo_alfajores", + "garnet", "gnosis", "eth", "eth_goerli", "eth_sepolia", + "filecoin", "optimism", + "optimism_celestia", "optimism_sepolia", "polygon", "rootstock_testnet", + "shibarium", "stability_testnet", "zkevm", + "zilliqa_prototestnet", "zksync", + "zora", ], "default": "main" }, diff --git a/Dockerfile b/Dockerfile index 83aa8c4b13..0141bee554 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # ***************************** # *** STAGE 1: Dependencies *** # ***************************** -FROM node:20.11.0-alpine AS deps +FROM node:22.11.0-alpine AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat python3 make g++ RUN ln -sf /usr/bin/python3 /usr/bin/python @@ -27,11 +27,17 @@ WORKDIR /envs-validator COPY ./deploy/tools/envs-validator/package.json ./deploy/tools/envs-validator/yarn.lock ./ RUN yarn --frozen-lockfile +### FAVICON GENERATOR +# Install dependencies +WORKDIR /favicon-generator +COPY ./deploy/tools/favicon-generator/package.json ./deploy/tools/favicon-generator/yarn.lock ./ +RUN yarn --frozen-lockfile + # ***************************** # ****** STAGE 2: Build ******* # ***************************** -FROM node:20.11.0-alpine AS builder +FROM node:22.11.0-alpine AS builder RUN apk add --no-cache --upgrade libc6-compat bash # pass build args to env variables @@ -51,7 +57,7 @@ COPY --from=deps /app/node_modules ./node_modules COPY . . # Generate .env.registry with ENVs list and save build args into .env file -COPY --chmod=+x ./deploy/scripts/collect_envs.sh ./ +COPY --chmod=755 ./deploy/scripts/collect_envs.sh ./ RUN ./collect_envs.sh ./docs/ENVS.md # Next.js collects completely anonymous telemetry data about general usage. @@ -77,11 +83,15 @@ COPY --from=deps /envs-validator/node_modules ./deploy/tools/envs-validator/node RUN cd ./deploy/tools/envs-validator && yarn build +### FAVICON GENERATOR +# Copy dependencies and source code +COPY --from=deps /favicon-generator/node_modules ./deploy/tools/favicon-generator/node_modules + # ***************************** # ******* STAGE 3: Run ******** # ***************************** # Production image, copy all the files and run next -FROM node:20.11.0-alpine AS runner +FROM node:22.11.0-alpine AS runner RUN apk add --no-cache --upgrade bash curl jq unzip ### APP @@ -105,15 +115,15 @@ COPY --from=builder /app/deploy/tools/feature-reporter/index.js ./feature-report # Copy scripts ## Entripoint -COPY --chmod=+x ./deploy/scripts/entrypoint.sh . +COPY --chmod=755 ./deploy/scripts/entrypoint.sh . ## ENV validator and client script maker -COPY --chmod=+x ./deploy/scripts/validate_envs.sh . -COPY --chmod=+x ./deploy/scripts/make_envs_script.sh . +COPY --chmod=755 ./deploy/scripts/validate_envs.sh . +COPY --chmod=755 ./deploy/scripts/make_envs_script.sh . ## Assets downloader -COPY --chmod=+x ./deploy/scripts/download_assets.sh . +COPY --chmod=755 ./deploy/scripts/download_assets.sh . ## Favicon generator -COPY --chmod=+x ./deploy/scripts/favicon_generator.sh . -COPY ./deploy/tools/favicon-generator ./deploy/tools/favicon-generator +COPY --chmod=755 ./deploy/scripts/favicon_generator.sh . +COPY --from=builder /app/deploy/tools/favicon-generator ./deploy/tools/favicon-generator RUN ["chmod", "-R", "777", "./deploy/tools/favicon-generator"] RUN ["chmod", "-R", "777", "./public"] diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 30ec4f8b6f..2dc937a6eb 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -14,6 +14,10 @@ - Updated dependency: PackageName 1 to version x.x.x. - Updated dependency: PackageName 2 to version x.x.x. +## 🎨 Design updates +- New style 1. +- New style 2. + ## ✨ Other Changes - Another minor change 1. - Another minor change 2. diff --git a/configs/app/chain.ts b/configs/app/chain.ts index c0cebaa279..3b8558aafa 100644 --- a/configs/app/chain.ts +++ b/configs/app/chain.ts @@ -1,7 +1,22 @@ +import type { RollupType } from 'types/client/rollup'; +import type { NetworkVerificationType, NetworkVerificationTypeEnvs } from 'types/networks'; + import { getEnvValue } from './utils'; const DEFAULT_CURRENCY_DECIMALS = 18; +const rollupType = getEnvValue('NEXT_PUBLIC_ROLLUP_TYPE') as RollupType; + +const verificationType: NetworkVerificationType = (() => { + if (rollupType === 'arbitrum') { + return 'posting'; + } + if (rollupType === 'zkEvm') { + return 'sequencing'; + } + return getEnvValue('NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE') as NetworkVerificationTypeEnvs || 'mining'; +})(); + const chain = Object.freeze({ id: getEnvValue('NEXT_PUBLIC_NETWORK_ID'), name: getEnvValue('NEXT_PUBLIC_NETWORK_NAME'), @@ -19,8 +34,8 @@ const chain = Object.freeze({ tokenStandard: getEnvValue('NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME') || 'ERC', rpcUrl: getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL'), isTestnet: getEnvValue('NEXT_PUBLIC_IS_TESTNET') === 'true', - verificationType: getEnvValue('NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE') || 'mining', nearNetwork: getEnvValue('NEXT_PUBLIC_NEAR_NETWORK'), + verificationType, }); export default chain; diff --git a/configs/app/features/account.ts b/configs/app/features/account.ts index 120a94b7e9..56cf1d1eaa 100644 --- a/configs/app/features/account.ts +++ b/configs/app/features/account.ts @@ -1,45 +1,16 @@ import type { Feature } from './types'; -import stripTrailingSlash from 'lib/stripTrailingSlash'; - -import app from '../app'; +import services from '../services'; import { getEnvValue } from '../utils'; -const authUrl = stripTrailingSlash(getEnvValue('NEXT_PUBLIC_AUTH_URL') || app.baseUrl); - -const logoutUrl = (() => { - try { - const envUrl = getEnvValue('NEXT_PUBLIC_LOGOUT_URL'); - const auth0ClientId = getEnvValue('NEXT_PUBLIC_AUTH0_CLIENT_ID'); - const returnUrl = authUrl + '/auth/logout'; - - if (!envUrl || !auth0ClientId) { - throw Error(); - } - - const url = new URL(envUrl); - url.searchParams.set('client_id', auth0ClientId); - url.searchParams.set('returnTo', returnUrl); - - return url.toString(); - } catch (error) { - return; - } -})(); - const title = 'My account'; -const config: Feature<{ authUrl: string; logoutUrl: string }> = (() => { - if ( - getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true' && - authUrl && - logoutUrl - ) { +const config: Feature<{ isEnabled: true; recaptchaSiteKey: string }> = (() => { + if (getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true' && services.reCaptchaV3.siteKey) { return Object.freeze({ title, isEnabled: true, - authUrl, - logoutUrl, + recaptchaSiteKey: services.reCaptchaV3.siteKey, }); } diff --git a/configs/app/features/addressProfileAPI.ts b/configs/app/features/addressProfileAPI.ts new file mode 100644 index 0000000000..e46301ee6f --- /dev/null +++ b/configs/app/features/addressProfileAPI.ts @@ -0,0 +1,45 @@ +import type { Feature } from './types'; +import type { AddressProfileAPIConfig } from 'types/client/addressProfileAPIConfig'; + +import { getEnvValue, parseEnvJson } from '../utils'; + +const value = parseEnvJson(getEnvValue('NEXT_PUBLIC_ADDRESS_USERNAME_TAG')); + +function checkApiUrlTemplate(apiUrlTemplate: string): boolean { + try { + const testUrl = apiUrlTemplate.replace('{address}', '0x0000000000000000000000000000000000000000'); + new URL(testUrl).toString(); + return true; + } catch (error) { + return false; + } +} + +const title = 'User profile API'; + +const config: Feature<{ + apiUrlTemplate: string; + tagLinkTemplate?: string; + tagIcon?: string; + tagBgColor?: string; + tagTextColor?: string; +}> = (() => { + if (value && checkApiUrlTemplate(value.api_url_template)) { + return Object.freeze({ + title, + isEnabled: true, + apiUrlTemplate: value.api_url_template, + tagLinkTemplate: value.tag_link_template, + tagIcon: value.tag_icon, + tagBgColor: value.tag_bg_color, + tagTextColor: value.tag_text_color, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/adsBanner.ts b/configs/app/features/adsBanner.ts index f9860d1afc..c455ed4546 100644 --- a/configs/app/features/adsBanner.ts +++ b/configs/app/features/adsBanner.ts @@ -34,7 +34,7 @@ type AdsBannerFeaturePayload = { mobile: AdButlerConfig; }; }; -} +}; const config: Feature = (() => { if (provider === 'adbutler') { diff --git a/configs/app/features/celo.ts b/configs/app/features/celo.ts new file mode 100644 index 0000000000..9d169e4220 --- /dev/null +++ b/configs/app/features/celo.ts @@ -0,0 +1,24 @@ +import type { Feature } from './types'; + +import { getEnvValue } from '../utils'; + +const title = 'Celo chain'; + +const config: Feature<{ L2UpgradeBlock: number | undefined; BLOCKS_PER_EPOCH: number }> = (() => { + + if (getEnvValue('NEXT_PUBLIC_CELO_ENABLED') === 'true') { + return Object.freeze({ + title, + isEnabled: true, + L2UpgradeBlock: getEnvValue('NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK') ? Number(getEnvValue('NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK')) : undefined, + BLOCKS_PER_EPOCH: 17_280, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/csvExport.ts b/configs/app/features/csvExport.ts index 442b4bedc7..1eb2cbe289 100644 --- a/configs/app/features/csvExport.ts +++ b/configs/app/features/csvExport.ts @@ -4,13 +4,13 @@ import services from '../services'; const title = 'Export data to CSV file'; -const config: Feature<{ reCaptcha: { siteKey: string }}> = (() => { - if (services.reCaptcha.siteKey) { +const config: Feature<{ reCaptcha: { siteKey: string } }> = (() => { + if (services.reCaptchaV3.siteKey) { return Object.freeze({ title, isEnabled: true, reCaptcha: { - siteKey: services.reCaptcha.siteKey, + siteKey: services.reCaptchaV3.siteKey, }, }); } diff --git a/configs/app/features/getGasButton.ts b/configs/app/features/getGasButton.ts new file mode 100644 index 0000000000..db392f3c9a --- /dev/null +++ b/configs/app/features/getGasButton.ts @@ -0,0 +1,35 @@ +import type { Feature } from './types'; +import type { GasRefuelProviderConfig } from 'types/client/gasRefuelProviderConfig'; + +import chain from '../chain'; +import { getEnvValue, parseEnvJson } from '../utils'; +import marketplace from './marketplace'; + +const value = parseEnvJson(getEnvValue('NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG')); + +const title = 'Get gas button'; + +const config: Feature<{ + name: string; + logoUrl?: string; + url: string; + dappId?: string; +}> = (() => { + if (value) { + return Object.freeze({ + title, + isEnabled: true, + name: value.name, + logoUrl: value.logo, + url: value.url_template.replace('{chainId}', chain.id || ''), + dappId: marketplace.isEnabled ? value.dapp_id : undefined, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/graphqlApiDocs.ts b/configs/app/features/graphqlApiDocs.ts index 09285ac898..d26c3bfde3 100644 --- a/configs/app/features/graphqlApiDocs.ts +++ b/configs/app/features/graphqlApiDocs.ts @@ -7,6 +7,14 @@ const defaultTxHash = getEnvValue('NEXT_PUBLIC_GRAPHIQL_TRANSACTION'); const title = 'GraphQL API documentation'; const config: Feature<{ defaultTxHash: string | undefined }> = (() => { + + if (defaultTxHash === 'none') { + return Object.freeze({ + title, + isEnabled: false, + }); + } + return Object.freeze({ title, isEnabled: true, diff --git a/configs/app/features/index.ts b/configs/app/features/index.ts index b2ac272980..b85a55d546 100644 --- a/configs/app/features/index.ts +++ b/configs/app/features/index.ts @@ -6,29 +6,36 @@ export { default as adsText } from './adsText'; export { default as beaconChain } from './beaconChain'; export { default as bridgedTokens } from './bridgedTokens'; export { default as blockchainInteraction } from './blockchainInteraction'; +export { default as celo } from './celo'; export { default as csvExport } from './csvExport'; export { default as dataAvailability } from './dataAvailability'; export { default as deFiDropdown } from './deFiDropdown'; export { default as faultProofSystem } from './faultProofSystem'; export { default as gasTracker } from './gasTracker'; +export { default as getGasButton } from './getGasButton'; export { default as googleAnalytics } from './googleAnalytics'; export { default as graphqlApiDocs } from './graphqlApiDocs'; export { default as growthBook } from './growthBook'; export { default as marketplace } from './marketplace'; export { default as metasuites } from './metasuites'; export { default as mixpanel } from './mixpanel'; +export { default as mudFramework } from './mudFramework'; export { default as multichainButton } from './multichainButton'; export { default as nameService } from './nameService'; export { default as publicTagsSubmission } from './publicTagsSubmission'; export { default as restApiDocs } from './restApiDocs'; +export { default as rewards } from './rewards'; export { default as rollup } from './rollup'; export { default as safe } from './safe'; +export { default as saveOnGas } from './saveOnGas'; export { default as sentry } from './sentry'; export { default as sol2uml } from './sol2uml'; export { default as stats } from './stats'; export { default as suave } from './suave'; export { default as txInterpretation } from './txInterpretation'; export { default as userOps } from './userOps'; +export { default as addressProfileAPI } from './addressProfileAPI'; export { default as validators } from './validators'; export { default as verifiedTokens } from './verifiedTokens'; export { default as web3Wallet } from './web3Wallet'; +export { default as xStarScore } from './xStarScore'; diff --git a/configs/app/features/marketplace.ts b/configs/app/features/marketplace.ts index 937397be6a..ab5ab4a965 100644 --- a/configs/app/features/marketplace.ts +++ b/configs/app/features/marketplace.ts @@ -14,6 +14,9 @@ const securityReportsUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_SEC const featuredApp = getEnvValue('NEXT_PUBLIC_MARKETPLACE_FEATURED_APP'); const bannerContentUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL'); const bannerLinkUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL'); +const ratingAirtableApiKey = getEnvValue('NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY'); +const ratingAirtableBaseId = getEnvValue('NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID'); +const graphLinksUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL'); const title = 'Marketplace'; @@ -27,6 +30,8 @@ const config: Feature<( securityReportsUrl: string | undefined; featuredApp: string | undefined; banner: { contentUrl: string; linkUrl: string } | undefined; + rating: { airtableApiKey: string; airtableBaseId: string } | undefined; + graphLinksUrl: string | undefined; }> = (() => { if (enabled === 'true' && chain.rpcUrl && submitFormUrl) { const props = { @@ -39,6 +44,11 @@ const config: Feature<( contentUrl: bannerContentUrl, linkUrl: bannerLinkUrl, } : undefined, + rating: ratingAirtableApiKey && ratingAirtableBaseId ? { + airtableApiKey: ratingAirtableApiKey, + airtableBaseId: ratingAirtableBaseId, + } : undefined, + graphLinksUrl, }; if (configUrl) { diff --git a/configs/app/features/mudFramework.ts b/configs/app/features/mudFramework.ts new file mode 100644 index 0000000000..86df2af34a --- /dev/null +++ b/configs/app/features/mudFramework.ts @@ -0,0 +1,22 @@ +import type { Feature } from './types'; + +import { getEnvValue } from '../utils'; +import rollup from './rollup'; + +const title = 'MUD framework'; + +const config: Feature<{ isEnabled: true }> = (() => { + if (rollup.isEnabled && rollup.type === 'optimistic' && getEnvValue('NEXT_PUBLIC_HAS_MUD_FRAMEWORK') === 'true') { + return Object.freeze({ + title, + isEnabled: true, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/multichainButton.ts b/configs/app/features/multichainButton.ts index 47b1433d05..923a16bd27 100644 --- a/configs/app/features/multichainButton.ts +++ b/configs/app/features/multichainButton.ts @@ -1,22 +1,24 @@ import type { Feature } from './types'; -import type { MultichainProviderConfig } from 'types/client/multichainProviderConfig'; +import type { MultichainProviderConfig, MultichainProviderConfigParsed } from 'types/client/multichainProviderConfig'; import { getEnvValue, parseEnvJson } from '../utils'; import marketplace from './marketplace'; -const value = parseEnvJson(getEnvValue('NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG')); +const value = parseEnvJson>(getEnvValue('NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG')); const title = 'Multichain balance'; -const config: Feature<{name: string; logoUrl?: string; urlTemplate: string; dappId?: string }> = (() => { +const config: Feature<{ providers: Array }> = (() => { if (value) { return Object.freeze({ title, isEnabled: true, - name: value.name, - logoUrl: value.logo, - urlTemplate: value.url_template, - dappId: marketplace.isEnabled ? value.dapp_id : undefined, + providers: value.map((provider) => ({ + name: provider.name, + logoUrl: provider.logo, + urlTemplate: provider.url_template, + dappId: marketplace.isEnabled ? provider.dapp_id : undefined, + })), }); } diff --git a/configs/app/features/publicTagsSubmission.ts b/configs/app/features/publicTagsSubmission.ts index 296d5e143a..ab1b0e33d2 100644 --- a/configs/app/features/publicTagsSubmission.ts +++ b/configs/app/features/publicTagsSubmission.ts @@ -9,7 +9,7 @@ const apiHost = getEnvValue('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST'); const title = 'Public tag submission'; const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { - if (services.reCaptcha.siteKey && addressMetadata.isEnabled && apiHost) { + if (services.reCaptchaV3.siteKey && addressMetadata.isEnabled && apiHost) { return Object.freeze({ title, isEnabled: true, diff --git a/configs/app/features/restApiDocs.ts b/configs/app/features/restApiDocs.ts index 281282ab05..ae25f05c0a 100644 --- a/configs/app/features/restApiDocs.ts +++ b/configs/app/features/restApiDocs.ts @@ -2,15 +2,23 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const specUrl = getEnvValue('NEXT_PUBLIC_API_SPEC_URL') || `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml`; +const DEFAULT_URL = `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml`; +const envValue = getEnvValue('NEXT_PUBLIC_API_SPEC_URL'); const title = 'REST API documentation'; const config: Feature<{ specUrl: string }> = (() => { + if (envValue === 'none') { + return Object.freeze({ + title, + isEnabled: false, + }); + } + return Object.freeze({ title, isEnabled: true, - specUrl, + specUrl: envValue || DEFAULT_URL, }); })(); diff --git a/configs/app/features/rewards.ts b/configs/app/features/rewards.ts new file mode 100644 index 0000000000..9f19ae58bf --- /dev/null +++ b/configs/app/features/rewards.ts @@ -0,0 +1,29 @@ +import type { Feature } from './types'; + +import { getEnvValue } from '../utils'; +import account from './account'; +import blockchainInteraction from './blockchainInteraction'; + +const apiHost = getEnvValue('NEXT_PUBLIC_REWARDS_SERVICE_API_HOST'); + +const title = 'Rewards service integration'; + +const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { + if (apiHost && account.isEnabled && blockchainInteraction.isEnabled) { + return Object.freeze({ + title, + isEnabled: true, + api: { + endpoint: apiHost, + basePath: '', + }, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/rollup.ts b/configs/app/features/rollup.ts index 2560734097..3728e60c18 100644 --- a/configs/app/features/rollup.ts +++ b/configs/app/features/rollup.ts @@ -2,6 +2,8 @@ import type { Feature } from './types'; import type { RollupType } from 'types/client/rollup'; import { ROLLUP_TYPES } from 'types/client/rollup'; +import stripTrailingSlash from 'lib/stripTrailingSlash'; + import { getEnvValue } from '../utils'; const type = (() => { @@ -14,15 +16,26 @@ const L2WithdrawalUrl = getEnvValue('NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL'); const title = 'Rollup (L2) chain'; -const config: Feature<{ type: RollupType; L1BaseUrl: string; L2WithdrawalUrl?: string }> = (() => { - +const config: Feature<{ + type: RollupType; + L1BaseUrl: string; + homepage: { showLatestBlocks: boolean }; + outputRootsEnabled: boolean; + L2WithdrawalUrl: string | undefined; + parentChainName: string | undefined; +}> = (() => { if (type && L1BaseUrl) { return Object.freeze({ title, isEnabled: true, type, - L1BaseUrl, - L2WithdrawalUrl, + L1BaseUrl: stripTrailingSlash(L1BaseUrl), + L2WithdrawalUrl: type === 'optimistic' ? L2WithdrawalUrl : undefined, + outputRootsEnabled: type === 'optimistic' && getEnvValue('NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED') !== 'false', + parentChainName: type === 'arbitrum' ? getEnvValue('NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME') : undefined, + homepage: { + showLatestBlocks: getEnvValue('NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS') === 'true', + }, }); } diff --git a/configs/app/features/saveOnGas.ts b/configs/app/features/saveOnGas.ts new file mode 100644 index 0000000000..64d24ed56f --- /dev/null +++ b/configs/app/features/saveOnGas.ts @@ -0,0 +1,25 @@ +import type { Feature } from './types'; + +import { getEnvValue } from '../utils'; +import marketplace from './marketplace'; + +const title = 'Save on gas with GasHawk'; + +const config: Feature<{ + apiUrlTemplate: string; +}> = (() => { + if (getEnvValue('NEXT_PUBLIC_SAVE_ON_GAS_ENABLED') === 'true' && marketplace.isEnabled) { + return Object.freeze({ + title, + isEnabled: true, + apiUrlTemplate: 'https://core.gashawk.io/apiv2/stats/address/
/savingsPotential/0x1', + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/features/web3Wallet.ts b/configs/app/features/web3Wallet.ts index f4df4d56ea..893f92394b 100644 --- a/configs/app/features/web3Wallet.ts +++ b/configs/app/features/web3Wallet.ts @@ -21,7 +21,7 @@ const wallets = ((): Array | undefined => { const title = 'Web3 wallet integration (add token or network to the wallet)'; -const config: Feature<{ wallets: Array; addToken: { isDisabled: boolean }}> = (() => { +const config: Feature<{ wallets: Array; addToken: { isDisabled: boolean } }> = (() => { if (wallets && wallets.length > 0) { return Object.freeze({ title, diff --git a/configs/app/features/xStarScore.ts b/configs/app/features/xStarScore.ts new file mode 100644 index 0000000000..4f8efa262d --- /dev/null +++ b/configs/app/features/xStarScore.ts @@ -0,0 +1,23 @@ +import type { Feature } from './types'; + +import { getEnvValue } from '../utils'; + +const title = 'XStar score'; +const url = getEnvValue('NEXT_PUBLIC_XSTAR_SCORE_URL'); + +const config: Feature<{ url: string }> = (() => { + if (url) { + return Object.freeze({ + title, + url, + isEnabled: true, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/app/meta.ts b/configs/app/meta.ts index 0fd72681ea..3d7b777e03 100644 --- a/configs/app/meta.ts +++ b/configs/app/meta.ts @@ -4,7 +4,7 @@ import { getEnvValue, getExternalAssetFilePath } from './utils'; const defaultImageUrl = '/static/og_placeholder.png'; const meta = Object.freeze({ - promoteBlockscoutInTitle: getEnvValue('NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE') || 'true', + promoteBlockscoutInTitle: getEnvValue('NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE') === 'false' ? false : true, og: { description: getEnvValue('NEXT_PUBLIC_OG_DESCRIPTION') || '', imageUrl: app.baseUrl + (getExternalAssetFilePath('NEXT_PUBLIC_OG_IMAGE_URL') || defaultImageUrl), diff --git a/configs/app/services.ts b/configs/app/services.ts index 86df538215..1472f00f82 100644 --- a/configs/app/services.ts +++ b/configs/app/services.ts @@ -1,7 +1,7 @@ import { getEnvValue } from './utils'; export default Object.freeze({ - reCaptcha: { - siteKey: getEnvValue('NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY'), + reCaptchaV3: { + siteKey: getEnvValue('NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY'), }, }); diff --git a/configs/app/ui.ts b/configs/app/ui.ts index 73308ede5b..674580d944 100644 --- a/configs/app/ui.ts +++ b/configs/app/ui.ts @@ -1,11 +1,13 @@ import type { ContractCodeIde } from 'types/client/contract'; import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId, type NavigationLayout } from 'types/client/navigation'; -import type { ChainIndicatorId } from 'types/homepage'; +import { HOME_STATS_WIDGET_IDS, type ChainIndicatorId, type HeroBannerConfig, type HomeStatsWidgetId } from 'types/homepage'; import type { NetworkExplorer } from 'types/networks'; import type { ColorThemeId } from 'types/settings'; +import type { FontFamily } from 'types/ui'; import { COLOR_THEMES } from 'lib/settings/colorTheme'; +import * as features from './features'; import * as views from './ui/views'; import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils'; @@ -24,6 +26,22 @@ const hiddenLinks = (() => { return result; })(); +const homePageStats: Array = (() => { + const parsedValue = parseEnvJson>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_STATS')); + + if (!Array.isArray(parsedValue)) { + const rollupFeature = features.rollup; + + if (rollupFeature.isEnabled && [ 'zkEvm', 'zkSync', 'arbitrum' ].includes(rollupFeature.type)) { + return [ 'latest_batch', 'average_block_time', 'total_txs', 'wallet_addresses', 'gas_tracker' ]; + } + + return [ 'total_blocks', 'average_block_time', 'total_txs', 'wallet_addresses', 'gas_tracker' ]; + } + + return parsedValue.filter((item) => HOME_STATS_WIDGET_IDS.includes(item)); +})(); + const highlightedRoutes = (() => { const parsedValue = parseEnvJson>(getEnvValue('NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES')); return Array.isArray(parsedValue) ? parsedValue : []; @@ -34,9 +52,6 @@ const defaultColorTheme = (() => { return COLOR_THEMES.find((theme) => theme.id === envValue); })(); -// eslint-disable-next-line max-len -const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = '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)'; - const UI = Object.freeze({ navigation: { logo: { @@ -60,11 +75,13 @@ const UI = Object.freeze({ }, homepage: { charts: parseEnvJson>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [], + stats: homePageStats, + heroBanner: parseEnvJson(getEnvValue('NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG')), + // !!! DEPRECATED !!! plate: { - background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND') || HOMEPAGE_PLATE_BACKGROUND_DEFAULT, - textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR') || 'white', + background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND'), + textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR'), }, - showAvgBlockTime: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME') === 'false' ? false : true, }, views, indexingAlert: { @@ -88,6 +105,11 @@ const UI = Object.freeze({ colorTheme: { 'default': defaultColorTheme, }, + fonts: { + heading: parseEnvJson(getEnvValue('NEXT_PUBLIC_FONT_FAMILY_HEADING')), + body: parseEnvJson(getEnvValue('NEXT_PUBLIC_FONT_FAMILY_BODY')), + }, + maxContentWidth: getEnvValue('NEXT_PUBLIC_MAX_CONTENT_WIDTH_ENABLED') === 'false' ? false : true, }); export default UI; diff --git a/configs/app/ui/views/address.ts b/configs/app/ui/views/address.ts index c2f0f4fb52..6794a2735f 100644 --- a/configs/app/ui/views/address.ts +++ b/configs/app/ui/views/address.ts @@ -1,5 +1,7 @@ -import type { AddressViewId, IdenticonType } from 'types/views/address'; -import { ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from 'types/views/address'; +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'; @@ -9,6 +11,28 @@ const identiconType: IdenticonType = (() => { return IDENTICON_TYPES.find((type) => value === type) || 'jazzicon'; })(); +const formats: Array = (() => { + const value = (parseEnvJson>(getEnvValue('NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT')) || []) + .filter((format) => ADDRESS_FORMATS.includes(format)); + + if (value.length === 0) { + return [ 'base16' ]; + } + + return value; +})(); + +const bech32Prefix = (() => { + const value = getEnvValue('NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX'); + + if (!value || !formats.includes('bech32')) { + return undefined; + } + + // these are the limits of the bech32 prefix - https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32 + return value.length >= 1 && value.length <= 83 ? value : undefined; +})(); + const hiddenViews = (() => { const parsedValue = parseEnvJson>(getEnvValue('NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS')) || []; @@ -24,10 +48,30 @@ const hiddenViews = (() => { return result; })(); +const extraVerificationMethods: Array = (() => { + const envValue = getEnvValue('NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS'); + if (envValue === 'none') { + return []; + } + + if (!envValue) { + return SMART_CONTRACT_EXTRA_VERIFICATION_METHODS; + } + + const parsedMethods = parseEnvJson>(getEnvValue('NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS')) || []; + + return SMART_CONTRACT_EXTRA_VERIFICATION_METHODS.filter((method) => parsedMethods.includes(method)); +})(); + const config = Object.freeze({ identiconType, + hashFormat: { + availableFormats: formats, + bech32Prefix, + }, hiddenViews, solidityscanEnabled: getEnvValue('NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED') === 'true', + extraVerificationMethods, }); export default config; diff --git a/configs/app/ui/views/nft.ts b/configs/app/ui/views/nft.ts index b0d9f9b28c..ab9636ccea 100644 --- a/configs/app/ui/views/nft.ts +++ b/configs/app/ui/views/nft.ts @@ -4,6 +4,9 @@ import { getEnvValue, parseEnvJson } from 'configs/app/utils'; const config = Object.freeze({ marketplaces: parseEnvJson>(getEnvValue('NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES')) || [], + verifiedFetch: { + isEnabled: getEnvValue('NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED') === 'false' ? false : true, + }, }); export default config; diff --git a/configs/app/utils.ts b/configs/app/utils.ts index 97a9afd05d..b188d14e8d 100644 --- a/configs/app/utils.ts +++ b/configs/app/utils.ts @@ -5,7 +5,7 @@ export const replaceQuotes = (value: string | undefined) => value?.replaceAll('\ export const getEnvValue = (envName: string) => { // eslint-disable-next-line no-restricted-properties - const envs = isBrowser() ? window.__envs : process.env; + const envs = (isBrowser() ? window.__envs : process.env) ?? {}; if (isBrowser() && envs.NEXT_PUBLIC_APP_INSTANCE === 'pw') { const storageValue = localStorage.getItem(envName); @@ -39,10 +39,22 @@ export const getExternalAssetFilePath = (envName: string) => { export const buildExternalAssetFilePath = (name: string, value: string) => { try { const fileName = name.replace(/^NEXT_PUBLIC_/, '').replace(/_URL$/, '').toLowerCase(); - const url = new URL(value); - const fileExtension = url.pathname.match(regexp.FILE_EXTENSION)?.[1]; + + const fileExtension = getAssetFileExtension(value); + if (!fileExtension) { + throw new Error('Cannot get file path'); + } return `/assets/configs/${ fileName }.${ fileExtension }`; } catch (error) { return; } }; + +function getAssetFileExtension(value: string) { + try { + const url = new URL(value); + return url.pathname.match(regexp.FILE_EXTENSION)?.[1]; + } catch (error) { + return parseEnvJson(value) ? 'json' : undefined; + } +} diff --git a/configs/envs/.env.arbitrum b/configs/envs/.env.arbitrum index a1ec2fef59..1fc81ce901 100644 --- a/configs/envs/.env.arbitrum +++ b/configs/envs/.env.arbitrum @@ -1,6 +1,6 @@ # Set of ENVs for Arbitrum One network explorer # https://arbitrum.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=arbitrum" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=arbitrum" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http diff --git a/configs/envs/.env.arbitrum_nova b/configs/envs/.env.arbitrum_nova new file mode 100644 index 0000000000..2888565d7d --- /dev/null +++ b/configs/envs/.env.arbitrum_nova @@ -0,0 +1,42 @@ +# Set of ENVs for Arbitrum One network explorer +# https://arbitrum.blockscout.com +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=arbitrum" + +# 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_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=arbitrum-nova.blockscout.com +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +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_GRAPHIQL_TRANSACTION=0x37c798810d49ba132b40efe7f4fdf6806a8fc58226bb5e185ddc91f896577abf +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(27, 74, 221, 1) +NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true +NEXT_PUBLIC_LOGOUT_URL=https://blockscout-arbitrum.us.auth0.com/v2/logout +NEXT_PUBLIC_MARKETPLACE_ENABLED=false +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ETH +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH +NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/arbitrum/pools'}}] +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/arbitrum-nova-icon.svg +NEXT_PUBLIC_NETWORK_ID=42170 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/arbitrum-nova.svg +NEXT_PUBLIC_NETWORK_NAME=Arbitrum Nova +NEXT_PUBLIC_NETWORK_RPC_URL=https://arbitrum.llamarpc.com +NEXT_PUBLIC_NETWORK_SHORT_NAME=Arbitrum Nova +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/arbitrum-nova.png +NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com +NEXT_PUBLIC_ROLLUP_TYPE=arbitrum +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file diff --git a/configs/envs/.env.base b/configs/envs/.env.base index 0c4a561e6b..8eab44c966 100644 --- a/configs/envs/.env.base +++ b/configs/envs/.env.base @@ -1,6 +1,6 @@ # Set of ENVs for Base Mainnet network explorer # https://base.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=base" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=base" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -11,7 +11,7 @@ NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws # Instance ENVs NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "728301", "width": "728", "height": "90" } -NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "728301", "width": "320", "height": "100" } +NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "728302", "width": "320", "height": "100" } NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_API_BASE_PATH=/ @@ -38,7 +38,7 @@ NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6u NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': '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_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': '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_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 diff --git a/configs/envs/.env.blackfort_testnet b/configs/envs/.env.blackfort_testnet new file mode 100644 index 0000000000..eeaad00054 --- /dev/null +++ b/configs/envs/.env.blackfort_testnet @@ -0,0 +1,44 @@ +# Set of ENVs for BXN Testnet network explorer +# https://blackfort-testnet.blockscout.com +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=blackfort_testnet" + +# 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_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=blackfort-testnet.blockscout.com +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +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/blackfort-testnet.json +NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/blackfort.json +NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xcb4140e22cde3412eb5aecdedf2403032c7a251f5c96b11122aca5b1b88ed953 +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=linear-gradient(92deg, rgb(3, 150, 254) 0.24%, rgb(36, 209, 245) 98.31%) +NEXT_PUBLIC_IS_TESTNET=true +NEXT_PUBLIC_MARKETPLACE_ENABLED=false +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=TBXN +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=TBXN +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/blackfort.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/blackfort-dark.svg +NEXT_PUBLIC_NETWORK_ID=4777 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/blackfort.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/blackfort-dark.svg +NEXT_PUBLIC_NETWORK_NAME=BXN Testnet +NEXT_PUBLIC_NETWORK_RPC_URL=https://testnet.blackfort.network/rpc +NEXT_PUBLIC_NETWORK_SHORT_NAME=BXN Testnet +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/blackfort.png +NEXT_PUBLIC_STATS_API_HOST=https://stats-blackfort-testnet.k8s.blockscout.com +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com +NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=blackfort \ No newline at end of file diff --git a/configs/envs/.env.celo_alfajores b/configs/envs/.env.celo_alfajores index 2cbb790629..d93f9e36ae 100644 --- a/configs/envs/.env.celo_alfajores +++ b/configs/envs/.env.celo_alfajores @@ -1,6 +1,6 @@ # Set of ENVs for Celo Alfajores network explorer # https://celo-alfajores.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=celo_alfajores" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=celo_alfajores" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -9,6 +9,9 @@ NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws +NEXT_PUBLIC_CELO_ENABLED=true +NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=26369280 + # Instance ENVs NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_HOST=celo-alfajores.blockscout.com @@ -36,4 +39,5 @@ NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true -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 +NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','current_epoch'] \ No newline at end of file diff --git a/configs/envs/.env.eth b/configs/envs/.env.eth index 09150ec6a5..293263ce7d 100644 --- a/configs/envs/.env.eth +++ b/configs/envs/.env.eth @@ -1,6 +1,6 @@ # Set of ENVs for Ethereum network explorer # https://eth.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=eth" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=eth" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -20,43 +20,51 @@ NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout 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_DATA_AVAILABILITY_ENABLED=true -NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'cow-swap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}] +NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swapscout','icon':'swap','dappId':'swapscout'},{'text':'Disperse','icon':'txn_batches_slim','dappId':'smol'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}] NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth.json NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/eth-mainnet.json +NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'} NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xd01175f1efa23f36c5579b3c13e2bbd0885017643a7efef5cbcb6b474384dfa8 NEXT_PUBLIC_HAS_BEACON_CHAIN=true +NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=true NEXT_PUBLIC_HAS_USER_OPS=true NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=true NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap'] NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_LOGOUT_URL=https://ethereum-mainnet.us.auth0.com/v2/logout +NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=

Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon!

+NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html +NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=eth NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json NEXT_PUBLIC_MARKETPLACE_ENABLED=true -NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=gearbox-protocol +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': '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_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'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}] NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH -NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ] +NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/eth/pools'}},{'title':'Etherscan','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/etherscan.png','baseUrl':'https://etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}, {'title':'Blockchair','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/blockchair.png','baseUrl':'https://blockchair.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address','token':'/ethereum/erc-20/token','block':'/ethereum/block'}},{'title':'Sentio','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/sentio.png','baseUrl':'https://app.sentio.xyz/','paths':{'tx':'/tx/1','address':'/contract/1'}}, {'title':'Tenderly','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/tenderly.png','baseUrl':'https://dashboard.tenderly.co','paths':{'tx':'/tx/mainnet'}}, {'title':'0xPPL','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/0xPPL.png','baseUrl':'https://0xppl.com','paths':{'tx':'/Ethereum/tx','address':'/','token':'/c/Ethereum'}}, {'title':'3xpl','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/3xpl.png','baseUrl':'https://3xpl.com/','paths':{'tx':'/ethereum/transaction','address':'/ethereum/address'}} ] NEXT_PUBLIC_NETWORK_ID=1 NEXT_PUBLIC_NETWORK_NAME=Ethereum -NEXT_PUBLIC_NETWORK_RPC_URL=https://eth.llamarpc.com +NEXT_PUBLIC_NETWORK_RPC_URL=https://eth.drpc.org NEXT_PUBLIC_NETWORK_SHORT_NAME=Ethereum NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/eth.jpg NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://eth.drpc.org?ref=559183','text':'Public RPC'}] +NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-mainnet.safe.global +NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true -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 +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 \ No newline at end of file diff --git a/configs/envs/.env.eth_sepolia b/configs/envs/.env.eth_sepolia index 1304b37c72..2b54a8faea 100644 --- a/configs/envs/.env.eth_sepolia +++ b/configs/envs/.env.eth_sepolia @@ -1,6 +1,6 @@ # Set of ENVs for Sepolia network explorer # https://eth-sepolia.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=eth_sepolia" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=eth_sepolia" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -19,28 +19,33 @@ NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout 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_DATA_AVAILABILITY_ENABLED=true -NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'cow-swap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'},{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}] +NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'cow-swap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'}] NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363 NEXT_PUBLIC_HAS_BEACON_CHAIN=true NEXT_PUBLIC_HAS_USER_OPS=true NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] -NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(51, 53, 67, 1) -NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(165, 252, 122, 1) +NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(51, 53, 67, 1)'],'text_color':['rgba(165, 252, 122, 1)']} NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout +NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=

Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon!

+NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maikReal/974c47f86a3158c1a86b092ae2f044b3/raw/abcc7e02150cd85d4974503a0357162c0a2c35a9/merits-banner.html +NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://swap.blockscout.com?utm_source=blockscout&utm_medium=eth-sepolia NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json NEXT_PUBLIC_MARKETPLACE_ENABLED=true +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=patbqG4V2CI998jAq.9810c58c9de973ba2650621c94559088cbdfa1a914498e385621ed035d33c0d0 +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': '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_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'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}] NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] +NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH @@ -57,9 +62,11 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}] +NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://merits.blockscout.com NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global NEXT_PUBLIC_SENTRY_ENABLE_TRACING=true NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s.blockscout.com NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true -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 +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 \ No newline at end of file diff --git a/configs/envs/.env.filecoin b/configs/envs/.env.filecoin new file mode 100644 index 0000000000..0f6e05cd25 --- /dev/null +++ b/configs/envs/.env.filecoin @@ -0,0 +1,42 @@ +# Set of ENVs for Filecoin Virtual Machine network explorer +# https://filecoin.blockscout.com +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=filecoin" + +# 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_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=filecoin.blockscout.com +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +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_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/fvm.json +NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x5abb6212c1802402b828ed20c2bd4d4a6153b8bee68a5259cba3c8d7a7c6b775 +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs', 'coin_price', 'market_cap'] +NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=linear-gradient(237deg, rgb(26, 58, 150) 14.83%, rgb(111, 223, 164) 132.56%) +NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(255, 255, 255, 1) +NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true +NEXT_PUBLIC_LOGOUT_URL=https://blockscout-filecoin.us.auth0.com/v2/logout +NEXT_PUBLIC_MARKETPLACE_ENABLED=false +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=FIL +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=FIL +NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/filecoin/pools'}}] +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/filecoin-icon-dark.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/filecoin-icon-light.svg +NEXT_PUBLIC_NETWORK_ID=314 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/filecoin-logo-dark.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/filecoin-logo-light.svg +NEXT_PUBLIC_NETWORK_NAME=Filecoin Virtual Machine +NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.ankr.com/filecoin +NEXT_PUBLIC_NETWORK_SHORT_NAME=Filecoin Virtual Machine +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/filecoin.png +NEXT_PUBLIC_STATS_API_HOST=https://stats-filecoin.k8s-prod-1.blockscout.com +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file diff --git a/configs/envs/.env.garnet b/configs/envs/.env.garnet new file mode 100644 index 0000000000..44720ed11c --- /dev/null +++ b/configs/envs/.env.garnet @@ -0,0 +1,53 @@ +# Set of ENVs for Garnet Testnet network explorer +# https://explorer.garnetchain.com +# This is an auto-generated file. To update all values, run "yarn preset:sync --name=garnet" + +# 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_PROVIDER=none +NEXT_PUBLIC_AD_TEXT_PROVIDER=none +NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=explorer.garnetchain.com +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +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/redstone-testnet.json +NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/redstone.json +NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x5b0ba69f2cf5fbc6da96b6cf475c5521f7a385efd9d68673f69c1fc54f737a52 +NEXT_PUBLIC_HAS_MUD_FRAMEWORK=true +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgb(169, 31, 47)']} +NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true +NEXT_PUBLIC_IS_TESTNET=true +NEXT_PUBLIC_LOGOUT_URL=https://redstone-lattice.us.auth0.com/v2/logout +NEXT_PUBLIC_MARKETPLACE_ENABLED=false +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/garnet.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/garnet-dark.svg +NEXT_PUBLIC_NETWORK_ID=17069 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/garnet.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/garnet-dark.svg +NEXT_PUBLIC_NETWORK_NAME=Garnet Testnet +NEXT_PUBLIC_NETWORK_RPC_URL=https://partner-rpc.garnetchain.com/tireless-strand-dreamt-overcome +NEXT_PUBLIC_NETWORK_SHORT_NAME=Garnet Testnet +NEXT_PUBLIC_OG_DESCRIPTION=Redstone is the home for onchain games, worlds, and other MUD applications +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/garnet.png +NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-holesky.blockscout.com/ +NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://garnet.qry.live/withdraw +NEXT_PUBLIC_ROLLUP_TYPE=optimistic +NEXT_PUBLIC_STATS_API_HOST=https://stats-redstone-garnet.k8s.blockscout.com +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file diff --git a/configs/envs/.env.gnosis b/configs/envs/.env.gnosis index fad2dde745..7e1d1e6993 100644 --- a/configs/envs/.env.gnosis +++ b/configs/envs/.env.gnosis @@ -1,6 +1,6 @@ # Set of ENVs for Gnosis chain network explorer # https://gnosis.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=gnosis" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=gnosis" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -42,9 +42,10 @@ NEXT_PUBLIC_MARKETPLACE_ENABLED=true NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form +NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/refs/heads/marketplace-graph-test/test-configs/marketplace-graph-links.json NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': '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_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': '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_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 diff --git a/configs/envs/.env.jest b/configs/envs/.env.jest index e1f80c7e75..7de7ee11fc 100644 --- a/configs/envs/.env.jest +++ b/configs/envs/.env.jest @@ -24,7 +24,6 @@ NEXT_PUBLIC_API_BASE_PATH=/ # ui config ## homepage NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] -NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND= ## sidebar NEXT_PUBLIC_NETWORK_LOGO= @@ -50,4 +49,4 @@ NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 -NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx +NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx diff --git a/configs/envs/.env.main b/configs/envs/.env.main index d33f7ec621..0568827c78 100644 --- a/configs/envs/.env.main +++ b/configs/envs/.env.main @@ -1,6 +1,6 @@ # Set of ENVs for Sepolia network explorer # https://eth-sepolia.k8s-dev.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=main" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=main" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -39,7 +39,7 @@ NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6u NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata-test.k8s-dev.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true -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_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_NAME_SERVICE_API_HOST=https://bens-rs-test.k8s-dev.blockscout.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/blocks','/apps'] NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal diff --git a/configs/envs/.env.optimism b/configs/envs/.env.optimism index f23300e95f..8fd9911afc 100644 --- a/configs/envs/.env.optimism +++ b/configs/envs/.env.optimism @@ -1,6 +1,6 @@ # Set of ENVs for OP Mainnet network explorer # https://optimism.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=optimism" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=optimism" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -39,7 +39,7 @@ NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6u NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': '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_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': '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_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 diff --git a/configs/envs/.env.optimism_celestia b/configs/envs/.env.optimism_celestia new file mode 100644 index 0000000000..650975e1c1 --- /dev/null +++ b/configs/envs/.env.optimism_celestia @@ -0,0 +1,46 @@ +# Set of ENVs for OP Celestia Raspberry network explorer +# https://opcelestia-raspberry.gelatoscout.com +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=optimism_celestia" + +# 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_ADBUTLER_CONFIG_DESKTOP={'id':'721628','width':'728','height':'90'} +NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={'id':'721627','width':'300','height':'100'} +NEXT_PUBLIC_AD_BANNER_PROVIDER=adbutler +NEXT_PUBLIC_AD_TEXT_PROVIDER=none +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=opcelestia-raspberry.gelatoscout.com +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +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_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/opcelestia-raspberry.json +NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x0f5b54de81848d8d8baa02c69030037218a2b4df622d64a2a429e11721606656 +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(255, 0, 0, 1) +NEXT_PUBLIC_IS_TESTNET=true +NEXT_PUBLIC_MARKETPLACE_ENABLED=false +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +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/optimism.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/optimism.svg +NEXT_PUBLIC_NETWORK_ID=123420111 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/optimism.svg +NEXT_PUBLIC_NETWORK_NAME=OP Celestia Raspberry +NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.opcelestia-raspberry.gelato.digital +NEXT_PUBLIC_NETWORK_SHORT_NAME=opcelestia-raspberry +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-sepolia.blockscout.com/ +NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://bridge.gelato.network/bridge/opcelestia-raspberry +NEXT_PUBLIC_ROLLUP_TYPE=optimistic +NEXT_PUBLIC_STATS_API_HOST=https://stats-opcelestia-raspberry.k8s.blockscout.com +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com +NEXT_PUBLIC_WEB3_WALLETS=none \ No newline at end of file diff --git a/configs/envs/.env.optimism_sepolia b/configs/envs/.env.optimism_sepolia index 8636924480..146b064965 100644 --- a/configs/envs/.env.optimism_sepolia +++ b/configs/envs/.env.optimism_sepolia @@ -1,6 +1,6 @@ # Set of ENVs for OP Sepolia network explorer # https://optimism-sepolia.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=optimism_sepolia" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=optimism_sepolia" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http diff --git a/configs/envs/.env.polygon b/configs/envs/.env.polygon index 4fe0631ac0..ddd94291fc 100644 --- a/configs/envs/.env.polygon +++ b/configs/envs/.env.polygon @@ -1,6 +1,6 @@ # Set of ENVs for Polygon Mainnet network explorer # https://polygon.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=polygon" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=polygon" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http diff --git a/configs/envs/.env.pw b/configs/envs/.env.pw index 7840c51b42..e7be38baea 100644 --- a/configs/envs/.env.pw +++ b/configs/envs/.env.pw @@ -25,7 +25,6 @@ NEXT_PUBLIC_API_BASE_PATH=/ # ui config ## homepage NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] -NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true ## sidebar ## footer NEXT_PUBLIC_GIT_TAG=v1.0.11 @@ -52,5 +51,9 @@ NEXT_PUBLIC_STATS_API_HOST=http://localhost:3004 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=http://localhost:3005 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=http://localhost:3006 NEXT_PUBLIC_METADATA_SERVICE_API_HOST=http://localhost:3007 -NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx +NEXT_PUBLIC_NAME_SERVICE_API_HOST=http://localhost:3008 +NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx +NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16','bech32'] +NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX=tom +NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED=false \ No newline at end of file diff --git a/configs/envs/.env.rootstock_testnet b/configs/envs/.env.rootstock_testnet index 2c3f426912..06368491ef 100644 --- a/configs/envs/.env.rootstock_testnet +++ b/configs/envs/.env.rootstock_testnet @@ -1,6 +1,6 @@ # Set of ENVs for Rootstock Testnet network explorer # https://rootstock-testnet.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=rootstock_testnet" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=rootstock_testnet" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -42,4 +42,5 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward','nonce'] NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true -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 +NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','btc_locked'] \ No newline at end of file diff --git a/configs/envs/.env.shibarium b/configs/envs/.env.shibarium new file mode 100644 index 0000000000..dde91dbeb8 --- /dev/null +++ b/configs/envs/.env.shibarium @@ -0,0 +1,64 @@ +# Set of ENVs for Shibarium network explorer +# https://www.shibariumscan.io +# This is an auto-generated file. To update all values, run "yarn preset:sync --name=shibarium" + +# 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_ADBUTLER_CONFIG_DESKTOP={ "id": "727504", "width": "728", "height": "90" } +NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "727507", "width": "320", "height": "100" } +NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler +NEXT_PUBLIC_AD_BANNER_PROVIDER=hype +NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=www.shibariumscan.io +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +NEXT_PUBLIC_APP_INSTANCE=shibarium_mainnet +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_DEFI_DROPDOWN_ITEMS=[{'text':'Get gas','icon':'gas','dappId':'smol-refuel'}] +NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/shibarium-mainnet.json +NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&disableBridges=true', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'} +NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xce531d29c0c469fb00b443b8091b8c059b4f13d7e025dd0ef843401d02b9a1a9 +NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=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(180deg, rgba(224, 111, 44, 1) 0%, rgba(228, 144, 52, 1) 100%)'],'text_color':['rgba(255, 255, 255, 1)']} +NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true +NEXT_PUBLIC_LOGOUT_URL=https://shibarium.us.auth0.com/v2/logout +NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE=

Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon!

+NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json +NEXT_PUBLIC_MARKETPLACE_ENABLED=true +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=patbqG4V2CI998jAq.9810c58c9de973ba2650621c94559088cbdfa1a914498e385621ed035d33c0d0 +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs +NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json +NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL +NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=BONE +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=BONE +NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/shibarium/pools'}}] +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/shibarium-short.png +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/shibarium-short.png +NEXT_PUBLIC_NETWORK_ID=109 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/shibarium-light.png +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/shibarium-dark.png +NEXT_PUBLIC_NETWORK_NAME=Shibarium +NEXT_PUBLIC_NETWORK_RPC_URL=https://www.shibrpc.com +NEXT_PUBLIC_NETWORK_SHORT_NAME=Shibarium +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/shibarium-mainnet.png +NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com/ +NEXT_PUBLIC_ROLLUP_TYPE=shibarium +NEXT_PUBLIC_STATS_API_HOST=https://stats-shibarium-mainnet.k8s-prod-2.blockscout.com +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['miner'] +NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file diff --git a/configs/envs/.env.stability_testnet b/configs/envs/.env.stability_testnet index c057fda27c..e96ca8462d 100644 --- a/configs/envs/.env.stability_testnet +++ b/configs/envs/.env.stability_testnet @@ -1,6 +1,6 @@ # Set of ENVs for Stability Testnet network explorer # https://stability-testnet.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=stability_testnet" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=stability_testnet" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http diff --git a/configs/envs/.env.zilliqa_prototestnet b/configs/envs/.env.zilliqa_prototestnet new file mode 100644 index 0000000000..cc40958ae8 --- /dev/null +++ b/configs/envs/.env.zilliqa_prototestnet @@ -0,0 +1,41 @@ +# Set of ENVs for Zilliqa EVM proto-testnet network explorer +# https://zilliqa-prototestnet.blockscout.com +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=zilliqa_prototestnet" + +# 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 + +NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['bech32','base16'] +NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX=zil +NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['base_fee'] + +# Instance ENVs +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=zilliqa-prototestnet.blockscout.com +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +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_GRAPHIQL_TRANSACTION=0x3d1ded3a7924cd3256a4b1a447c9bfb194f54b9a8ceb441edb8bb01563b516db +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['linear-gradient(90deg, rgba(52,103, 109, 1) 0.06%, rgba(105, 181, 172, 1) 99.97%)'],'text_color':['rgba(255, 255, 255, 1)']} +NEXT_PUBLIC_IS_TESTNET=true +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ZIL +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ZIL +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/zilliqa.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/zilliqa-dark.svg +NEXT_PUBLIC_NETWORK_ID=33103 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/zilliqa.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/zilliqa-dark.svg +NEXT_PUBLIC_NETWORK_NAME=Zilliqa EVM proto-testnet +NEXT_PUBLIC_NETWORK_RPC_URL=https://api.zq2-prototestnet.zilliqa.com +NEXT_PUBLIC_NETWORK_SHORT_NAME=Zilliqa EVM proto-testnet +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/zilliqa.png +NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=6Ld0iT8aAAAAAJdju0CmAwGjW7JTDvIw-Q5pwt5T +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com \ No newline at end of file diff --git a/configs/envs/.env.zkevm b/configs/envs/.env.zkevm index 8b7dc2e566..65db320a6b 100644 --- a/configs/envs/.env.zkevm +++ b/configs/envs/.env.zkevm @@ -1,6 +1,6 @@ # Set of ENVs for Polygon zkEVM network explorer # https://zkevm.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=zkevm" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=zkevm" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http diff --git a/configs/envs/.env.zksync b/configs/envs/.env.zksync index 56e104488b..f7b18c0548 100644 --- a/configs/envs/.env.zksync +++ b/configs/envs/.env.zksync @@ -1,6 +1,6 @@ # Set of ENVs for ZkSync Era network explorer # https://zksync.blockscout.com -# This is an auto-generated file. To update all values, run "yarn preset:sync --name=zksync" +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=zksync" # Local ENVs NEXT_PUBLIC_APP_PROTOCOL=http @@ -10,6 +10,8 @@ NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws # Instance ENVs +NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none + NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_HOST=zksync.blockscout.com @@ -25,7 +27,7 @@ NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_LOGOUT_URL=https://zksync.us.auth0.com/v2/logout NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METASUITES_ENABLED=true -NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': '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_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': '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_NETWORK_CURRENCY_DECIMALS=18 NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ETH NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH diff --git a/configs/envs/.env.zora b/configs/envs/.env.zora new file mode 100644 index 0000000000..de5a551bd1 --- /dev/null +++ b/configs/envs/.env.zora @@ -0,0 +1,60 @@ +# Set of ENVs for Zora Mainnet network explorer +# https://explorer.zora.energy +# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=zora" + +# 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_PROVIDER=none +NEXT_PUBLIC_AD_TEXT_PROVIDER=none +NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_API_HOST=explorer.zora.energy +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml +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_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'uniswap'}] +NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/zora.json +NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x6d54c0226a57f5bc854f8aa589bb15113388f984f318c9e1b2722115e4e35873 +NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS=true +NEXT_PUBLIC_HAS_USER_OPS=true +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=linear-gradient(89deg, rgb(63, 36, 22) 0.56%, rgb(44, 56, 105) 98.31%) +NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true +NEXT_PUBLIC_LOGOUT_URL=https://zora-blockscout.us.auth0.com/v2/logout +NEXT_PUBLIC_MARKETPLACE_ENABLED=true +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=patbqG4V2CI998jAq.9810c58c9de973ba2650621c94559088cbdfa1a914498e385621ed035d33c0d0 +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs +NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL +NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form +NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com +NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com +NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/apps'] +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH +NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'GeckoTerminal','logo':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/explorer-logos/geckoterminal.png','baseUrl':'https://www.geckoterminal.com/','paths':{'token':'/zora-network/pools'}}] +NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/zora.svg +NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/zora-dark.svg +NEXT_PUBLIC_NETWORK_ID=7777777 +NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/zora.svg +NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/zora-dark.svg +NEXT_PUBLIC_NETWORK_NAME=Zora Mainnet +NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.zora.energy +NEXT_PUBLIC_NETWORK_SHORT_NAME=Zora Mainnet +NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation +NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true +NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/zora-mainnet.png +NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com/ +NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://bridge.zora.energy +NEXT_PUBLIC_ROLLUP_TYPE=optimistic +NEXT_PUBLIC_STATS_API_HOST=https://stats-l2-zora-mainnet.k8s-prod-1.blockscout.com +NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout +NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com +NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'dapp_id': '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_ADDRESS_USERNAME_TAG={'api_url_template': 'https://api.zora.co/discover/user/{address}', 'tag_link_template': 'httpszora.co/{username}', 'tag_icon': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/zora.svg', 'tag_bg_color': 'rgba(0,0,0)', 'tag_text_color': 'rgba(255,255,255)'} diff --git a/deploy/helmfile.yaml b/deploy/helmfile.yaml index bad95f0a82..6052eda969 100644 --- a/deploy/helmfile.yaml +++ b/deploy/helmfile.yaml @@ -33,7 +33,7 @@ releases: type: kubernetes.io/dockerconfigjson - name: bs-stack chart: blockscout/blockscout-stack - version: 1.2.* + version: 1.*.* namespace: review-l2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} labels: app: review-l2-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} @@ -58,7 +58,7 @@ releases: type: kubernetes.io/dockerconfigjson - name: bs-stack chart: blockscout/blockscout-stack - version: 1.2.* + version: 1.*.* namespace: review-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} labels: app: review-{{ requiredEnv "GITHUB_REF_NAME_SLUG" }} diff --git a/deploy/scripts/download_assets.sh b/deploy/scripts/download_assets.sh index 747b7269c9..f53bbb2bea 100755 --- a/deploy/scripts/download_assets.sh +++ b/deploy/scripts/download_assets.sh @@ -18,6 +18,7 @@ ASSETS_ENVS=( "NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL" "NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL" "NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL" + "NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL" "NEXT_PUBLIC_FEATURED_NETWORKS" "NEXT_PUBLIC_FOOTER_LINKS" "NEXT_PUBLIC_NETWORK_LOGO" @@ -49,10 +50,14 @@ get_target_filename() { # Extract the extension from the filename local extension="${filename##*.}" else - # Remove query parameters from the URL and get the filename - local filename=$(basename "${url%%\?*}") - # Extract the extension from the filename - local extension="${filename##*.}" + if [[ "$url" == http* ]]; then + # Remove query parameters from the URL and get the filename + local filename=$(basename "${url%%\?*}") + # Extract the extension from the filename + local extension="${filename##*.}" + else + local extension="json" + fi fi # Convert the extension to lowercase @@ -80,16 +85,31 @@ download_and_save_asset() { # Copy the local file to the destination cp "${url#file://}" "$destination" else - # Download the asset using curl - curl -s -o "$destination" "$url" + # Check if the value is a URL + if [[ "$url" == http* ]]; then + # Download the asset using curl + curl -s -o "$destination" "$url" + else + # Convert single-quoted JSON-like content to valid JSON + json_content=$(echo "${!env_var}" | sed "s/'/\"/g") + + # Save the JSON content to a file + echo "$json_content" > "$destination" + fi + fi + + if [[ "$url" == file://* ]] || [[ "$url" == http* ]]; then + local source_name=$url + else + local source_name="raw input" fi # Check if the download was successful if [ $? -eq 0 ]; then - echo " [+] $env_var: Successfully saved file from $url to $destination." + echo " [+] $env_var: Successfully saved file from $source_name to $destination." return 0 else - echo " [-] $env_var: Failed to save file from $url." + echo " [-] $env_var: Failed to save file from $source_name." return 1 fi } diff --git a/deploy/scripts/entrypoint.sh b/deploy/scripts/entrypoint.sh index 298303c7a0..ac1f91d6c5 100755 --- a/deploy/scripts/entrypoint.sh +++ b/deploy/scripts/entrypoint.sh @@ -39,9 +39,14 @@ export_envs_from_preset ./download_assets.sh ./public/assets/configs # Check run-time ENVs values -./validate_envs.sh -if [ $? -ne 0 ]; then - exit 1 +if [ "$SKIP_ENVS_VALIDATION" != "true" ]; then + ./validate_envs.sh + if [ $? -ne 0 ]; then + exit 1 + fi +else + echo "😱 Skipping ENVs validation." + echo fi # Generate favicons bundle diff --git a/deploy/scripts/favicon_generator.sh b/deploy/scripts/favicon_generator.sh index 0cfc9f7f8a..394e901b4e 100755 --- a/deploy/scripts/favicon_generator.sh +++ b/deploy/scripts/favicon_generator.sh @@ -4,7 +4,7 @@ master_url="${FAVICON_MASTER_URL:-$NEXT_PUBLIC_NETWORK_ICON}" export MASTER_URL="$master_url" cd ./deploy/tools/favicon-generator -./script.sh +node "$(dirname "$0")/index.js" if [ $? -ne 0 ]; then cd ../../../ exit 1 diff --git a/deploy/scripts/make_envs_script.sh b/deploy/scripts/make_envs_script.sh index 0634296c45..b3548cb207 100755 --- a/deploy/scripts/make_envs_script.sh +++ b/deploy/scripts/make_envs_script.sh @@ -18,6 +18,12 @@ echo "window.__envs = {" >> $output_file; # Iterate through all environment variables for var in $(env | grep '^NEXT_PUBLIC_' | cut -d= -f1); do + # Skip variables that start with NEXT_PUBLIC_VERCEL. Vercel injects these + # and they can cause runtime errors, particularly when commit messages wrap lines. + if [[ $var == NEXT_PUBLIC_VERCEL* ]]; then + continue + fi + # Get the value of the variable value="${!var}" diff --git a/deploy/tools/affected-tests/yarn.lock b/deploy/tools/affected-tests/yarn.lock index 385918d425..bd755eda2b 100644 --- a/deploy/tools/affected-tests/yarn.lock +++ b/deploy/tools/affected-tests/yarn.lock @@ -90,7 +90,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2: +braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -441,11 +441,11 @@ merge2@^1.3.0, merge2@^1.4.1: integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" minimatch@^3.1.1: @@ -582,9 +582,9 @@ requirejs-config-file@^4.0.0: stringify-object "^3.2.1" requirejs@^2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" - integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg== + version "2.3.7" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.7.tgz#0b22032e51a967900e0ae9f32762c23a87036bd0" + integrity "sha1-CyIDLlGpZ5AOCunzJ2LCOocDa9A= sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==" resolve-dependency-path@^3.0.2: version "3.0.2" diff --git a/deploy/tools/envs-validator/index.ts b/deploy/tools/envs-validator/index.ts index 9770ea6399..fe7354f7a7 100644 --- a/deploy/tools/envs-validator/index.ts +++ b/deploy/tools/envs-validator/index.ts @@ -20,7 +20,9 @@ async function run() { return result; }, {} as Record); + printDeprecationWarning(appEnvs); await checkPlaceholdersCongruity(appEnvs); + checkDeprecatedEnvs(appEnvs); await validateEnvs(appEnvs); } catch (error) { @@ -38,6 +40,7 @@ async function validateEnvs(appEnvs: Record) { 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL', 'NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', + 'NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL', 'NEXT_PUBLIC_FOOTER_LINKS', ]; @@ -135,3 +138,45 @@ function getEnvsPlaceholders(filePath: string): Promise> { }); }); } + +function printDeprecationWarning(envsMap: Record) { + if (envsMap.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY && envsMap.NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY) { + console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗'); + // eslint-disable-next-line max-len + console.warn('The NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY variables are now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY variable.'); + console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n'); + } + + if ( + envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR || + envsMap.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND + ) { + console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗'); + // eslint-disable-next-line max-len + console.warn('The NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR and NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND variables are now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG variable.'); + console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n'); + } + + if ( + envsMap.NEXT_PUBLIC_AUTH0_CLIENT_ID || + envsMap.NEXT_PUBLIC_AUTH_URL || + envsMap.NEXT_PUBLIC_LOGOUT_URL + ) { + console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗'); + // eslint-disable-next-line max-len + console.warn('The NEXT_PUBLIC_AUTH0_CLIENT_ID, NEXT_PUBLIC_AUTH_URL and NEXT_PUBLIC_LOGOUT_URL variables are now deprecated and will be removed in the next release.'); + console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n'); + } +} + +function checkDeprecatedEnvs(envsMap: Record) { + !silent && console.log(`🌀 Checking deprecated environment variables...`); + + if (envsMap.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY && !envsMap.NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY) { + // eslint-disable-next-line max-len + console.log('🚨 The NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY variable is no longer supported. Please pass NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY or remove it completely.'); + throw new Error(); + } + + !silent && console.log('👍 All good!\n'); +} diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index 15f6a40e9e..80901a8624 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ declare module 'yup' { interface StringSchema { // Yup's URL validator is not perfect so we made our own @@ -9,10 +10,12 @@ declare module 'yup' { import * as yup from 'yup'; import type { AdButlerConfig } from '../../../types/client/adButlerConfig'; +import type { AddressProfileAPIConfig } from '../../../types/client/addressProfileAPIConfig'; import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS, SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS } from '../../../types/client/adProviders'; import type { AdTextProviders, AdBannerProviders, AdBannerAdditionalProviders } from '../../../types/client/adProviders'; -import type { ContractCodeIde } from '../../../types/client/contract'; +import { SMART_CONTRACT_EXTRA_VERIFICATION_METHODS, type ContractCodeIde, type SmartContractVerificationMethodExtra } from '../../../types/client/contract'; import type { DeFiDropdownItem } from '../../../types/client/deFiDropdown'; +import type { GasRefuelProviderConfig } from '../../../types/client/gasRefuelProviderConfig'; import { GAS_UNITS } from '../../../types/client/gasTracker'; import type { GasUnit } from '../../../types/client/gasTracker'; import type { MarketplaceAppOverview, MarketplaceAppSecurityReportRaw, MarketplaceAppSecurityReport } from '../../../types/client/marketplace'; @@ -27,12 +30,13 @@ import type { ValidatorsChainType } from '../../../types/client/validators'; import type { WalletType } from '../../../types/client/wallets'; import { SUPPORTED_WALLETS } from '../../../types/client/wallets'; import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks'; -import { CHAIN_INDICATOR_IDS } from '../../../types/homepage'; -import type { ChainIndicatorId } from '../../../types/homepage'; -import { type NetworkVerificationType, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS, type NearNetworkType } from '../../../types/networks'; +import { CHAIN_INDICATOR_IDS, HOME_STATS_WIDGET_IDS } from '../../../types/homepage'; +import type { ChainIndicatorId, HeroBannerButtonState, HeroBannerConfig, HomeStatsWidgetId } from '../../../types/homepage'; +import { type NetworkVerificationTypeEnvs, type NetworkExplorer, type FeaturedNetwork, NETWORK_GROUPS, type NearNetworkType } from '../../../types/networks'; import { COLOR_THEME_IDS } from '../../../types/settings'; -import type { AddressViewId } from '../../../types/views/address'; -import { ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from '../../../types/views/address'; +import type { FontFamily } from '../../../types/ui'; +import type { AddressFormat, AddressViewId } from '../../../types/views/address'; +import { ADDRESS_FORMATS, ADDRESS_VIEWS_IDS, IDENTICON_TYPES } from '../../../types/views/address'; import { BLOCK_FIELDS_IDS } from '../../../types/views/block'; import type { BlockFieldId } from '../../../types/views/block'; import type { NftMarketplaceItem } from '../../../types/views/nft'; @@ -223,6 +227,30 @@ const marketplaceSchema = yup // eslint-disable-next-line max-len otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL cannot not be used without NEXT_PUBLIC_MARKETPLACE_ENABLED'), }), + NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY: yup + .string() + .when('NEXT_PUBLIC_MARKETPLACE_ENABLED', { + is: true, + then: (schema) => schema, + // eslint-disable-next-line max-len + otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY cannot not be used without NEXT_PUBLIC_MARKETPLACE_ENABLED'), + }), + NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID: yup + .string() + .when('NEXT_PUBLIC_MARKETPLACE_ENABLED', { + is: true, + then: (schema) => schema, + // eslint-disable-next-line max-len + otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID cannot not be used without NEXT_PUBLIC_MARKETPLACE_ENABLED'), + }), + NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL: yup + .string() + .when('NEXT_PUBLIC_MARKETPLACE_ENABLED', { + is: true, + then: (schema) => schema, + // eslint-disable-next-line max-len + otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL cannot not be used without NEXT_PUBLIC_MARKETPLACE_ENABLED'), + }), }); const beaconChainSchema = yup @@ -259,6 +287,55 @@ const rollupSchema = yup then: (schema) => schema.test(urlTest).required(), otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL can be used only if NEXT_PUBLIC_ROLLUP_TYPE is set to \'optimistic\' '), }), + NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED: yup + .boolean() + .when('NEXT_PUBLIC_ROLLUP_TYPE', { + is: 'optimistic', + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED can only be used if NEXT_PUBLIC_ROLLUP_TYPE is set to \'optimistic\' ', + value => value === undefined, + ), + }), + NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME: yup + .string() + .when('NEXT_PUBLIC_ROLLUP_TYPE', { + is: 'arbitrum', + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME can only be used if NEXT_PUBLIC_ROLLUP_TYPE is set to \'arbitrum\' ', + value => value === undefined, + ), + }), + NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS: yup + .boolean() + .when('NEXT_PUBLIC_ROLLUP_TYPE', { + is: (value: string) => value, + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS cannot not be used if NEXT_PUBLIC_ROLLUP_TYPE is not defined', + value => value === undefined, + ), + }), + }); + +const celoSchema = yup + .object() + .shape({ + NEXT_PUBLIC_CELO_ENABLED: yup.boolean(), + NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK: yup + .string() + .when('NEXT_PUBLIC_CELO_ENABLED', { + is: (value: boolean) => value, + then: (schema) => schema.min(0).optional(), + otherwise: (schema) => schema.max( + -1, + 'NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK cannot not be used if NEXT_PUBLIC_CELO_ENABLED is not set to "true"', + ), + }), }); const adButlerConfigSchema = yup @@ -336,7 +413,7 @@ const accountSchema = yup .string() .when('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED', { is: (value: boolean) => value, - then: (schema) => schema.required(), + then: (schema) => schema, otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_AUTH0_CLIENT_ID cannot not be used if NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED is not set to "true"'), }), NEXT_PUBLIC_AUTH_URL: yup @@ -350,7 +427,7 @@ const accountSchema = yup .string() .when('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED', { is: (value: boolean) => value, - then: (schema) => schema.test(urlTest).required(), + then: (schema) => schema.test(urlTest), otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_LOGOUT_URL cannot not be used if NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED is not set to "true"'), }), }); @@ -372,6 +449,34 @@ const navItemExternalSchema: yup.ObjectSchema = yup url: yup.string().test(urlTest).required(), }); +const fontFamilySchema: yup.ObjectSchema = yup + .object() + .transform(replaceQuotes) + .json() + .shape({ + name: yup.string().required(), + url: yup.string().test(urlTest).required(), + }); + +const heroBannerButtonStateSchema: yup.ObjectSchema = yup.object({ + background: yup.array().max(2).of(yup.string()), + text_color: yup.array().max(2).of(yup.string()), +}); + +const heroBannerSchema: yup.ObjectSchema = yup.object() + .transform(replaceQuotes) + .json() + .shape({ + background: yup.array().max(2).of(yup.string()), + text_color: yup.array().max(2).of(yup.string()), + border: yup.array().max(2).of(yup.string()), + button: yup.object({ + _default: heroBannerButtonStateSchema, + _hover: heroBannerButtonStateSchema, + _selected: heroBannerButtonStateSchema, + }), + }); + const footerLinkSchema: yup.ObjectSchema = yup .object({ text: yup.string().required(), @@ -463,6 +568,13 @@ const deFiDropdownItemSchema: yup.ObjectSchema = yup return Boolean(value.dappId) || Boolean(value.url); }) as yup.ObjectSchema; +const multichainProviderConfigSchema: yup.ObjectSchema = yup.object({ + name: yup.string().required(), + url_template: yup.string().required(), + logo: yup.string().required(), + dapp_id: yup.string(), +}); + const schema = yup .object() .noUnknown(true, (params) => { @@ -492,7 +604,17 @@ const schema = yup NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS: yup.number().integer().positive(), NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL: yup.string(), NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES: yup.boolean(), - NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: yup.string().oneOf([ 'validation', 'mining' ]), + NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: yup + .string().oneOf([ 'validation', 'mining' ]) + .when('NEXT_PUBLIC_ROLLUP_TYPE', { + is: (value: string) => value === 'arbitrum' || value === 'zkEvm', + then: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE can not be set for Arbitrum and ZkEVM rollups', + value => value === undefined, + ), + otherwise: (schema) => schema, + }), NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME: yup.string(), NEXT_PUBLIC_IS_TESTNET: yup.boolean(), NEXT_PUBLIC_NEAR_NETWORK: yup.string().oneOf([ 'mainnet', 'testnet' ]), @@ -511,9 +633,30 @@ const schema = yup .transform(replaceQuotes) .json() .of(yup.string().oneOf(CHAIN_INDICATOR_IDS)), + NEXT_PUBLIC_HOMEPAGE_STATS: yup + .array() + .transform(replaceQuotes) + .json() + .of(yup.string().oneOf(HOME_STATS_WIDGET_IDS)), NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(), NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(), - NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME: yup.boolean(), + NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG: yup + .mixed() + .test( + 'shape', + (ctx) => { + try { + heroBannerSchema.validateSync(ctx.originalValue); + throw new Error('Unknown validation error'); + } catch (error: unknown) { + const message = typeof error === 'object' && error !== null && 'errors' in error && Array.isArray(error.errors) ? error.errors.join(', ') : ''; + return 'Invalid schema were provided for NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG' + (message ? `: ${ message }` : ''); + } + }, + (data) => { + const isUndefined = data === undefined; + return isUndefined || heroBannerSchema.isValidSync(data); + }), // b. sidebar NEXT_PUBLIC_FEATURED_NETWORKS: yup @@ -554,12 +697,40 @@ const schema = yup .json() .of(yup.string().oneOf(BLOCK_FIELDS_IDS)), NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE: yup.string().oneOf(IDENTICON_TYPES), + NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT: yup + .array() + .transform(replaceQuotes) + .json() + .of(yup.string().oneOf(ADDRESS_FORMATS)), + NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX: yup + .string() + .when('NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT', { + is: (value: Array | undefined) => value && value.includes('bech32'), + then: (schema) => schema.required().min(1).max(83), + otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX is required if NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT contains "bech32"'), + }), + NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS: yup .array() .transform(replaceQuotes) .json() .of(yup.string().oneOf(ADDRESS_VIEWS_IDS)), NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED: yup.boolean(), + NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS: yup + .mixed() + .test( + 'shape', + 'Invalid schema were provided for NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS, it should be either array of method ids or "none" string literal', + (data) => { + const isNoneSchema = yup.string().oneOf([ 'none' ]); + const isArrayOfMethodsSchema = yup + .array() + .transform(replaceQuotes) + .json() + .of(yup.string().oneOf(SMART_CONTRACT_EXTRA_VERIFICATION_METHODS)); + + return isNoneSchema.isValidSync(data) || isArrayOfMethodsSchema.isValidSync(data); + }), NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS: yup .array() .transform(replaceQuotes) @@ -575,6 +746,7 @@ const schema = yup .transform(replaceQuotes) .json() .of(nftMarketplaceSchema), + NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED: yup.boolean(), // e. misc NEXT_PUBLIC_NETWORK_EXPLORERS: yup @@ -592,9 +764,29 @@ const schema = yup NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS: yup.boolean(), NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE: yup.string(), NEXT_PUBLIC_COLOR_THEME_DEFAULT: yup.string().oneOf(COLOR_THEME_IDS), + NEXT_PUBLIC_FONT_FAMILY_HEADING: yup + .mixed() + .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_FONT_FAMILY_HEADING', (data) => { + const isUndefined = data === undefined; + return isUndefined || fontFamilySchema.isValidSync(data); + }), + NEXT_PUBLIC_FONT_FAMILY_BODY: yup + .mixed() + .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_FONT_FAMILY_BODY', (data) => { + const isUndefined = data === undefined; + return isUndefined || fontFamilySchema.isValidSync(data); + }), + NEXT_PUBLIC_MAX_CONTENT_WIDTH_ENABLED: yup.boolean(), // 5. Features configuration - NEXT_PUBLIC_API_SPEC_URL: yup.string().test(urlTest), + NEXT_PUBLIC_API_SPEC_URL: yup + .mixed() + .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_API_SPEC_URL, it should be either URL-string or "none" string literal', (data) => { + const isNoneSchema = yup.string().oneOf([ 'none' ]); + const isUrlStringSchema = yup.string().test(urlTest); + + return isNoneSchema.isValidSync(data) || isUrlStringSchema.isValidSync(data); + }), NEXT_PUBLIC_STATS_API_HOST: yup.string().test(urlTest), NEXT_PUBLIC_STATS_API_BASE_PATH: yup.string(), NEXT_PUBLIC_VISUALIZE_API_HOST: yup.string().test(urlTest), @@ -603,7 +795,14 @@ const schema = yup NEXT_PUBLIC_NAME_SERVICE_API_HOST: yup.string().test(urlTest), NEXT_PUBLIC_METADATA_SERVICE_API_HOST: yup.string().test(urlTest), NEXT_PUBLIC_ADMIN_SERVICE_API_HOST: yup.string().test(urlTest), - NEXT_PUBLIC_GRAPHIQL_TRANSACTION: yup.string().matches(regexp.HEX_REGEXP), + NEXT_PUBLIC_GRAPHIQL_TRANSACTION: yup + .mixed() + .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_GRAPHIQL_TRANSACTION, it should be either Hex-string or "none" string literal', (data) => { + const isNoneSchema = yup.string().oneOf([ 'none' ]); + const isHashStringSchema = yup.string().matches(regexp.HEX_REGEXP); + + return isNoneSchema.isValidSync(data) || isHashStringSchema.isValidSync(data); + }), NEXT_PUBLIC_WEB3_WALLETS: yup .mixed() .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_WEB3_WALLETS, it should be either array or "none" string literal', (data) => { @@ -629,10 +828,15 @@ const schema = yup NEXT_PUBLIC_HAS_USER_OPS: yup.boolean(), NEXT_PUBLIC_METASUITES_ENABLED: yup.boolean(), NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG: yup + .array() + .transform(replaceQuotes) + .json() + .of(multichainProviderConfigSchema), + NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG: yup .mixed() - .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG, it should have name and url template', (data) => { + .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG, it should have name and url template', (data) => { const isUndefined = data === undefined; - const valueSchema = yup.object().transform(replaceQuotes).json().shape({ + const valueSchema = yup.object().transform(replaceQuotes).json().shape({ name: yup.string().required(), url_template: yup.string().required(), logo: yup.string(), @@ -660,10 +864,38 @@ const schema = yup value => value === undefined, ), }), + NEXT_PUBLIC_HAS_MUD_FRAMEWORK: yup.boolean() + .when('NEXT_PUBLIC_ROLLUP_TYPE', { + is: 'optimistic', + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_HAS_MUD_FRAMEWORK can only be used with NEXT_PUBLIC_ROLLUP_TYPE=optimistic', + value => value === undefined, + ), + }), + NEXT_PUBLIC_SAVE_ON_GAS_ENABLED: yup.boolean(), + NEXT_PUBLIC_ADDRESS_USERNAME_TAG: yup + .mixed() + .test('shape', 'Invalid schema were provided for NEXT_PUBLIC_ADDRESS_USERNAME_TAG, it should have api_url_template', (data) => { + const isUndefined = data === undefined; + const valueSchema = yup.object().transform(replaceQuotes).json().shape({ + api_url_template: yup.string().required(), + tag_link_template: yup.string(), + tag_icon: yup.string(), + tag_bg_color: yup.string(), + tag_text_color: yup.string(), + }); + + return isUndefined || valueSchema.isValidSync(data); + }), + NEXT_PUBLIC_REWARDS_SERVICE_API_HOST: yup.string().test(urlTest), + NEXT_PUBLIC_XSTAR_SCORE_URL: yup.string().test(urlTest), // 6. External services envs NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(), - NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY: yup.string(), + NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY: yup.string(), // DEPRECATED + NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY: yup.string(), NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: yup.string(), NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: yup.string(), NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: yup.string(), @@ -675,6 +907,7 @@ const schema = yup .concat(adsBannerSchema) .concat(marketplaceSchema) .concat(rollupSchema) + .concat(celoSchema) .concat(beaconChainSchema) .concat(bridgedTokensSchema) .concat(sentrySchema); diff --git a/deploy/tools/envs-validator/test/.env.alt b/deploy/tools/envs-validator/test/.env.alt new file mode 100644 index 0000000000..fe62f612d7 --- /dev/null +++ b/deploy/tools/envs-validator/test/.env.alt @@ -0,0 +1,8 @@ +NEXT_PUBLIC_GRAPHIQL_TRANSACTION=none +NEXT_PUBLIC_API_SPEC_URL=none +NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=none +NEXT_PUBLIC_HOMEPAGE_STATS=[] +NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16','bech32'] +NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX=foo +NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=deprecated +NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx \ No newline at end of file diff --git a/deploy/tools/envs-validator/test/.env.arbitrum b/deploy/tools/envs-validator/test/.env.arbitrum new file mode 100644 index 0000000000..ee0ce91f00 --- /dev/null +++ b/deploy/tools/envs-validator/test/.env.arbitrum @@ -0,0 +1,4 @@ +NEXT_PUBLIC_ROLLUP_TYPE=arbitrum +NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://example.com +NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS=true +NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME=DuckChain \ No newline at end of file diff --git a/deploy/tools/envs-validator/test/.env.base b/deploy/tools/envs-validator/test/.env.base index 71798d55ab..a80650f4bc 100644 --- a/deploy/tools/envs-validator/test/.env.base +++ b/deploy/tools/envs-validator/test/.env.base @@ -3,12 +3,11 @@ NEXT_PUBLIC_AUTH_URL=https://example.com NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_LOGOUT_URL=https://example.com NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx -NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx +NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=xxx NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx -FAVICON_GENERATOR_API_KEY=xxx NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx NEXT_PUBLIC_AD_TEXT_PROVIDER=coinzilla NEXT_PUBLIC_AD_BANNER_PROVIDER=slise @@ -28,20 +27,26 @@ NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true NEXT_PUBLIC_FEATURED_NETWORKS=https://example.com NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/accounts','/apps'] NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal +NEXT_PUBLIC_FONT_FAMILY_HEADING={'name':'Montserrat','url':'https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap'} +NEXT_PUBLIC_FONT_FAMILY_BODY={'name':'Raleway','url':'https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap'} NEXT_PUBLIC_FOOTER_LINKS=https://example.com NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d +NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED=false NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=false NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS=false +NEXT_PUBLIC_MAX_CONTENT_WIDTH_ENABLED=false NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','current_epoch'] NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='#fff' NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgb(255, 145, 0)' -NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true +NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['lightpink'],'text_color':['deepskyblue','white'],'border':['3px solid black']} NEXT_PUBLIC_GAS_TRACKER_ENABLED=true NEXT_PUBLIC_GAS_TRACKER_UNITS=['gwei'] NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE='Hello' NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://example.com NEXT_PUBLIC_METASUITES_ENABLED=true +NEXT_PUBLIC_NAVIGATION_HIDDEN_LINKS=['eth_rpc_api','rpc_api'] NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH @@ -66,7 +71,9 @@ NEXT_PUBLIC_STATS_API_HOST=https://example.com NEXT_PUBLIC_STATS_API_BASE_PATH=/ NEXT_PUBLIC_USE_NEXT_JS_PROXY=false NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=gradient_avatar +NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16'] NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS=['top_accounts'] +NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS=['solidity-hardhat','solidity-foundry'] NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward'] NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'NFT Marketplace','collection_url':'https://example.com/{hash}','instance_url':'https://example.com/{hash}/{id}','logo_url':'https://example.com/logo.png'}] NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS=['fee_per_gas'] @@ -77,4 +84,7 @@ NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=false NEXT_PUBLIC_WEB3_WALLETS=['coinbase','metamask','token_pocket'] NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=stability NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'uniswap'},{'text':'Payment link','icon':'payment_link','url':'https://example.com'}] -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_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_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'dapp_id': 'smol-refuel', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&utm_medium=address&disableBridges=true', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'} +NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true +NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://example.com diff --git a/deploy/tools/envs-validator/test/.env.celo b/deploy/tools/envs-validator/test/.env.celo new file mode 100644 index 0000000000..1082044208 --- /dev/null +++ b/deploy/tools/envs-validator/test/.env.celo @@ -0,0 +1,2 @@ +NEXT_PUBLIC_CELO_ENABLED=true +NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=420 \ No newline at end of file diff --git a/deploy/tools/envs-validator/test/.env.marketplace b/deploy/tools/envs-validator/test/.env.marketplace index 01eab57086..6cc6b1f839 100644 --- a/deploy/tools/envs-validator/test/.env.marketplace +++ b/deploy/tools/envs-validator/test/.env.marketplace @@ -8,3 +8,5 @@ NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://example.com NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=aave NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maxaleks/36f779fd7d74877b57ec7a25a9a3a6c9/raw/746a8a59454c0537235ee44616c4690ce3bbf3c8/banner.html NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://www.basename.app +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=test +NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=test diff --git a/deploy/tools/envs-validator/test/.env.rollup b/deploy/tools/envs-validator/test/.env.optimism similarity index 51% rename from deploy/tools/envs-validator/test/.env.rollup rename to deploy/tools/envs-validator/test/.env.optimism index e7cacfb086..4821e1ca62 100644 --- a/deploy/tools/envs-validator/test/.env.rollup +++ b/deploy/tools/envs-validator/test/.env.optimism @@ -1,4 +1,6 @@ NEXT_PUBLIC_ROLLUP_TYPE=optimistic NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://example.com NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://example.com -NEXT_PUBLIC_FAULT_PROOF_ENABLED=true \ No newline at end of file +NEXT_PUBLIC_FAULT_PROOF_ENABLED=true +NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS=true +NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED=false \ No newline at end of file diff --git a/deploy/tools/envs-validator/yarn.lock b/deploy/tools/envs-validator/yarn.lock index 6ea4de04cf..8259f31c56 100644 --- a/deploy/tools/envs-validator/yarn.lock +++ b/deploy/tools/envs-validator/yarn.lock @@ -21,6 +21,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -44,7 +49,20 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.20": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== @@ -52,28 +70,12 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "8.44.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.0.tgz#55818eabb376e2272f77fbf5c96c43137c3c1e53" - integrity sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" +"@types/estree@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" - integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== - -"@types/json-schema@*", "@types/json-schema@^7.0.8": +"@types/json-schema@^7.0.8": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== @@ -83,10 +85,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.2.tgz#129cc9ae69f93824f92fac653eebfb4812ab4af9" integrity sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw== -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== dependencies: "@webassemblyjs/helper-numbers" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" @@ -101,10 +103,10 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== "@webassemblyjs/helper-numbers@1.11.6": version "1.11.6" @@ -120,15 +122,15 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/ieee754@1.11.6": version "1.11.6" @@ -149,59 +151,59 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-api-error" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" "@webpack-cli/configtest@^2.1.1": @@ -229,10 +231,10 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -acorn-import-assertions@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" - integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== acorn@^8.7.1, acorn@^8.8.2: version "8.10.0" @@ -261,32 +263,32 @@ ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -braces@^3.0.2: +braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" -browserslist@^4.14.5: - version "4.21.9" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" - integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== +browserslist@^4.21.10: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: - caniuse-lite "^1.0.30001503" - electron-to-chromium "^1.4.431" - node-releases "^2.0.12" - update-browserslist-db "^1.0.11" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -caniuse-lite@^1.0.30001503: - version "1.0.30001517" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8" - integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA== +caniuse-lite@^1.0.30001669: + version "1.0.30001673" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz#5aa291557af1c71340e809987367410aab7a5a9e" + integrity sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw== chalk@^4.1.0: version "4.1.2" @@ -366,12 +368,12 @@ dotenv@^16.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== -electron-to-chromium@^1.4.431: - version "1.4.467" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.467.tgz#b0660bf644baff7eedea33b8c742fb53ec60e3c2" - integrity sha512-2qI70O+rR4poYeF2grcuS/bCps5KJh6y1jtZMDDEteyKJQrzLOEhFyXCLcHW6DTBjKjWkk26JhWoAi+Ux9A0fg== +electron-to-chromium@^1.5.41: + version "1.5.47" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz#ef0751bc19b28be8ee44cd8405309de3bf3b20c7" + integrity sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ== -enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0, enhanced-resolve@^5.7.0: +enhanced-resolve@^5.0.0, enhanced-resolve@^5.7.0: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== @@ -379,6 +381,14 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0, enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + envinfo@^7.7.3: version "7.10.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" @@ -389,10 +399,10 @@ es-module-lexer@^1.2.1: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== eslint-scope@5.1.1: version "5.1.1" @@ -464,7 +474,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -577,11 +587,11 @@ merge-stream@^2.0.0: integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== micromatch@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" mime-db@1.52.0: @@ -606,10 +616,10 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -node-releases@^2.0.12: - version "2.0.13" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" - integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== p-limit@^2.2.0: version "2.3.0" @@ -645,10 +655,10 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.3.1: version "2.3.1" @@ -796,21 +806,21 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.3.7: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.8" + terser "^5.26.0" -terser@^5.16.8: - version "5.19.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.2.tgz#bdb8017a9a4a8de4663a7983f45c506534f9234e" - integrity sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA== +terser@^5.26.0: + version "5.36.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e" + integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -867,13 +877,13 @@ type-fest@^2.19.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.2.0" + picocolors "^1.1.0" uri-js@^4.2.2: version "4.4.1" @@ -882,10 +892,10 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -923,33 +933,32 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.88.2: - version "5.88.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" - integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== - dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + version "5.95.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.95.0.tgz#8fd8c454fa60dad186fbe36c400a55848307b4c0" + integrity sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q== + dependencies: + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.9.0" - browserslist "^4.14.5" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" which@^2.0.1: diff --git a/deploy/tools/favicon-generator/.gitignore b/deploy/tools/favicon-generator/.gitignore old mode 100755 new mode 100644 index 09af2dfe51..8b8c2bf2a8 --- a/deploy/tools/favicon-generator/.gitignore +++ b/deploy/tools/favicon-generator/.gitignore @@ -1,4 +1,4 @@ -/output -config.json -response.json -favicon_package** \ No newline at end of file +/node_modules +/public +.env +/output \ No newline at end of file diff --git a/deploy/tools/favicon-generator/config.template.json b/deploy/tools/favicon-generator/config.template.json deleted file mode 100755 index 4d032e8bb9..0000000000 --- a/deploy/tools/favicon-generator/config.template.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "favicon_generation": { - "api_key": "", - "master_picture": { - "type": "url", - "url": "" - }, - "files_location": { - "type": "path", - "path": "/favicons" - }, - "favicon_design": { - "desktop_browser": {}, - "ios": { - "picture_aspect": "no_change", - "assets": { - "ios6_and_prior_icons": false, - "ios7_and_later_icons": true, - "precomposed_icons": false, - "declare_only_default_icon": true - } - }, - "safari_pinned_tab": { - "picture_aspect": "black_and_white", - "threshold": 60 - } - }, - "settings": { - "compression": "3", - "scaling_algorithm": "Mitchell", - "error_on_image_too_small": true, - "readme_file": false, - "html_code_file": false, - "use_path_as_is": false - }, - "versioning": { - "param_name": "ver", - "param_value": "15Zd8" - } - } -} \ No newline at end of file diff --git a/deploy/tools/favicon-generator/index.js b/deploy/tools/favicon-generator/index.js new file mode 100644 index 0000000000..b552a9f6c9 --- /dev/null +++ b/deploy/tools/favicon-generator/index.js @@ -0,0 +1,71 @@ +/* eslint-disable no-console */ +const { favicons } = require('favicons'); +const fs = require('fs/promises'); +const path = require('path'); + +generateFavicons(); + +async function generateFavicons() { + console.log('Generating favicons...'); + const masterUrl = process.env.MASTER_URL; + try { + if (!masterUrl) { + throw new Error('FAVICON_MASTER_URL or NEXT_PUBLIC_NETWORK_ICON must be set'); + } + + const fetch = await import('node-fetch'); + const response = await fetch.default(masterUrl); + const buffer = await response.arrayBuffer(); + const source = Buffer.from(buffer); + + const configuration = { + path: '/output', + appName: 'Blockscout', + icons: { + android: true, + appleIcon: { + background: 'transparent', + }, + appleStartup: false, + favicons: true, + windows: false, + yandex: false, + }, + }; + + try { + const result = await favicons(source, configuration); + + const outputDir = path.resolve(process.cwd(), 'output'); + await fs.mkdir(outputDir, { recursive: true }); + + for (const image of result.images) { + // keep only necessary files + if (image.name === 'apple-touch-icon-180x180.png' || image.name === 'android-chrome-192x192.png' || + (!image.name.startsWith('apple-touch-icon') && !image.name.startsWith('android-chrome')) + ) { + await fs.writeFile(path.join(outputDir, image.name), image.contents); + } + + // copy android-chrome-192x192.png to logo-icon.png for marketing purposes + if (image.name === 'android-chrome-192x192.png') { + await fs.writeFile(path.join(outputDir, 'logo-icon.png'), image.contents); + } + } + + for (const file of result.files) { + if (file.name !== 'manifest.webmanifest') { + await fs.writeFile(path.join(outputDir, file.name), file.contents); + } + } + + console.log('Favicons generated successfully!'); + } catch (faviconError) { + console.error('Error generating favicons:', faviconError); + process.exit(1); + } + } catch (error) { + console.error('Error in favicon generation process:', error); + process.exit(1); + } +} diff --git a/deploy/tools/favicon-generator/package.json b/deploy/tools/favicon-generator/package.json new file mode 100644 index 0000000000..f4127ccc37 --- /dev/null +++ b/deploy/tools/favicon-generator/package.json @@ -0,0 +1,20 @@ +{ + "name": "favicon-generator", + "version": "1.0.0", + "main": "index.js", + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "favicons": "^7.2.0", + "ts-loader": "^9.4.4", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" + }, + "devDependencies": { + "dotenv-cli": "^7.4.2", + "node-loader": "^2.0.0", + "tsconfig-paths-webpack-plugin": "^4.1.0" + } +} diff --git a/deploy/tools/favicon-generator/script.sh b/deploy/tools/favicon-generator/script.sh deleted file mode 100755 index 69145399ed..0000000000 --- a/deploy/tools/favicon-generator/script.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash - -echo "🌀 Generating favicons bundle..." - -# Check if MASTER_URL is provided -if [ -z "$MASTER_URL" ]; then - echo "🛑 Error: MASTER_URL variable is not provided." - exit 1 -fi - -# Check if FAVICON_GENERATOR_API_KEY is provided -if [ -z "$FAVICON_GENERATOR_API_KEY" ]; then - echo "🛑 Error: FAVICON_GENERATOR_API_KEY variable is not provided." - exit 1 -fi - -# Mask the FAVICON_GENERATOR_API_KEY to display only the first 8 characters -API_KEY_MASKED="${FAVICON_GENERATOR_API_KEY:0:8}***" -echo "🆗 The following variables are provided:" -echo " MASTER_URL: $MASTER_URL" -echo " FAVICON_GENERATOR_API_KEY: $API_KEY_MASKED" -echo - -# RealFaviconGenerator API endpoint URL -API_URL="https://realfavicongenerator.net/api/favicon" - -# Target folder for the downloaded assets -TARGET_FOLDER="./output" - -# Path to the config JSON template file -CONFIG_TEMPLATE_FILE="config.template.json" - -# Path to the generated config JSON file -CONFIG_FILE="config.json" - -# Replace and placeholders in the JSON template file -API_KEY_VALUE="$FAVICON_GENERATOR_API_KEY" -sed -e "s||$API_KEY_VALUE|" -e "s||$MASTER_URL|" "$CONFIG_TEMPLATE_FILE" > "$CONFIG_FILE" - -# Make the API POST request with JSON data from the config file -echo "⏳ Making request to API..." -API_RESPONSE=$(curl -s -X POST -H "Content-Type: application/json" -d @"$CONFIG_FILE" "$API_URL") - -# Create the response.json file with the API response -echo "$API_RESPONSE" > response.json - -# Check if the API response is valid JSON and contains success status -if ! jq -e '.favicon_generation_result.result.status == "success"' <<< "$API_RESPONSE" >/dev/null; then - echo "🛑 Error: API response does not contain the expected structure or has an error status." - ERROR_MESSAGE=$(echo "$API_RESPONSE" | jq -r '.favicon_generation_result.result.error_message' | tr -d '\\') - if [ -n "$ERROR_MESSAGE" ]; then - echo "🛑 $ERROR_MESSAGE" - fi - exit 1 -fi -echo "🆗 API responded with success status." - -# Parse the JSON response to extract the file URL and remove backslashes -FILE_URL=$(echo "$API_RESPONSE" | jq -r '.favicon_generation_result.favicon.package_url' | tr -d '\\') -PREVIEW_URL=$(echo "$API_RESPONSE" | jq -r '.favicon_generation_result.preview_picture_url' | tr -d '\\') - -# Check if FILE_URL is empty -if [ -z "$FILE_URL" ]; then - echo "🛑 File URL not found in JSON response." - exit 1 -fi - -echo "🆗 Found following file URL in the response: $FILE_URL" -echo "🆗 Favicon preview URL: $PREVIEW_URL" -echo - -# Generate a filename based on the URL -FILE_NAME=$(basename "$FILE_URL") - -# Check if the target folder exists and clear its contents if it does -if [ -d "$TARGET_FOLDER" ]; then - rm -r "$TARGET_FOLDER" -fi -mkdir -p "$TARGET_FOLDER" - -# Download the file -echo "⏳ Trying to download the file..." -curl -s -L "$FILE_URL" -o "$FILE_NAME" - -# Check if the download was successful -if [ $? -eq 0 ]; then - echo "🆗 File downloaded successfully." - echo -else - echo "🛑 Error: Failed to download the file." - exit 1 -fi - -# Unzip the downloaded file to the target folder -echo "⏳ Unzipping the file..." -unzip -q "$FILE_NAME" -d "$TARGET_FOLDER" - -# Check if the unzip operation was successful -if [ $? -eq 0 ]; then - echo "🆗 File unzipped successfully." - echo -else - echo "🛑 Failed to unzip the file." - exit 1 -fi - -# Clean up - remove the JSON response file and temporary JSON config file -rm response.json "$CONFIG_FILE" - -echo "✅ Done." \ No newline at end of file diff --git a/deploy/tools/favicon-generator/yarn.lock b/deploy/tools/favicon-generator/yarn.lock new file mode 100644 index 0000000000..af54e5366c --- /dev/null +++ b/deploy/tools/favicon-generator/yarn.lock @@ -0,0 +1,1178 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@emnapi/runtime@^1.2.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60" + integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw== + dependencies: + tslib "^2.4.0" + +"@img/sharp-darwin-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz" + integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.0.4" + +"@img/sharp-darwin-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61" + integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.0.4" + +"@img/sharp-libvips-darwin-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz" + integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== + +"@img/sharp-libvips-darwin-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062" + integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== + +"@img/sharp-libvips-linux-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704" + integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== + +"@img/sharp-libvips-linux-arm@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197" + integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== + +"@img/sharp-libvips-linux-s390x@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce" + integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== + +"@img/sharp-libvips-linux-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0" + integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== + +"@img/sharp-libvips-linuxmusl-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5" + integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== + +"@img/sharp-libvips-linuxmusl-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff" + integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== + +"@img/sharp-linux-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22" + integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.0.4" + +"@img/sharp-linux-arm@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff" + integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.0.5" + +"@img/sharp-linux-s390x@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667" + integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.0.4" + +"@img/sharp-linux-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb" + integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.0.4" + +"@img/sharp-linuxmusl-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b" + integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + +"@img/sharp-linuxmusl-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48" + integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + +"@img/sharp-wasm32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1" + integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== + dependencies: + "@emnapi/runtime" "^1.2.0" + +"@img/sharp-win32-ia32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9" + integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== + +"@img/sharp-win32-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" + integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@types/estree@^1.0.5": + version "1.0.6" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/json-schema@^7.0.8": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*": + version "22.7.9" + resolved "https://registry.npmjs.org/@types/node/-/node-22.7.9.tgz" + integrity sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg== + dependencies: + undici-types "~6.19.2" + +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn@^8.7.1, acorn@^8.8.2: + version "8.13.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz" + integrity sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w== + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.21.10: + version "4.24.2" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== + dependencies: + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +caniuse-lite@^1.0.30001669: + version "1.0.30001669" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz" + integrity sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/color/-/color-4.2.3.tgz" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +detect-libc@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +dotenv-cli@^7.4.2: + version "7.4.2" + resolved "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.2.tgz" + integrity sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA== + dependencies: + cross-spawn "^7.0.3" + dotenv "^16.3.0" + dotenv-expand "^10.0.0" + minimist "^1.2.6" + +dotenv-expand@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz" + integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== + +dotenv@^16.3.0: + version "16.4.5" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +electron-to-chromium@^1.5.41: + version "1.5.43" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.43.tgz" + integrity sha512-NxnmFBHDl5Sachd2P46O7UJiMaMHMLSofoIWVJq3mj8NJgG0umiSeljAVP9lGzjI0UDLJJ5jjoGjcrB8RSbjLQ== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.1, enhanced-resolve@^5.7.0: + version "5.17.1" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.14.0" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz" + integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== + +es-module-lexer@^1.2.1: + version "1.5.4" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +favicons@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/favicons/-/favicons-7.2.0.tgz" + integrity sha512-k/2rVBRIRzOeom3wI9jBPaSEvoTSQEW4iM0EveBmBBKFxO8mSyyRWtDlfC3VnEfu0avmjrMzy8/ZFPSe6F71Hw== + dependencies: + escape-html "^1.0.3" + sharp "^0.33.1" + xml2js "^0.6.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json5@^2.1.2, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^4.0.0: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-loader@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/node-loader/-/node-loader-2.0.0.tgz#9109a6d828703fd3e0aa03c1baec12a798071562" + integrity sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q== + dependencies: + loader-utils "^2.0.0" + +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver@^7.3.4, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +sharp@^0.33.1: + version "0.33.5" + resolved "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz" + integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== + dependencies: + color "^4.2.3" + detect-libc "^2.0.3" + semver "^7.6.3" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.33.5" + "@img/sharp-darwin-x64" "0.33.5" + "@img/sharp-libvips-darwin-arm64" "1.0.4" + "@img/sharp-libvips-darwin-x64" "1.0.4" + "@img/sharp-libvips-linux-arm" "1.0.5" + "@img/sharp-libvips-linux-arm64" "1.0.4" + "@img/sharp-libvips-linux-s390x" "1.0.4" + "@img/sharp-libvips-linux-x64" "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + "@img/sharp-linux-arm" "0.33.5" + "@img/sharp-linux-arm64" "0.33.5" + "@img/sharp-linux-s390x" "0.33.5" + "@img/sharp-linux-x64" "0.33.5" + "@img/sharp-linuxmusl-arm64" "0.33.5" + "@img/sharp-linuxmusl-x64" "0.33.5" + "@img/sharp-wasm32" "0.33.5" + "@img/sharp-win32-ia32" "0.33.5" + "@img/sharp-win32-x64" "0.33.5" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.36.0" + resolved "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz" + integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-loader@^9.4.4: + version "9.5.1" + resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz" + integrity sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + source-map "^0.7.4" + +tsconfig-paths-webpack-plugin@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz" + integrity sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.7.0" + tsconfig-paths "^4.1.2" + +tsconfig-paths@^4.1.2: + version "4.2.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.4.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" + integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== + +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@^5.1.4: + version "5.1.4" + resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.88.2: + version "5.95.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz" + integrity sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q== + dependencies: + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.1" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +xml2js@^0.6.1: + version "0.6.2" + resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== diff --git a/deploy/tools/feature-reporter/yarn.lock b/deploy/tools/feature-reporter/yarn.lock index 502e97507e..783abddc02 100644 --- a/deploy/tools/feature-reporter/yarn.lock +++ b/deploy/tools/feature-reporter/yarn.lock @@ -39,7 +39,15 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.20": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": version "0.3.19" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== @@ -68,28 +76,12 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" +"@types/estree@^1.0.5": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity "sha1-Yo7/7q4gZKG055946B2Ht+X8e1A= sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" -"@types/eslint@*": - version "8.44.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.2.tgz#0d21c505f98a89b8dd4d37fa162b09da6089199a" - integrity sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" - integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== - -"@types/json-schema@*", "@types/json-schema@^7.0.8": +"@types/json-schema@^7.0.8": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== @@ -99,10 +91,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85" integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg== -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== dependencies: "@webassemblyjs/helper-numbers" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" @@ -117,10 +109,10 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== "@webassemblyjs/helper-numbers@1.11.6": version "1.11.6" @@ -136,15 +128,15 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/ieee754@1.11.6": version "1.11.6" @@ -165,59 +157,59 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-api-error" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" "@webpack-cli/configtest@^2.1.1": @@ -245,10 +237,10 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -acorn-import-assertions@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" - integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== acorn@^8.7.1, acorn@^8.8.2: version "8.10.0" @@ -288,32 +280,32 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" -browserslist@^4.14.5: - version "4.21.10" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" - integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== +browserslist@^4.21.10: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity "sha1-9YRbyRBp29Ve6J+vmCLh2IXRZYA= sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==" dependencies: - caniuse-lite "^1.0.30001517" - electron-to-chromium "^1.4.477" - node-releases "^2.0.13" - update-browserslist-db "^1.0.11" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -caniuse-lite@^1.0.30001517: - version "1.0.30001519" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" - integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== +caniuse-lite@^1.0.30001669: + version "1.0.30001673" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz#5aa291557af1c71340e809987367410aab7a5a9e" + integrity "sha1-WqKRVXrxxxNA6AmYc2dBCqt6Wp4= sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==" chokidar@^3.5.3: version "3.5.3" @@ -400,15 +392,15 @@ dotenv@^16.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== -electron-to-chromium@^1.4.477: - version "1.4.487" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.487.tgz#e2ef8b15f2791bf68fa6f38f2656f1a551d360ae" - integrity sha512-XbCRs/34l31np/p33m+5tdBrdXu9jJkZxSbNxj5I0H1KtV2ZMSB+i/HYqDiRzHaFx2T5EdytjoBRe8QRJE2vQg== +electron-to-chromium@^1.5.41: + version "1.5.47" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz#ef0751bc19b28be8ee44cd8405309de3bf3b20c7" + integrity "sha1-7wdRvBmyi+juRM2EBTCd4787IMc= sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==" -enhanced-resolve@^5.15.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -423,10 +415,10 @@ es-module-lexer@^1.2.1: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== eslint-scope@5.1.1: version "5.1.1" @@ -540,7 +532,7 @@ globby@^11.0.4: merge2 "^1.4.1" slash "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -670,11 +662,11 @@ merge2@^1.3.0, merge2@^1.4.1: integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" mime-db@1.52.0: @@ -704,10 +696,10 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -node-releases@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" - integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -753,10 +745,10 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity "sha1-PTIa8+q5ObCDyPkpodEs2oHCa2s= sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" @@ -921,21 +913,21 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.3.7: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.8" + terser "^5.26.0" -terser@^5.16.8: - version "5.19.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.2.tgz#bdb8017a9a4a8de4663a7983f45c506534f9234e" - integrity sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA== +terser@^5.26.0: + version "5.36.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e" + integrity "sha1-iw2+1FmsQP97TJ/Vo6ICneEFGA4= sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==" dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -971,13 +963,13 @@ typescript@5.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity "sha1-gIRvuh156CVH+2YfjRQeCUV1X+U= sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==" dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.2.0" + picocolors "^1.1.0" uri-js@^4.2.2: version "4.4.1" @@ -986,10 +978,10 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity "sha1-L+6u1nQS58MxhOWnnKc4+9OFZNo= sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==" dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -1027,33 +1019,32 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.88.2: - version "5.88.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" - integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== - dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + version "5.95.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.95.0.tgz#8fd8c454fa60dad186fbe36c400a55848307b4c0" + integrity "sha1-j9jEVPpg2tGG++NsQApVhIMHtMA= sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==" + dependencies: + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.9.0" - browserslist "^4.14.5" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" which@^2.0.1: diff --git a/deploy/values/review-l2/values.yaml.gotmpl b/deploy/values/review-l2/values.yaml.gotmpl index 09a609ed13..782072ff59 100644 --- a/deploy/values/review-l2/values.yaml.gotmpl +++ b/deploy/values/review-l2/values.yaml.gotmpl @@ -49,15 +49,10 @@ frontend: env: NEXT_PUBLIC_APP_ENV: development NEXT_PUBLIC_APP_INSTANCE: review_L2 - NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/base.svg NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/base.svg NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/base-mainnet.json NEXT_PUBLIC_API_HOST: base.blockscout.com - NEXT_PUBLIC_MARKETPLACE_ENABLED: true - NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C - NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM: https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form - NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json NEXT_PUBLIC_LOGOUT_URL: https://blockscoutcom.us.auth0.com/v2/logout NEXT_PUBLIC_STATS_API_HOST: https://stats-l2-base-mainnet.k8s-prod-1.blockscout.com NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: "linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)" @@ -72,7 +67,7 @@ frontend: NEXT_PUBLIC_METADATA_SERVICE_API_HOST: https://metadata.services.blockscout.com NEXT_PUBLIC_ROLLUP_TYPE: optimistic NEXT_PUBLIC_ROLLUP_L1_BASE_URL: https://eth.blockscout.com - NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL: https://bridge.base.org/withdraw + NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62 NEXT_PUBLIC_USE_NEXT_JS_PROXY: true NEXT_PUBLIC_NAVIGATION_LAYOUT: horizontal @@ -82,7 +77,6 @@ frontend: SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID 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/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID - FAVICON_GENERATOR_API_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY NEXT_PUBLIC_OG_IMAGE_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/base-mainnet.png + NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY diff --git a/deploy/values/review/values.yaml.gotmpl b/deploy/values/review/values.yaml.gotmpl index ef5a6388c2..b0d34ffa99 100644 --- a/deploy/values/review/values.yaml.gotmpl +++ b/deploy/values/review/values.yaml.gotmpl @@ -4,7 +4,7 @@ imagePullSecrets: - name: regcred config: network: - id: 11155111 + id: "11155111" name: Blockscout shortname: Blockscout currency: @@ -49,7 +49,6 @@ frontend: env: NEXT_PUBLIC_APP_ENV: development NEXT_PUBLIC_APP_INSTANCE: review - NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: validation NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-sepolia.json 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 @@ -61,18 +60,10 @@ frontend: 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_AUTH_URL: https://blockscout-main.k8s-dev.blockscout.com - NEXT_PUBLIC_MARKETPLACE_ENABLED: true - NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM: https://airtable.com/shrqUAcjgGJ4jU88C - NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM: https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form - NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL: https://gist.githubusercontent.com/maxaleks/ce5c7e3de53e8f5b240b88265daf5839/raw/328383c958a8f7ecccf6d50c953bcdf8ab3faa0a/security_reports_goerli_test.json NEXT_PUBLIC_LOGOUT_URL: https://blockscoutcom.us.auth0.com/v2/logout 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_MARKETPLACE_CATEGORIES_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace-categories/default.json - NEXT_PUBLIC_MARKETPLACE_FEATURED_APP: zkbob-wallet - NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL: https://gist.githubusercontent.com/maxaleks/36f779fd7d74877b57ec7a25a9a3a6c9/raw/746a8a59454c0537235ee44616c4690ce3bbf3c8/banner.html - NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL: https://www.basename.app NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d NEXT_PUBLIC_WEB3_WALLETS: "['token_pocket','coinbase','metamask']" NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE: gradient_avatar @@ -86,13 +77,13 @@ frontend: NEXT_PUBLIC_AD_BANNER_PROVIDER: slise NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']" + PROMETHEUS_METRICS_ENABLED: true envFromSecret: NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID 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_RE_CAPTCHA_APP_SITE_KEY: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY 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 - FAVICON_GENERATOR_API_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY 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_V3_APP_SITE_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 0284e6e3cf..58d9e3af5d 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -49,7 +49,7 @@ To develop locally, follow one of the two paths outlined below: A. Custom configuration: 1. Create `.env.local` file in the root folder and include all required environment variables from the [list](./ENVS.md) -2. Optionally, clone `.env.example` and name it `.env.secrets`. Fill it with necessary secrets for integrating with [external services](./ENVS.md#external-services-configuration). Include only secrets your need. +2. Optionally, clone `.env.example` and name it `.env.secrets`. Fill it with necessary secrets for integrating with [external services](./ENVS.md#external-services-configuration). Include only secrets you need. 3. Use `yarn dev` command to start the dev server. 4. Open your browser and navigate to the URL provided in the command line output (by default, it is `http://localhost:3000`). @@ -66,7 +66,7 @@ B. Pre-defined configuration: ## Adding new dependencies For all types of dependencies: - **Do not add** a dependency if the desired functionality is easily implementable -- If adding a dependency is necessary, please be sure that is is well-maintained and trustworthy +- If adding a dependency is necessary, please be sure that it is well-maintained and trustworthy   @@ -109,11 +109,11 @@ Every feature or bugfix should be accompanied by tests, either unit tests or com ### Jest unit tests -If your changes only related to the logic of the app and not to its visual presentation, then try to write unit tests using [Jest](https://jestjs.io/) framework and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). In general these tests are "cheaper" and faster than Playwright ones. Use them for testing your utilities and React hooks, as well as the whole components logic. +If your changes are only related to the logic of the app and not to its visual presentation, then try to write unit tests using [Jest](https://jestjs.io/) framework and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). In general these tests are "cheaper" and faster than Playwright ones. Use them for testing your utilities and React hooks, as well as the whole components logic. Place your test suites in `.test.ts` or `.test.tsx` files. You can find or add some mocks or other helpful utilities for these tests purposes in the `/jest` folder. -*Note*, that we are using custom renderer and wrapper in all test for React components, so please do not import package `@testing-library/react` directly in your test suites, instead use imports from `jest/lib` utility. +*Note*, that we are using custom renderer and wrapper in all tests for React components, so please do not import package `@testing-library/react` directly in your test suites, instead use imports from `jest/lib` utility. ### Playwright components tests diff --git a/docs/DEPRECATED_ENVS.md b/docs/DEPRECATED_ENVS.md index 0cf27ae106..3b06de639f 100644 --- a/docs/DEPRECATED_ENVS.md +++ b/docs/DEPRECATED_ENVS.md @@ -3,6 +3,7 @@ | Variable | Type | Description | Compulsoriness | Default value | Example value | Introduced in version | Deprecated in version | Comment | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY | `string` | RealFaviconGenerator [API key](https://realfavicongenerator.net/api/) | Required | - | `` | v1.11.0 | v1.16.0 | Replaced FAVICON_GENERATOR_API_KEY | +| FAVICON_GENERATOR_API_KEY | `string` | RealFaviconGenerator [API key](https://realfavicongenerator.net/api/) | Required | - | `` | v1.16.0+ | v1.37.0 | We don't use RealFaviconGenerator anymore | | NEXT_PUBLIC_IS_OPTIMISTIC_L2_NETWORK | `boolean` | Set to true for optimistic L2 solutions | Required | - | `true` | v1.17.0 | v1.24.0 | Replaced by NEXT_PUBLIC_ROLLUP_TYPE | | NEXT_PUBLIC_IS_ZKEVM_L2_NETWORK | `boolean` | Set to true for zkevm L2 solutions | Required | - | `true` | v1.17.0 | v1.24.0 | Replaced by NEXT_PUBLIC_ROLLUP_TYPE | | NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL | `string` | URL for optimistic L2 -> L1 withdrawals | Required | - | `https://app.optimism.io/bridge/withdraw` | v1.17.0 | v1.24.0 | Renamed to NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | @@ -10,3 +11,5 @@ | NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` | - | v1.25.0 | Replaced by NEXT_PUBLIC_GAS_TRACKER_ENABLED | | NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL | `string` | Network governance token symbol | - | - | `GNO` | v1.12.0 | v1.29.0 | Replaced by NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL | | NEXT_PUBLIC_SWAP_BUTTON_URL | `string` | Application ID in the marketplace or website URL | - | - | `uniswap` | v1.24.0 | v1.31.0 | Replaced by NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS | +| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ | v1.35.0 | Replaced by NEXT_PUBLIC_HOMEPAGE_STATS | +| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | Google reCAPTCHA v2 site key | - | - | `` | v1.0.x+ | v1.36.0 | Replaced by NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY | diff --git a/docs/ENVS.md b/docs/ENVS.md index c525bf93f2..a038c2223f 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -54,13 +54,18 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will - [Data availability](ENVS.md#data-availability) - [Bridged tokens](ENVS.md#bridged-tokens) - [Safe{Core} address tags](ENVS.md#safecore-address-tags) + - [Address profile API](ENVS.md#address-profile-api) + - [Address XStar XHS score](ENVS.md#address-xstar-xhs-score) - [SUAVE chain](ENVS.md#suave-chain) - [MetaSuites extension](ENVS.md#metasuites-extension) - [Validators list](ENVS.md#validators-list) - [Sentry error monitoring](ENVS.md#sentry-error-monitoring) - [OpenTelemetry](ENVS.md#opentelemetry) - - [Swap button](ENVS.md#defi-dropdown) + - [DeFi dropdown](ENVS.md#defi-dropdown) - [Multichain balance button](ENVS.md#multichain-balance-button) + - [Get gas button](ENVS.md#get-gas-button) + - [Save on gas with GasHawk](ENVS.md#save-on-gas-with-gashawk) + - [Rewards service API](ENVS.md#rewards-service-api) - [3rd party services configuration](ENVS.md#external-services-configuration)   @@ -92,7 +97,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will | 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` or `mining` | Verification type in the network | - | `mining` | `validation` | v1.0.x+ | +| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` \| `mining` | 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_TOKEN_STANDARD_NAME | `string` | Name of the standard for creating tokens | - | `ERC` | `BEP` | v1.31.0+ | | NEXT_PUBLIC_IS_TESTNET | `boolean`| Set to true if network is testnet | - | `false` | `true` | v1.0.x+ | | NEXT_PUBLIC_NEAR_NETWORK | `mainnet` or `testnet` | Near network target for explorer | - | `false` | `true` | @@ -118,9 +123,21 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_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_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `white` | `\#DCFE76` | v1.0.x+ | -| NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `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)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` | v1.1.0+ | -| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ | +| NEXT_PUBLIC_HOMEPAGE_STATS | `Array<'latest_batch' \| 'total_blocks' \| 'average_block_time' \| 'total_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_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `white` | `\#DCFE76` | v1.0.x+ | +| NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead). **DEPRECATED** _Use `NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG` instead_ | - | `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)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` | v1.1.0+ | +| 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+ | + +#### 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. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | +| --- | --- | --- | --- | --- | --- | +| 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']` | +| button | `Partial>` | The button on the banner. It has three possible states: `_default`, `_hover`, and `_selected`. The `_selected` state reflects when the user is logged in or their wallet is connected to the app. | - | - | `{'_default':{'background':['deeppink'],'text_color':['white']}}` |   @@ -132,7 +149,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will | NEXT_PUBLIC_NETWORK_LOGO_DARK | `string` | Network logo for dark color mode; if not provided, **inverted** regular logo will be used instead | - | - | `https://placekitten.com/240/40` | v1.0.x+ | | NEXT_PUBLIC_NETWORK_ICON | `string` | Network icon; used as a replacement for regular network logo when nav bar is collapsed; if not provided, placeholder will be shown; *Note* the icon size should be at least 60px by 60px | - | - | `https://placekitten.com/60/60` | v1.0.x+ | | NEXT_PUBLIC_NETWORK_ICON_DARK | `string` | Network icon for dark color mode; if not provided, **inverted** regular icon will be used instead | - | - | `https://placekitten.com/60/60` | v1.0.x+ | -| NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) which contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` | v1.0.x+ | +| NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) or file content string representation. It contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` \| `[{'title':'Astar(EVM)','url':'https://astar.blockscout.com/','group':'Mainnets','icon':'https://example.com/astar.svg'}]` | v1.0.x+ | | NEXT_PUBLIC_OTHER_LINKS | `Array<{url: string; text: string}>` | List of links for the "Other" navigation menu | - | - | `[{'url':'https://blockscout.com','text':'Blockscout'}]` | v1.0.x+ | | NEXT_PUBLIC_NAVIGATION_HIDDEN_LINKS | `Array` | List of external links hidden in the navigation. Supported ids are `eth_rpc_api`, `rpc_api` | - | - | `['eth_rpc_api']` | v1.16.0+ | | NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES | `Array` | List of menu item routes that should have a lightning label | - | - | `['/accounts']` | v1.31.0+ | @@ -155,7 +172,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_FOOTER_LINKS | `string` | URL of configuration file (`.json` format only) which contains list of link groups to be displayed in the footer. See [below](#footer-links-configuration-properties) list of available properties for particular group | - | - | `https://example.com/footer_links_config.json` | v1.1.1+ | +| NEXT_PUBLIC_FOOTER_LINKS | `string` | URL of configuration file (`.json` format only) or file content string representation. It contains list of link groups to be displayed in the footer. See [below](#footer-links-configuration-properties) list of available properties for particular group | - | - | `https://example.com/footer_links_config.json` \| `[{'title':'My chain','links':[{'text':'About','url':'https://example.com/about'},{'text':'Contacts','url':'https://example.com/contacts'}]}]` | v1.1.1+ | The app version shown in the footer is derived from build-time ENV variables `NEXT_PUBLIC_GIT_TAG` and `NEXT_PUBLIC_GIT_COMMIT_SHA` and cannot be overwritten at run-time. @@ -170,11 +187,10 @@ The app version shown in the footer is derived from build-time ENV variables `NE ### Favicon -By default, the app has generic favicon. You can override this behavior by providing the following variables. Hence, the favicon assets bundle will be generated at the container start time and will be used instead of default one. +By default, the app has generic favicon. You can override this behavior by providing the following variable. Hence, the favicon assets bundle will be generated at the container start time and will be used instead of default one. | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| FAVICON_GENERATOR_API_KEY | `string` | RealFaviconGenerator [API key](https://realfavicongenerator.net/api/) | Required | - | `` | v1.16.0+ | | FAVICON_MASTER_URL | `string` | - | - | `NEXT_PUBLIC_NETWORK_ICON` | `https://placekitten.com/180/180` | v1.11.0+ |   @@ -205,6 +221,7 @@ Settings for meta tags, OG tags and SEO ##### Block fields list | Id | Description | | --- | --- | +| `base_fee` | Base fee | | `burnt_fees` | Burnt fees | | `total_reward` | Total block reward | | `nonce` | Block nonce | @@ -219,8 +236,11 @@ Settings for meta tags, OG tags and SEO | Variable | Type | Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE | `"github" \| "jazzicon" \| "gradient_avatar" \| "blockie"` | Default style of address identicon appearance. Choose between [GitHub](https://github.blog/2013-08-14-identicons/), [Metamask Jazzicon](https://metamask.github.io/jazzicon/), [Gradient Avatar](https://github.com/varld/gradient-avatar) and [Ethereum Blocky](https://mycryptohq.github.io/ethereum-blockies-base64/) | - | `jazzicon` | `gradient_avatar` | v1.12.0+ | +| NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT | `Array<"base16" \| "bech32">` | Displayed address format, could be either `base16` standard or [`bech32`](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32) standard. If the array contains multiple values, the address format toggle will appear in the UI, allowing the user to switch between formats. The first item in the array will be the default format. | - | `'["base16"]'` | `'["bech32", "base16"]'` | v1.36.0+ | +| NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX | `string` | Human-readable prefix of `bech32` address format. | Required, if `NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT` contains "bech32" value | - | `duck` | v1.36.0+ | | NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS | `Array` | Address views that should not be displayed. See below the list of the possible id values. | - | - | `'["top_accounts"]'` | v1.15.0+ | | NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED | `boolean` | Set to `true` if SolidityScan reports are supported | - | - | `true` | v1.19.0+ | +| NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS | `Array<'solidity-hardhat' \| 'solidity-foundry'>` | Pass an array of additional methods from which users can choose while verifying a smart contract. Both methods are available by default, pass `'none'` string to disable them all. | - | - | `['solidity-hardhat']` | v1.33.0+ | ##### Address views list | Id | Description | @@ -260,7 +280,7 @@ Settings for meta tags, OG tags and SEO | Variable | Type | Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES | `Array` where `NftMarketplace` can have following [properties](#nft-marketplace-properties) | Used to build up links to NFT collections and NFT instances in external marketplaces. | - | - | `[{'name':'OpenSea','collection_url':'https://opensea.io/assets/ethereum/{hash}','instance_url':'https://opensea.io/assets/ethereum/{hash}/{id}','logo_url':'https://opensea.io/static/images/logos/opensea-logo.svg'}]` | v1.15.0+ | - +| NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED | `boolean` | Indicates that the [Helia verified fetch](https://github.com/ipfs/helia-verified-fetch/tree/main/packages/verified-fetch) should be used for retrieving content of NFT assets (currently limited to images) directly from IPFS network using trustless gateways. | - | `true` | `false` | v1.37.0+ | ##### NFT marketplace properties | Variable | Type| Description | Compulsoriness | Default value | Example value | @@ -285,6 +305,9 @@ Settings for meta tags, OG tags and SEO | NEXT_PUBLIC_HIDE_INDEXING_ALERT_INT_TXS | `boolean` | Set to `true` to hide indexing alert in the page footer about indexing block's internal transactions | - | `false` | `true` | v1.17.0+ | | NEXT_PUBLIC_MAINTENANCE_ALERT_MESSAGE | `string` | Used for displaying custom announcements or alerts in the header of the site. Could be a regular string or a HTML code. | - | - | `Hello world! 🤪` | v1.13.0+ | | NEXT_PUBLIC_COLOR_THEME_DEFAULT | `'light' \| 'dim' \| 'midnight' \| 'dark'` | Preferred color theme of the app | - | - | `midnight` | v1.30.0+ | +| NEXT_PUBLIC_FONT_FAMILY_HEADING | `FontFamily`, see full description [below](#font-family-configuration-properties) | Special typeface to use in page headings (`

`, `

`, etc.) | - | - | `{'name':'Montserrat','url':'https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap'}` | v1.35.0+ | +| NEXT_PUBLIC_FONT_FAMILY_BODY | `FontFamily`, see full description [below](#font-family-configuration-properties) | Main typeface to use in page content elements. | - | - | `{'name':'Raleway','url':'https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap'}` | v1.35.0+ | +| NEXT_PUBLIC_MAX_CONTENT_WIDTH_ENABLED | `boolean` | Set to `true` to restrict the page content width on extra-large screens. | - | `true` | `false` | v1.34.1+ | #### Network explorer configuration properties @@ -305,6 +328,13 @@ Settings for meta tags, OG tags and SEO | url | `string` | URL of the IDE with placeholders for contract hash (`{hash}`) and current domain (`{domain}`) | Required | - | `https://remix.blockscout.com/?address={hash}&blockscout={domain}` | | icon_url | `string` | URL of the IDE icon | Required | - | `https://example.com/icon.svg` | +#### Font family configuration properties + +| Variable | Type| Description | Compulsoriness | Default value | Example value | +| --- | --- | --- | --- | --- | --- | +| name | `string` | Font family name; used to define the `font-family` CSS property. | Required | - | `Montserrat` | +| url | `string` | URL for external font. Ensure the font supports the following weights: 400, 500, 600, and 700. | Required | - | `https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap` | +   ## App features @@ -316,9 +346,10 @@ Settings for meta tags, OG tags and SEO | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED | `boolean` | Set to true if network has account feature | Required | - | `true` | v1.0.x+ | -| NEXT_PUBLIC_AUTH0_CLIENT_ID | `string` | Client id for [Auth0](https://auth0.com/) provider | Required | - | `` | v1.0.x+ | -| NEXT_PUBLIC_AUTH_URL | `string` | Account auth base url; it is used for building login URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/auth0`) and logout return URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/logout`); if not provided the base app URL will be used instead | Required | - | `https://blockscout.com` | v1.0.x+ | -| NEXT_PUBLIC_LOGOUT_URL | `string` | Account logout url. Required if account is supported for the app instance. | Required | - | `https://blockscoutcom.us.auth0.com/v2/logout` | v1.0.x+ | +| NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY | `boolean` | See [below](ENVS.md#google-recaptcha) | Required | - | `` | v1.36.0+ | +| NEXT_PUBLIC_AUTH0_CLIENT_ID | `string` | **DEPRECATED** Client id for [Auth0](https://auth0.com/) provider | - | - | `` | v1.0.x+ | +| NEXT_PUBLIC_AUTH_URL | `string` | **DEPRECATED** Account auth base url; it is used for building login URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/auth0`) and logout return URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/logout`); if not provided the base app URL will be used instead | - | - | `https://blockscout.com` | v1.0.x+ | +| NEXT_PUBLIC_LOGOUT_URL | `string` | **DEPRECATED** Account logout url. Required if account is supported for the app instance. | - | - | `https://blockscoutcom.us.auth0.com/v2/logout` | v1.0.x+ |   @@ -407,6 +438,10 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi | NEXT_PUBLIC_ROLLUP_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` | v1.24.0+ | | NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals (Optimistic stack only) | Required for `optimistic` rollups | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0+ | | NEXT_PUBLIC_FAULT_PROOF_ENABLED | `boolean` | Set to `true` for chains with fault proof system enabled (Optimistic stack only) | - | - | `true` | v1.31.0+ | +| NEXT_PUBLIC_HAS_MUD_FRAMEWORK | `boolean` | Set to `true` for instances that use MUD framework (Optimistic stack only) | - | - | `true` | v1.33.0+ | +| NEXT_PUBLIC_ROLLUP_HOMEPAGE_SHOW_LATEST_BLOCKS | `boolean` | Set to `true` to display "Latest blocks" widget instead of "Latest batches" on the home page | - | - | `true` | v1.36.0+ | +| NEXT_PUBLIC_ROLLUP_OUTPUT_ROOTS_ENABLED | `boolean` | Enables "Output roots" page (Optimistic stack only) | - | `true` | `false` | v1.37.0+ | +| NEXT_PUBLIC_ROLLUP_PARENT_CHAIN_NAME | `string` | Set to customize L1 transaction status labels in the UI (e.g., "Sent to "). This setting is applicable only for Arbitrum-based chains. | - | - | `DuckChain` | v1.37.0+ |   @@ -414,7 +449,7 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | See [below](ENVS.md#google-recaptcha) | true | - | `` | v1.0.x+ | +| NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY | `string` | See [below](ENVS.md#google-recaptcha) | true | - | `` | v1.36.0+ |   @@ -444,19 +479,21 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi ### GraphQL API documentation -This feature is **always enabled**, but you can configure its behavior by passing the following variables. +This feature is **always enabled**, but you can disable it by passing `none` value to `NEXT_PUBLIC_GRAPHIQL_TRANSACTION` variable. | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_GRAPHIQL_TRANSACTION | `string` | Txn hash for default query at GraphQl playground page | - | - | `0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62` | v1.0.x+ | +| NEXT_PUBLIC_GRAPHIQL_TRANSACTION | `string` | Txn hash for default query at GraphQl playground page. Pass `none` to disable the feature. | - | - | `0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62` | v1.0.x+ |   ### REST API documentation +This feature is **always enabled**, but you can disable it by passing `none` value to `NEXT_PUBLIC_API_SPEC_URL` variable. + | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_API_SPEC_URL | `string` | Spec to be displayed on `/api-docs` page | Required | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` | v1.0.x+ | +| NEXT_PUBLIC_API_SPEC_URL | `string` | Spec to be displayed on `/api-docs` page. Pass `none` to disable the feature. | - | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` | v1.0.x+ |   @@ -464,7 +501,7 @@ This feature is **always enabled**, but you can configure its behavior by passin | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_MARKETPLACE_ENABLED | `boolean` | `true` means that the marketplace page will be enabled | - | - | `true` | v1.24.1+ | +| 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_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | Required | - | `https://airtable.com/shrqUAcjgGJ4jU88C` | v1.0.x+ | @@ -475,6 +512,9 @@ This feature is **always enabled**, but you can configure its behavior by passin | NEXT_PUBLIC_MARKETPLACE_FEATURED_APP | `string` | ID of the featured application to be displayed on the banner on the Marketplace page | - | - | `uniswap` | v1.29.0+ | | NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL | `string` | URL of the banner HTML content | - | - | `https://example.com/banner` | v1.29.0+ | | NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL | `string` | URL of the page the banner leads to | - | - | `https://example.com` | v1.29.0+ | +| NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY | `string` | Airtable API key | - | - | - | v1.33.0+ | +| NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID | `string` | Airtable base ID with dapp ratings | - | - | - | v1.33.0+ | +| NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL | `string` | URL of the file (`.json` format only) which contains the list of The Graph links to be displayed on the Marketplace page | - | - | `https://example.com/graph_links.json` | v1.36.0+ | #### Marketplace app configuration properties @@ -624,6 +664,38 @@ For the smart contract addresses which are [Safe{Core} accounts](https://safe.gl   +### Address profile API + +This feature allows the integration of an external API to fetch user info for addresses or contracts. When configured, if the API returns a username, a public tag with a custom link will be displayed in the address page header. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_ADDRESS_USERNAME_TAG | `{api_url: string; tag_link_template: string; tag_icon: string; tag_bg_color: string; tag_text_color: string}` | Address profile API tag configuration properties. See [below](#user-profile-api-configuration-properties). | - | - | `uniswap` | v1.35.0+ | + +  + +#### Address profile API configuration properties + +| Variable | Type| Description | Compulsoriness | Default value | Example value | +| --- | --- | --- | --- | --- | --- | +| api_url_template | `string` | User profile API URL. Should be a template with `{address}` variable | Required | - | `https://example-api.com/{address}` | +| tag_link_template | `string` | External link to the profile. Should be a template with `{username}` variable | - | - | `https://example.com/{address}` | +| tag_icon | `string` | Public tag icon (.svg) url | - | - | `https://example.com/icon.svg` | +| tag_bg_color | `string` | Public tag background color (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | - | `\#000000` | +| tag_text_color | `string` | Public tag text color (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | - | `\#FFFFFF` | + +  + +### Address XStar XHS score + +This feature allows the integration of an XStar API to fetch XHS score for addresses. When configured, if the API returns a score, a public tag with that score will be displayed in the address page header. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_XSTAR_SCORE_URL | `string` | XStar XHS score documentation URL for the address tag. Enables the XStar score feature. | - | - | `https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm` | v1.36.0+ | + +  + ### SUAVE chain For blockchains that implement SUAVE architecture additional fields will be shown on the transaction page ("Allowed peekers", "Kettle"). Users also will be able to see the list of all transactions for a particular Kettle in the separate view. @@ -634,6 +706,17 @@ For blockchains that implement SUAVE architecture additional fields will be show   +### Celo chain + +For blockchains that use the Celo platform. _Note_, that once the Celo mainnet becomes an L2 chain, these variables will be migrated to the Rollup configuration section. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_CELO_ENABLED | `boolean` | Indicates that it is a Celo-based chain. | - | - | `true` | v1.37.0+ | +| NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK | `number` | Indicates the block number when the Celo-type chain transitioned to L2. This is used to display links to the Epoch block page from a regular block page. | - | - | `26369280` | v1.37.0+ | + +  + ### MetaSuites extension Enables [MetaSuites browser extension](https://github.com/blocksecteam/metasuites) to integrate with the app views. @@ -650,7 +733,7 @@ The feature enables the Validators page which provides detailed information abou | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE | `'stability'` | Chain type | Required | - | `'stability'` | v1.25.0+ | +| NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE | `'stability' \| 'blackfort'` | Chain type | Required | - | `'stability'` | v1.25.0+ |   @@ -692,7 +775,7 @@ If the feature is enabled, a Multichain balance button will be displayed on the | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG | `{ name: string; url_template: string; dapp_id?: string; logo?: string }` | Multichain portfolio application config See [below](#multichain-button-configuration-properties) | - | - | `{ name: 'zerion', url_template: 'https://app.zerion.io/{address}/overview', logo: 'https://example.com/icon.svg'` | v1.31.0+ | +| NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG | `[{ name: string; url_template: string; dapp_id?: string; logo: string }]` | Multichain portfolio application config See [below](#multichain-button-configuration-properties) | - | - | `[{ name: 'zerion', url_template: 'https://app.zerion.io/{address}/overview', logo: 'https://example.com/icon.svg'}]` | v1.31.0+ |   @@ -707,6 +790,45 @@ If the feature is enabled, a Multichain balance button will be displayed on the   +### Get gas button + +If the feature is enabled, a Get gas button will be displayed in the top bar, which will take you to the gas refuel application in the marketplace or to an external site. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG | `{ name: string; url_template: string; dapp_id?: string; logo?: string }` | Get gas button config. See [below](#get-gas-button-configuration-properties) | - | - | `{ name: 'Need gas?', dapp_id: 'smol-refuel', url_template: 'https://smolrefuel.com/?outboundChain={chainId}', logo: 'https://example.com/icon.png' }` | v1.33.0+ | + +  + +#### Get gas button configuration properties + +| Variable | Type| Description | Compulsoriness | Default value | Example value | +| --- | --- | --- | --- | --- | --- | +| name | `string` | Text on the button | Required | - | `Need gas?` | +| url_template | `string` | Url template, may contain `{chainId}` variable | Required | - | `https://smolrefuel.com/?outboundChain={chainId}` | +| dapp_id | `string` | Set for open a Blockscout dapp page instead of opening external app page | - | - | `smol-refuel` | +| logo | `string` | Gas refuel application logo url | - | - | `https://example.com/icon.png` | + +  + +### Save on gas with GasHawk + +The feature enables a "Save with GasHawk" button next to the "Gas used" value on the address page. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_SAVE_ON_GAS_ENABLED | `boolean` | Set to "true" to enable the feature | - | - | `true` | v1.35.0+ | + +  + +### Rewards service API + +This feature enables Blockscout Merits program. It requires that the [My account](ENVS.md#my-account) and [Blockchain interaction](ENVS.md#blockchain-interaction-writing-to-contract-etc) features are also enabled. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_REWARDS_SERVICE_API_HOST | `string` | API URL | - | - | `https://example.com` | v1.36.0+ | + ## External services configuration ### Google ReCaptcha @@ -715,4 +837,5 @@ For obtaining the variables values please refer to [reCAPTCHA documentation](htt | Variable | Type| Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | Site key | - | - | `` | v1.0.x+ | +| NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY | `string` | Google reCAPTCHA v3 site key | - | - | `` | v1.36.0+ | +| NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY | `string` | **DEPRECATED** Google reCAPTCHA v2 site key | - | - | `` | v1.0.x+ | diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000000..9f7ae682b6 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,442 @@ +import { includeIgnoreFile } from '@eslint/compat'; +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 importPlugin from 'eslint-plugin-import'; +import importHelpersPlugin from 'eslint-plugin-import-helpers'; +import jestPlugin from 'eslint-plugin-jest'; +import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'; +import noCyrillicStringPlugin from 'eslint-plugin-no-cyrillic-string'; +import playwrightPlugin from 'eslint-plugin-playwright'; +import reactPlugin from 'eslint-plugin-react'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; +import * as regexpPlugin from 'eslint-plugin-regexp'; +import globals from 'globals'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import tseslint from 'typescript-eslint'; + +const RESTRICTED_MODULES = { + paths: [ + { name: 'dayjs', message: 'Please use lib/date/dayjs.ts instead of directly importing dayjs' }, + { name: '@chakra-ui/icons', message: 'Using @chakra-ui/icons is prohibited. Please use regular svg-icon instead (see examples in "icons/" folder)' }, + { name: '@metamask/providers', message: 'Please lazy-load @metamask/providers or use useProvider hook instead' }, + { name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' }, + { name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' }, + { + name: '@chakra-ui/react', + importNames: [ 'Popover', 'Menu', 'PinInput', 'useToast' ], + message: 'Please use corresponding component or hook from ui/shared/chakra component instead', + }, + { + name: 'lodash', + message: 'Please use `import [package] from \'lodash/[package]\'` instead.', + }, + ], + patterns: [ + 'icons/*', + '!lodash/*', + ], +}; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const gitignorePath = path.resolve(__dirname, '.gitignore'); + +/** @type {import('eslint').Linter.Config[]} */ +export default tseslint.config( + includeIgnoreFile(gitignorePath), + + { files: [ '**/*.{js,mjs,cjs,ts,jsx,tsx}', '**/*.pw.tsx' ] }, + + { ignores: [ + 'deploy/tools/', + 'public/', + 'theme/dist/', + '.git/', + 'theme/webpack.config.js', + 'next.config.js', + ] }, + + { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, + + { settings: { react: { version: 'detect' } } }, + + jsPlugin.configs.recommended, + + { + plugins: { + '@typescript-eslint': tseslint.plugin, + jest: jestPlugin, + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: true, + }, + globals: jestPlugin.environments.globals.globals, + }, + rules: { + '@typescript-eslint/array-type': [ 'error', { + 'default': 'generic', + readonly: 'generic', + } ], + '@typescript-eslint/consistent-type-imports': [ 'error' ], + '@typescript-eslint/naming-convention': [ 'error', + { + selector: 'default', + format: [ 'camelCase' ], + leadingUnderscore: 'allow', + trailingUnderscore: 'forbid', + }, + { + selector: 'import', + leadingUnderscore: 'allow', + format: [ 'camelCase', 'PascalCase' ], + }, + { + selector: 'class', + format: [ 'PascalCase' ], + }, + { + selector: 'enum', + format: [ 'PascalCase', 'UPPER_CASE' ], + }, + { + selector: 'enumMember', + format: [ 'camelCase', 'PascalCase', 'UPPER_CASE' ], + }, + { + selector: 'function', + format: [ 'camelCase', 'PascalCase' ], + }, + { + selector: 'interface', + format: [ 'PascalCase' ], + }, + { + selector: 'method', + format: [ 'camelCase', 'snake_case', 'UPPER_CASE' ], + leadingUnderscore: 'allow', + }, + { + selector: 'parameter', + format: [ 'camelCase', 'PascalCase' ], + leadingUnderscore: 'allow', + }, + { + selector: 'property', + format: null, + }, + { + selector: 'typeAlias', + format: [ 'PascalCase' ], + }, + { + selector: 'typeLike', + format: [ 'PascalCase' ], + }, + { + selector: 'typeParameter', + format: [ 'PascalCase', 'UPPER_CASE' ], + }, + { + selector: 'variable', + format: [ 'camelCase', 'PascalCase', 'UPPER_CASE' ], + leadingUnderscore: 'allow', + }, + ], + '@typescript-eslint/no-empty-function': [ 'off' ], + '@typescript-eslint/no-unused-vars': [ 'error', { caughtErrors: 'none', ignoreRestSiblings: true } ], + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-useless-constructor': [ 'error' ], + '@typescript-eslint/no-explicit-any': [ 'error', { ignoreRestArgs: true } ], + '@typescript-eslint/no-unused-expressions': [ 'error', { + allowShortCircuit: true, + allowTernary: true, + } ], + }, + }, + { + // disable type-aware linting on JS files + files: [ '**/*.{js,mjs}' ], + ...tseslint.configs.disableTypeChecked, + }, + + { + plugins: { + react: reactPlugin, + }, + rules: { + 'react/jsx-key': 'error', + 'react/jsx-no-bind': [ 'error', { + ignoreRefs: true, + } ], + 'react/jsx-curly-brace-presence': [ 'error', { + props: 'never', + children: 'never', + } ], + 'react/jsx-curly-spacing': [ 'error', { + when: 'always', + children: true, + spacing: { + objectLiterals: 'never', + }, + } ], + 'react/jsx-equals-spacing': [ 'error', 'never' ], + 'react/jsx-fragments': [ 'error', 'syntax' ], + 'react/jsx-no-duplicate-props': 'error', + 'react/jsx-no-target-blank': 'off', + 'react/jsx-no-useless-fragment': 'error', + 'react/jsx-tag-spacing': [ 'error', { + afterOpening: 'never', + beforeSelfClosing: 'never', + closingSlash: 'never', + } ], + 'react/jsx-wrap-multilines': [ 'error', { + declaration: 'parens-new-line', + assignment: 'parens-new-line', + 'return': 'parens-new-line', + arrow: 'parens-new-line', + condition: 'parens-new-line', + logical: 'parens-new-line', + prop: 'parens-new-line', + } ], + 'react/no-access-state-in-setstate': 'error', + 'react/no-deprecated': 'error', + 'react/no-direct-mutation-state': 'error', + 'react/no-find-dom-node': 'off', + 'react/no-redundant-should-component-update': 'error', + 'react/no-render-return-value': 'error', + 'react/no-string-refs': 'off', + 'react/no-unknown-property': 'error', + 'react/no-unused-state': 'error', + 'react/require-optimization': [ 'error' ], + 'react/void-dom-elements-no-children': 'error', + }, + }, + + { + plugins: { + '@next/next': nextJsPlugin, + }, + rules: { + ...nextJsPlugin.configs.recommended.rules, + ...nextJsPlugin.configs['core-web-vitals'].rules, + }, + }, + + { + plugins: { '@tanstack/query': reactQueryPlugin }, + }, + + { + ...playwrightPlugin.configs['flat/recommended'], + files: [ '**/*.pw.tsx' ], + rules: { + ...playwrightPlugin.configs['flat/recommended'].rules, + 'playwright/no-standalone-expect': 'off', // this rules does not work correctly with extended test functions + }, + }, + + { + plugins: { 'react-hooks': reactHooksPlugin }, + ignores: [ '**/*.pw.tsx', 'playwright/**' ], + rules: { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'error', + }, + }, + + { + files: [ '**/*.test.{ts,js,jsx,tsx}' ], + plugins: { jest: jestPlugin }, + languageOptions: { + globals: jestPlugin.environments.globals.globals, + }, + }, + + regexpPlugin.configs['flat/recommended'], + + { + plugins: { + 'import': importPlugin, + }, + rules: { + 'import/no-duplicates': 'error', + }, + }, + + { + plugins: { + 'import-helpers': importHelpersPlugin, + }, + rules: { + 'import-helpers/order-imports': [ + 'error', + { + newlinesBetween: 'always', + groups: [ + 'module', + '/types/', + [ '/^nextjs/' ], + [ + '/^configs/', + '/^data/', + '/^deploy/', + '/^icons/', + '/^jest/', + '/^lib/', + '/^mocks/', + '/^pages/', + '/^playwright/', + '/^stubs/', + '/^theme/', + '/^ui/', + ], + [ 'parent', 'sibling', 'index' ], + ], + alphabetize: { order: 'asc', ignoreCase: true }, + }, + ], + }, + }, + + { + plugins: { + 'no-cyrillic-string': noCyrillicStringPlugin, + }, + rules: { + 'no-cyrillic-string/no-cyrillic-string': 'error', + }, + }, + + { + plugins: { + 'jsx-a11y': jsxA11yPlugin, + }, + languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } }, + }, + + { + plugins: { + '@stylistic': stylisticPlugin, + }, + rules: { + // replacement for @typescript-eslint + '@stylistic/indent': [ 'error', 2 ], + '@stylistic/brace-style': [ 'error', '1tbs' ], + '@stylistic/member-delimiter-style': [ 'error' ], + '@stylistic/type-annotation-spacing': 'error', + + // replacement for eslint + '@stylistic/array-bracket-spacing': [ 'error', 'always' ], + '@stylistic/arrow-spacing': [ 'error', { before: true, after: true } ], + '@stylistic/comma-dangle': [ 'error', 'always-multiline' ], + '@stylistic/comma-spacing': [ 'error' ], + '@stylistic/comma-style': [ 'error', 'last' ], + '@stylistic/curly-newline': [ 'error', { multiline: true, minElements: 1 } ], + '@stylistic/eol-last': 'error', + '@stylistic/jsx-quotes': [ 'error', 'prefer-double' ], + '@stylistic/key-spacing': [ 'error', { + beforeColon: false, + afterColon: true, + } ], + '@stylistic/keyword-spacing': 'error', + '@stylistic/linebreak-style': [ 'error', 'unix' ], + '@stylistic/lines-around-comment': [ 'error', { + beforeBlockComment: true, + allowBlockStart: true, + } ], + '@stylistic/no-mixed-operators': [ 'error', { + groups: [ + [ '&&', '||' ], + ], + } ], + '@stylistic/no-mixed-spaces-and-tabs': 'error', + '@stylistic/no-multiple-empty-lines': [ 'error', { + max: 1, + maxEOF: 0, + maxBOF: 0, + } ], + '@stylistic/no-multi-spaces': 'error', + '@stylistic/no-trailing-spaces': 'error', + '@stylistic/object-curly-spacing': [ 'error', 'always' ], + '@stylistic/operator-linebreak': [ 'error', 'after' ], + '@stylistic/quote-props': [ 'error', 'as-needed', { + keywords: true, + numbers: true, + } ], + '@stylistic/quotes': [ 'error', 'single', { + allowTemplateLiterals: true, + } ], + '@stylistic/semi': [ 'error', 'always' ], + '@stylistic/space-before-function-paren': [ 'error', 'never' ], + '@stylistic/space-before-blocks': [ 'error', 'always' ], + '@stylistic/space-in-parens': [ 'error', 'never' ], + '@stylistic/space-infix-ops': 'error', + '@stylistic/space-unary-ops': 'off', + '@stylistic/template-curly-spacing': [ 'error', 'always' ], + '@stylistic/wrap-iife': [ 'error', 'inside' ], + }, + }, + + { + rules: { + // disabled in favor of @typescript-eslint and @stylistic + 'no-use-before-define': 'off', + 'no-useless-constructor': 'off', + 'no-unused-vars': 'off', + 'no-empty': [ 'error', { allowEmptyCatch: true } ], + 'no-unused-expressions': 'off', + + // this is checked by typescript compiler + 'no-redeclare': 'off', + + // rules customizations + eqeqeq: [ 'error', 'allow-null' ], + 'id-match': [ 'error', '^[\\w$]+$' ], + 'max-len': [ 'error', 160, 4 ], + 'no-console': 'error', + 'no-implicit-coercion': [ 'error', { + number: true, + 'boolean': true, + string: true, + } ], + 'no-nested-ternary': 'error', + 'no-multi-str': 'error', + 'no-spaced-func': 'error', + 'no-with': 'error', + 'object-shorthand': 'off', + 'one-var': [ 'error', 'never' ], + 'prefer-const': 'error', + + // restricted imports and properties + 'no-restricted-imports': [ 'error', RESTRICTED_MODULES ], + 'no-restricted-properties': [ 2, { + object: 'process', + property: 'env', + // FIXME: restrict the rule only NEXT_PUBLIC variables + message: 'Please use configs/app/index.ts to import any NEXT_PUBLIC environment variables. For other properties please disable this rule for a while.', + } ], + }, + }, + { + files: [ + 'pages/**', + 'nextjs/**', + 'playwright/**', + 'deploy/tools/**', + 'middleware.ts', + 'instrumentation*.ts', + '*.config.ts', + '*.config.js', + ], + rules: { + // for configs allow to consume env variables from process.env directly + 'no-restricted-properties': 'off', + }, + }, +); diff --git a/global.d.ts b/global.d.ts index 1632505b52..94cfa4677d 100644 --- a/global.d.ts +++ b/global.d.ts @@ -4,7 +4,7 @@ type CPreferences = { zone: string; width: string; height: string; -} +}; declare global { export interface Window { diff --git a/icons/API_slim.svg b/icons/API_slim.svg new file mode 100644 index 0000000000..f4b36f08ba --- /dev/null +++ b/icons/API_slim.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/MUD.svg b/icons/MUD.svg new file mode 100644 index 0000000000..8ab1229a71 --- /dev/null +++ b/icons/MUD.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/MUD_menu.svg b/icons/MUD_menu.svg new file mode 100644 index 0000000000..c30c571c47 --- /dev/null +++ b/icons/MUD_menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/apps_slim.svg b/icons/apps_slim.svg new file mode 100644 index 0000000000..59e2f2d818 --- /dev/null +++ b/icons/apps_slim.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/apps_xs.svg b/icons/apps_xs.svg deleted file mode 100644 index 4daa74955d..0000000000 --- a/icons/apps_xs.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/block_countdown.svg b/icons/block_countdown.svg new file mode 100644 index 0000000000..0024e52ce9 --- /dev/null +++ b/icons/block_countdown.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/brands/celenium.svg b/icons/brands/celenium.svg new file mode 100644 index 0000000000..8a5d645ac2 --- /dev/null +++ b/icons/brands/celenium.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/icons/brands/graph.svg b/icons/brands/graph.svg new file mode 100644 index 0000000000..bd3cc916d9 --- /dev/null +++ b/icons/brands/graph.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/checkered_flag.svg b/icons/checkered_flag.svg new file mode 100644 index 0000000000..918c29cea4 --- /dev/null +++ b/icons/checkered_flag.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/clock-light.svg b/icons/clock-light.svg index 785174078b..9b18072460 100644 --- a/icons/clock-light.svg +++ b/icons/clock-light.svg @@ -1,11 +1,3 @@ - - - - - - - - - - + + diff --git a/icons/email-sent.svg b/icons/email-sent.svg deleted file mode 100644 index d31e30f244..0000000000 --- a/icons/email-sent.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/gas.svg b/icons/gas.svg index d199870ab9..4334fe93f5 100644 --- a/icons/gas.svg +++ b/icons/gas.svg @@ -1,3 +1,10 @@ - + + + + + + + + diff --git a/icons/heart_filled.svg b/icons/heart_filled.svg new file mode 100644 index 0000000000..80926b1668 --- /dev/null +++ b/icons/heart_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/heart_outline.svg b/icons/heart_outline.svg new file mode 100644 index 0000000000..8bf7ce3e36 --- /dev/null +++ b/icons/heart_outline.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/hourglass.svg b/icons/hourglass.svg new file mode 100644 index 0000000000..7ebd6d78b2 --- /dev/null +++ b/icons/hourglass.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/link_external.svg b/icons/link_external.svg new file mode 100644 index 0000000000..dbddf710bc --- /dev/null +++ b/icons/link_external.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/merits.svg b/icons/merits.svg new file mode 100644 index 0000000000..91b128a2b9 --- /dev/null +++ b/icons/merits.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/merits_colored.svg b/icons/merits_colored.svg new file mode 100644 index 0000000000..4006f7e43a --- /dev/null +++ b/icons/merits_colored.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/icons/merits_slim.svg b/icons/merits_slim.svg new file mode 100644 index 0000000000..8a0623a169 --- /dev/null +++ b/icons/merits_slim.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/merits_with_dot.svg b/icons/merits_with_dot.svg new file mode 100644 index 0000000000..e216115e4e --- /dev/null +++ b/icons/merits_with_dot.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/merits_with_dot_slim.svg b/icons/merits_with_dot_slim.svg new file mode 100644 index 0000000000..4b5bf8a0aa --- /dev/null +++ b/icons/merits_with_dot_slim.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/private_tags_slim.svg b/icons/private_tags_slim.svg new file mode 100644 index 0000000000..538c47d054 --- /dev/null +++ b/icons/private_tags_slim.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/icons/share.svg b/icons/share.svg new file mode 100644 index 0000000000..f1124e47d0 --- /dev/null +++ b/icons/share.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/sign_out.svg b/icons/sign_out.svg new file mode 100644 index 0000000000..d5b68ecdab --- /dev/null +++ b/icons/sign_out.svg @@ -0,0 +1,4 @@ + + + + diff --git a/icons/star_filled.svg b/icons/star_filled.svg index 2bdea23a41..7b6312c876 100644 --- a/icons/star_filled.svg +++ b/icons/star_filled.svg @@ -1,3 +1,3 @@ - - + + diff --git a/icons/star_outline.svg b/icons/star_outline.svg index bf2eca9845..05286fa1d5 100644 --- a/icons/star_outline.svg +++ b/icons/star_outline.svg @@ -1,3 +1,3 @@ - + diff --git a/icons/token-transfers.svg b/icons/token-transfers.svg new file mode 100644 index 0000000000..f3bef44d80 --- /dev/null +++ b/icons/token-transfers.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/icons/verified_slim.svg b/icons/verified_slim.svg new file mode 100644 index 0000000000..a13930aab2 --- /dev/null +++ b/icons/verified_slim.svg @@ -0,0 +1,4 @@ + + + + diff --git a/instrumentation.node.ts b/instrumentation.node.ts index 5cae612a92..a42232f4b8 100644 --- a/instrumentation.node.ts +++ b/instrumentation.node.ts @@ -10,7 +10,7 @@ import { } from '@opentelemetry/sdk-metrics'; import { NodeSDK } from '@opentelemetry/sdk-node'; import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION, SEMRESATTRS_SERVICE_INSTANCE_ID } from '@opentelemetry/semantic-conventions'; diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO); @@ -18,9 +18,9 @@ const traceExporter = new OTLPTraceExporter(); const sdk = new NodeSDK({ resource: new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: 'blockscout_frontend', - [SemanticResourceAttributes.SERVICE_VERSION]: process.env.NEXT_PUBLIC_GIT_TAG || process.env.NEXT_PUBLIC_GIT_COMMIT_SHA || 'unknown_version', - [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: + [SEMRESATTRS_SERVICE_NAME]: 'blockscout_frontend', + [SEMRESATTRS_SERVICE_VERSION]: process.env.NEXT_PUBLIC_GIT_TAG || process.env.NEXT_PUBLIC_GIT_COMMIT_SHA || 'unknown_version', + [SEMRESATTRS_SERVICE_INSTANCE_ID]: process.env.NEXT_PUBLIC_APP_INSTANCE || process.env.NEXT_PUBLIC_APP_HOST?.replace('.blockscout.com', '').replaceAll('-', '_') || 'unknown_app', diff --git a/instrumentation.ts b/instrumentation.ts index c5384e36a7..dc366667cb 100644 --- a/instrumentation.ts +++ b/instrumentation.ts @@ -1,5 +1,5 @@ export async function register() { - if (process.env.NEXT_RUNTIME === 'nodejs') { + if (process.env.NEXT_RUNTIME === 'nodejs' && process.env.NEXT_OPEN_TELEMETRY_ENABLED === 'true') { await import('./instrumentation.node'); } } diff --git a/jest.config.ts b/jest.config.ts index d87d585507..d718d56a7b 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-len */ import type { JestConfigWithTsJest } from 'ts-jest'; /* diff --git a/jest/lib.tsx b/jest/lib.tsx index deafc637c6..cfbed98138 100644 --- a/jest/lib.tsx +++ b/jest/lib.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { AppContextProvider } from 'lib/contexts/app'; import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection'; import { SocketProvider } from 'lib/socket/context'; -import theme from 'theme'; +import theme from 'theme/theme'; import 'lib/setLocale'; @@ -20,7 +20,7 @@ const PAGE_PROPS = { apiData: null, }; -const TestApp = ({ children }: {children: React.ReactNode}) => { +const TestApp = ({ children }: { children: React.ReactNode }) => { const [ queryClient ] = React.useState(() => new QueryClient({ defaultOptions: { queries: { diff --git a/lib/address/bech32.ts b/lib/address/bech32.ts new file mode 100644 index 0000000000..85fcfda3ed --- /dev/null +++ b/lib/address/bech32.ts @@ -0,0 +1,49 @@ +import { bech32 } from '@scure/base'; + +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 + +export function toBech32Address(hash: string) { + if (config.UI.views.address.hashFormat.bech32Prefix) { + try { + const words = bech32.toWords(hexToBytes(hash)); + return bech32.encode(config.UI.views.address.hashFormat.bech32Prefix, words); + } catch (error) {} + } + + return hash; +} + +export function isBech32Address(hash: string) { + if (!config.UI.views.address.hashFormat.bech32Prefix) { + return false; + } + + if (!hash.startsWith(`${ config.UI.views.address.hashFormat.bech32Prefix }${ BECH_32_SEPARATOR }`)) { + return false; + } + + const strippedHash = hash.replace(`${ config.UI.views.address.hashFormat.bech32Prefix }${ BECH_32_SEPARATOR }`, ''); + return DATA_PART_REGEXP.test(strippedHash); +} + +export function fromBech32Address(hash: string) { + if (config.UI.views.address.hashFormat.bech32Prefix) { + try { + const { words, prefix } = bech32.decode(hash as `${ string }${ typeof BECH_32_SEPARATOR }${ string }`); + + if (prefix !== config.UI.views.address.hashFormat.bech32Prefix) { + return hash; + } + + const bytes = bech32.fromWords(words); + return bytesToHex(bytes); + } catch (error) {} + } + + return hash; +} diff --git a/lib/address/getCheckedSummedAddress.ts b/lib/address/getCheckedSummedAddress.ts new file mode 100644 index 0000000000..6cf744620d --- /dev/null +++ b/lib/address/getCheckedSummedAddress.ts @@ -0,0 +1,9 @@ +import { getAddress } from 'viem'; + +export default function getCheckedSummedAddress(address: string): string { + try { + return getAddress(address); + } catch (error) { + return address; + } +} diff --git a/lib/address/parseMetaPayload.ts b/lib/address/parseMetaPayload.ts index e8b067b148..f8d6fbc570 100644 --- a/lib/address/parseMetaPayload.ts +++ b/lib/address/parseMetaPayload.ts @@ -27,6 +27,10 @@ export default function parseMetaPayload(meta: AddressMetadataTag['meta']): Addr 'appLogoURL', 'appActionButtonText', 'warpcastHandle', + 'data', + 'alertBgColor', + 'alertTextColor', + 'alertStatus', ]; for (const stringField of stringFields) { diff --git a/lib/api/buildUrl.ts b/lib/api/buildUrl.ts index 07805dbd9c..ea7e9fac3f 100644 --- a/lib/api/buildUrl.ts +++ b/lib/api/buildUrl.ts @@ -10,11 +10,12 @@ export default function buildUrl( resourceName: R, pathParams?: ResourcePathParams, queryParams?: Record | number | boolean | null | undefined>, + noProxy?: boolean, ): string { const resource: ApiResource = RESOURCES[resourceName]; - const baseUrl = isNeedProxy() ? config.app.baseUrl : (resource.endpoint || config.api.endpoint); + const baseUrl = !noProxy && isNeedProxy() ? config.app.baseUrl : (resource.endpoint || config.api.endpoint); const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath; - const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path; + const path = !noProxy && isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path; const url = new URL(compile(path)(pathParams), baseUrl); queryParams && Object.entries(queryParams).forEach(([ key, value ]) => { diff --git a/lib/api/resources.ts b/lib/api/resources.ts index ee25a7ef09..7a728202fd 100644 --- a/lib/api/resources.ts +++ b/lib/api/resources.ts @@ -31,9 +31,16 @@ import type { AddressNFTsResponse, AddressCollectionsResponse, AddressNFTTokensFilter, - AddressCoinBalanceHistoryChartOld, + AddressMudTables, + AddressMudTablesFilter, + AddressMudRecords, + AddressMudRecordsFilter, + AddressMudRecordsSorting, + AddressMudRecord, + AddressEpochRewardsResponse, + AddressXStarResponse, } from 'types/api/address'; -import type { AddressesResponse } from 'types/api/addresses'; +import type { AddressesResponse, AddressesMetadataSearchResult, AddressesMetadataSearchFilters } from 'types/api/addresses'; import type { AddressMetadataInfo, PublicTagTypesResponse } from 'types/api/addressMetadata'; import type { ArbitrumL2MessagesResponse, @@ -41,16 +48,28 @@ import type { ArbitrumL2TxnBatchesResponse, ArbitrumL2BatchTxs, ArbitrumL2BatchBlocks, + ArbitrumL2TxnBatchesItem, + ArbitrumLatestDepositsResponse, } from 'types/api/arbitrumL2'; import type { TxBlobs, Blob } from 'types/api/blobs'; -import type { BlocksResponse, BlockTransactionsResponse, Block, BlockFilters, BlockWithdrawalsResponse } from 'types/api/block'; +import type { + BlocksResponse, + BlockTransactionsResponse, + Block, + BlockFilters, + BlockWithdrawalsResponse, + BlockCountdownResponse, + BlockEpoch, + BlockEpochElectionRewardDetailsResponse, +} from 'types/api/block'; import type { ChartMarketResponse, ChartSecondaryCoinPriceResponse, ChartTransactionResponse } from 'types/api/charts'; -import type { BackendVersionConfig } from 'types/api/configs'; +import type { BackendVersionConfig, CsvExportConfig } from 'types/api/configs'; import type { SmartContract, - SmartContractVerificationConfig, - SolidityscanReport, + SmartContractVerificationConfigRaw, SmartContractSecurityAudits, + SmartContractMudSystemsResponse, + SmartContractMudSystemInfo, } from 'types/api/contract'; import type { VerifiedContractsResponse, VerifiedContractsFilters, VerifiedContractsCounters } from 'types/api/contracts'; import type { @@ -61,6 +80,7 @@ import type { import type { IndexingStatus } from 'types/api/indexingStatus'; import type { InternalTransactionsResponse } from 'types/api/internalTransaction'; import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log'; +import type { MudWorldsResponse } from 'types/api/mudWorlds'; import type { NovesAccountHistoryResponse, NovesDescribeTxsResponse, NovesResponseData } from 'types/api/noves'; import type { OptimisticL2DepositsResponse, @@ -69,8 +89,22 @@ import type { OptimisticL2TxnBatchesResponse, OptimisticL2WithdrawalsResponse, OptimisticL2DisputeGamesResponse, + OptimismL2TxnBatch, + OptimismL2BatchTxs, + OptimismL2BatchBlocks, } from 'types/api/optimisticL2'; import type { RawTracesResponse } from 'types/api/rawTrace'; +import type { + RewardsConfigResponse, + RewardsCheckRefCodeResponse, + RewardsNonceResponse, + RewardsCheckUserResponse, + RewardsLoginResponse, + RewardsUserBalancesResponse, + RewardsUserDailyCheckResponse, + RewardsUserDailyClaimResponse, + RewardsUserReferralsResponse, +} from 'types/api/rewards'; import type { SearchRedirectResult, SearchResult, SearchResultFilters, SearchResultItem } from 'types/api/search'; import type { ShibariumWithdrawalsResponse, ShibariumDepositsResponse } from 'types/api/shibarium'; import type { HomeStats } from 'types/api/stats'; @@ -99,7 +133,15 @@ import type { TxInterpretationResponse } from 'types/api/txInterpretation'; import type { TTxsFilters, TTxsWithBlobsFilters } from 'types/api/txsFilters'; import type { TxStateChanges } from 'types/api/txStateChanges'; import type { UserOpsResponse, UserOp, UserOpsFilters, UserOpsAccount } from 'types/api/userOps'; -import type { ValidatorsCountersResponse, ValidatorsFilters, ValidatorsResponse, ValidatorsSorting } from 'types/api/validators'; +import type { + ValidatorsStabilityCountersResponse, + ValidatorsStabilityFilters, + ValidatorsStabilityResponse, + ValidatorsStabilitySorting, + ValidatorsBlackfortCountersResponse, + ValidatorsBlackfortResponse, + ValidatorsBlackfortSorting, +} from 'types/api/validators'; import type { VerifiedContractsSorting } from 'types/api/verifiedContracts'; import type { WithdrawalsResponse, WithdrawalsCounters } from 'types/api/withdrawals'; import type { @@ -138,30 +180,27 @@ export const RESOURCES = { user_info: { path: '/api/account/v2/user/info', }, - email_resend: { - path: '/api/account/v2/email/resend', - }, custom_abi: { - path: '/api/account/v2/user/custom_abis/:id?', + path: '/api/account/v2/user/custom_abis{/:id}', pathParams: [ 'id' as const ], }, watchlist: { - path: '/api/account/v2/user/watchlist/:id?', + path: '/api/account/v2/user/watchlist{/:id}', pathParams: [ 'id' as const ], filterFields: [ ], }, private_tags_address: { - path: '/api/account/v2/user/tags/address/:id?', + path: '/api/account/v2/user/tags/address{/:id}', pathParams: [ 'id' as const ], filterFields: [ ], }, private_tags_tx: { - path: '/api/account/v2/user/tags/transaction/:id?', + path: '/api/account/v2/user/tags/transaction{/:id}', pathParams: [ 'id' as const ], filterFields: [ ], }, api_keys: { - path: '/api/account/v2/user/api_keys/:id?', + path: '/api/account/v2/user/api_keys{/:id}', pathParams: [ 'id' as const ], }, @@ -191,13 +230,33 @@ export const RESOURCES = { }, token_info_applications: { - path: '/api/v1/chains/:chainId/token-info-submissions/:id?', + path: '/api/v1/chains/:chainId/token-info-submissions{/:id}', pathParams: [ 'chainId' as const, 'id' as const ], endpoint: getFeaturePayload(config.features.addressVerification)?.api.endpoint, basePath: getFeaturePayload(config.features.addressVerification)?.api.basePath, needAuth: true, }, + // AUTH + auth_send_otp: { + path: '/api/account/v2/send_otp', + }, + auth_confirm_otp: { + path: '/api/account/v2/confirm_otp', + }, + auth_siwe_message: { + path: '/api/account/v2/siwe_message', + }, + auth_siwe_verify: { + path: '/api/account/v2/authenticate_via_wallet', + }, + auth_link_email: { + path: '/api/account/v2/email/link', + }, + auth_link_address: { + path: '/api/account/v2/address/link', + }, + // STATS MICROSERVICE API stats_counters: { path: '/api/v1/counters', @@ -224,6 +283,12 @@ export const RESOURCES = { basePath: getFeaturePayload(config.features.nameService)?.api.basePath, filterFields: [ 'address' as const, 'resolved_to' as const, 'owned_by' as const, 'only_active' as const, 'protocols' as const ], }, + address_domain: { + path: '/api/v1/:chainId/addresses/:address', + pathParams: [ 'chainId' as const, 'address' as const ], + endpoint: getFeaturePayload(config.features.nameService)?.api.endpoint, + basePath: getFeaturePayload(config.features.nameService)?.api.basePath, + }, domain_info: { path: '/api/v1/:chainId/domains/:name', pathParams: [ 'chainId' as const, 'name' as const ], @@ -294,6 +359,60 @@ export const RESOURCES = { basePath: marketplaceApi?.basePath, }, + // REWARDS SERVICE + rewards_config: { + path: '/api/v1/config', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_check_ref_code: { + path: '/api/v1/auth/code/:code', + pathParams: [ 'code' as const ], + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_nonce: { + path: '/api/v1/auth/nonce', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_check_user: { + path: '/api/v1/auth/user/:address', + pathParams: [ 'address' as const ], + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_login: { + path: '/api/v1/auth/login', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_logout: { + path: '/api/v1/auth/logout', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_user_balances: { + path: '/api/v1/user/balances', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_user_daily_check: { + path: '/api/v1/user/daily/check', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_user_daily_claim: { + path: '/api/v1/user/daily/claim', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + rewards_user_referrals: { + path: '/api/v1/user/referrals', + endpoint: getFeaturePayload(config.features.rewards)?.api.endpoint, + basePath: getFeaturePayload(config.features.rewards)?.api.basePath, + }, + // BLOCKS, TXS blocks: { path: '/api/v2/blocks', @@ -313,6 +432,16 @@ export const RESOURCES = { pathParams: [ 'height_or_hash' as const ], filterFields: [], }, + block_epoch: { + path: '/api/v2/blocks/:height_or_hash/epoch', + pathParams: [ 'height_or_hash' as const ], + filterFields: [], + }, + block_election_rewards: { + path: '/api/v2/blocks/:height_or_hash/election-rewards/:reward_type', + pathParams: [ 'height_or_hash' as const, 'reward_type' as const ], + filterFields: [], + }, txs_stats: { path: '/api/v2/transactions/stats', }, @@ -386,6 +515,10 @@ export const RESOURCES = { path: '/api/v2/addresses/', filterFields: [ ], }, + addresses_metadata_search: { + path: '/api/v2/proxy/metadata/addresses', + filterFields: [ 'slug' as const, 'tag_type' as const ], + }, // ADDRESS address: { @@ -458,6 +591,15 @@ export const RESOURCES = { pathParams: [ 'hash' as const ], filterFields: [], }, + address_epoch_rewards: { + path: '/api/v2/addresses/:hash/election-rewards', + pathParams: [ 'hash' as const ], + filterFields: [], + }, + address_xstar_score: { + path: '/api/v2/proxy/xname/addresses/:hash', + pathParams: [ 'hash' as const ], + }, // CONTRACT contract: { @@ -471,7 +613,7 @@ export const RESOURCES = { path: '/api/v2/smart-contracts/:hash/verification/via/:method', pathParams: [ 'hash' as const, 'method' as const ], }, - contract_solidityscan_report: { + contract_solidity_scan_report: { path: '/api/v2/smart-contracts/:hash/solidityscan-report', pathParams: [ 'hash' as const ], }, @@ -552,6 +694,12 @@ export const RESOURCES = { filterFields: [], }, + // TOKEN TRANSFERS + token_transfers_all: { + path: '/api/v2/token-transfers', + filterFields: [ 'type' as const ], + }, + // APP STATS stats: { path: '/api/v2/stats', @@ -573,15 +721,21 @@ export const RESOURCES = { homepage_blocks: { path: '/api/v2/main-page/blocks', }, - homepage_deposits: { + homepage_optimistic_deposits: { path: '/api/v2/main-page/optimism-deposits', }, + homepage_arbitrum_deposits: { + path: '/api/v2/main-page/arbitrum/messages/to-rollup', + }, 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', + }, homepage_txs_watchlist: { path: '/api/v2/main-page/transactions/watchlist', }, @@ -594,6 +748,9 @@ export const RESOURCES = { homepage_zksync_latest_batch: { path: '/api/v2/main-page/zksync/batches/latest-number', }, + homepage_arbitrum_latest_batch: { + path: '/api/v2/main-page/arbitrum/batches/latest-number', + }, // SEARCH quick_search: { @@ -637,12 +794,29 @@ export const RESOURCES = { }, optimistic_l2_txn_batches: { - path: '/api/v2/optimism/txn-batches', + path: '/api/v2/optimism/batches', filterFields: [], }, optimistic_l2_txn_batches_count: { - path: '/api/v2/optimism/txn-batches/count', + path: '/api/v2/optimism/batches/count', + }, + + optimistic_l2_txn_batch: { + path: '/api/v2/optimism/batches/:number', + pathParams: [ 'number' as const ], + }, + + optimistic_l2_txn_batch_txs: { + path: '/api/v2/transactions/optimism-batch/:number', + pathParams: [ 'number' as const ], + filterFields: [], + }, + + optimistic_l2_txn_batch_blocks: { + path: '/api/v2/blocks/optimism-batch/:number', + pathParams: [ 'number' as const ], + filterFields: [], }, optimistic_l2_dispute_games: { @@ -654,6 +828,44 @@ export const RESOURCES = { path: '/api/v2/optimism/games/count', }, + // MUD worlds on optimism + mud_worlds: { + path: '/api/v2/mud/worlds', + filterFields: [], + }, + + address_mud_tables: { + path: '/api/v2/mud/worlds/:hash/tables', + pathParams: [ 'hash' as const ], + filterFields: [ 'q' as const ], + }, + + address_mud_tables_count: { + path: '/api/v2/mud/worlds/:hash/tables/count', + pathParams: [ 'hash' as const ], + }, + + address_mud_records: { + path: '/api/v2/mud/worlds/:hash/tables/:table_id/records', + pathParams: [ 'hash' as const, 'table_id' as const ], + filterFields: [ 'filter_key0' as const, 'filter_key1' as const ], + }, + + address_mud_record: { + path: '/api/v2/mud/worlds/:hash/tables/:table_id/records/:record_id', + pathParams: [ 'hash' as const, 'table_id' as const, 'record_id' as const ], + }, + + contract_mud_systems: { + path: '/api/v2/mud/worlds/:hash/systems', + pathParams: [ 'hash' as const ], + }, + + contract_mud_system_info: { + path: '/api/v2/mud/worlds/:hash/systems/:system_address', + pathParams: [ 'hash' as const, 'system_address' as const ], + }, + // arbitrum L2 arbitrum_l2_messages: { path: '/api/v2/arbitrum/messages/:direction', @@ -804,14 +1016,19 @@ export const RESOURCES = { }, // VALIDATORS - validators: { - path: '/api/v2/validators/:chainType', - pathParams: [ 'chainType' as const ], + validators_stability: { + path: '/api/v2/validators/stability', filterFields: [ 'address_hash' as const, 'state_filter' as const ], }, - validators_counters: { - path: '/api/v2/validators/:chainType/counters', - pathParams: [ 'chainType' as const ], + validators_stability_counters: { + path: '/api/v2/validators/stability/counters', + }, + validators_blackfort: { + path: '/api/v2/validators/blackfort', + filterFields: [], + }, + validators_blackfort_counters: { + path: '/api/v2/validators/blackfort/counters', }, // BLOBS @@ -824,6 +1041,9 @@ export const RESOURCES = { config_backend_version: { path: '/api/v2/config/backend-version', }, + config_csv_export: { + path: '/api/v2/config/csv-export', + }, // CSV EXPORT csv_export_token_holders: { @@ -849,19 +1069,25 @@ export const RESOURCES = { csv_export_logs: { path: '/api/v1/logs-csv', }, + csv_export_epoch_rewards: { + path: '/api/v1/celo-election-rewards-csv', + }, graphql: { path: '/api/v1/graphql', }, + block_countdown: { + path: '/api', + }, }; export type ResourceName = keyof typeof RESOURCES; type ResourcePathMap = { [K in ResourceName]: typeof RESOURCES[K]['path'] -} -export type ResourcePath = ResourcePathMap[keyof ResourcePathMap] +}; +export type ResourcePath = ResourcePathMap[keyof ResourcePathMap]; -export type ResourceFiltersKey = typeof RESOURCES[R] extends {filterFields: Array} ? +export type ResourceFiltersKey = typeof RESOURCES[R] extends { filterFields: Array } ? ArrayElement : never; @@ -882,31 +1108,33 @@ export interface ResourceError { statusText: Response['statusText']; } -export type ResourceErrorAccount = ResourceError<{ errors: T }> +export type ResourceErrorAccount = ResourceError<{ errors: T }>; -export type PaginatedResources = 'blocks' | 'block_txs' | +export type PaginatedResources = 'blocks' | 'block_txs' | 'block_election_rewards' | 'txs_validated' | 'txs_pending' | 'txs_with_blobs' | 'txs_watchlist' | 'txs_execution_node' | 'tx_internal_txs' | 'tx_logs' | 'tx_token_transfers' | 'tx_state_changes' | 'tx_blobs' | -'addresses' | +'addresses' | 'addresses_metadata_search' | 'address_txs' | 'address_internal_txs' | 'address_token_transfers' | 'address_blocks_validated' | 'address_coin_balance' | 'search' | -'address_logs' | 'address_tokens' | 'address_nfts' | 'address_collections' | +'address_logs' | 'address_tokens' | 'address_nfts' | 'address_collections' | 'address_epoch_rewards' | 'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' | 'tokens_bridged' | 'token_instance_transfers' | 'token_instance_holders' | 'verified_contracts' | 'optimistic_l2_output_roots' | 'optimistic_l2_withdrawals' | 'optimistic_l2_txn_batches' | 'optimistic_l2_deposits' | -'optimistic_l2_dispute_games' | +'optimistic_l2_dispute_games' | 'optimistic_l2_txn_batch_txs' | 'optimistic_l2_txn_batch_blocks' | +'mud_worlds' | 'address_mud_tables' | 'address_mud_records' | 'shibarium_deposits' | 'shibarium_withdrawals' | 'arbitrum_l2_messages' | 'arbitrum_l2_txn_batches' | 'arbitrum_l2_txn_batch_txs' | 'arbitrum_l2_txn_batch_blocks' | 'zkevm_l2_deposits' | 'zkevm_l2_withdrawals' | 'zkevm_l2_txn_batches' | 'zkevm_l2_txn_batch_txs' | 'zksync_l2_txn_batches' | 'zksync_l2_txn_batch_txs' | 'withdrawals' | 'address_withdrawals' | 'block_withdrawals' | 'watchlist' | 'private_tags_address' | 'private_tags_tx' | -'domains_lookup' | 'addresses_lookup' | 'user_ops' | 'validators' | 'noves_address_history'; +'domains_lookup' | 'addresses_lookup' | 'user_ops' | 'validators_stability' | 'validators_blackfort' | 'noves_address_history' | +'token_transfers_all'; export type PaginatedResponse = ResourcePayload; -/* eslint-disable @typescript-eslint/indent */ +/* eslint-disable @stylistic/indent */ // !!! IMPORTANT !!! // Don't add any new types here because TypeScript cannot handle it properly // use ResourcePayloadB instead @@ -927,18 +1155,24 @@ Q extends 'stats_charts_secondary_coin_price' ? ChartSecondaryCoinPriceResponse Q extends 'homepage_blocks' ? Array : Q extends 'homepage_txs' ? Array : Q extends 'homepage_txs_watchlist' ? Array : -Q extends 'homepage_deposits' ? Array : +Q extends 'homepage_optimistic_deposits' ? Array : +Q extends 'homepage_arbitrum_deposits' ? ArbitrumLatestDepositsResponse : Q extends 'homepage_zkevm_l2_batches' ? { items: Array } : +Q extends 'homepage_arbitrum_l2_batches' ? { items: Array } : Q extends 'homepage_indexing_status' ? IndexingStatus : Q extends 'homepage_zkevm_latest_batch' ? number : Q extends 'homepage_zksync_latest_batch' ? number : +Q extends 'homepage_arbitrum_latest_batch' ? number : Q extends 'stats_counters' ? stats.Counters : Q extends 'stats_lines' ? stats.LineCharts : Q extends 'stats_line' ? stats.LineChart : Q extends 'blocks' ? BlocksResponse : Q extends 'block' ? Block : +Q extends 'block_countdown' ? BlockCountdownResponse : Q extends 'block_txs' ? BlockTransactionsResponse : Q extends 'block_withdrawals' ? BlockWithdrawalsResponse : +Q extends 'block_epoch' ? BlockEpoch : +Q extends 'block_election_rewards' ? BlockEpochElectionRewardDetailsResponse : Q extends 'txs_stats' ? TransactionsStats : Q extends 'txs_validated' ? TransactionsResponseValidated : Q extends 'txs_pending' ? TransactionsResponsePending : @@ -954,6 +1188,7 @@ Q extends 'tx_state_changes' ? TxStateChanges : Q extends 'tx_blobs' ? TxBlobs : Q extends 'tx_interpretation' ? TxInterpretationResponse : Q extends 'addresses' ? AddressesResponse : +Q extends 'addresses_metadata_search' ? AddressesMetadataSearchResult : Q extends 'address' ? Address : Q extends 'address_counters' ? AddressCounters : Q extends 'address_tabs_counters' ? AddressTabsCounters : @@ -962,7 +1197,7 @@ Q extends 'address_internal_txs' ? AddressInternalTxsResponse : Q extends 'address_token_transfers' ? AddressTokenTransferResponse : Q extends 'address_blocks_validated' ? AddressBlocksValidatedResponse : Q extends 'address_coin_balance' ? AddressCoinBalanceHistoryResponse : -Q extends 'address_coin_balance_chart' ? AddressCoinBalanceHistoryChartOld | AddressCoinBalanceHistoryChart : +Q extends 'address_coin_balance_chart' ? AddressCoinBalanceHistoryChart : Q extends 'address_logs' ? LogsResponseAddress : Q extends 'address_tokens' ? AddressTokensResponse : Q extends 'address_nfts' ? AddressNFTsResponse : @@ -984,38 +1219,42 @@ Q extends 'quick_search' ? Array : Q extends 'search' ? SearchResult : Q extends 'search_check_redirect' ? SearchRedirectResult : Q extends 'contract' ? SmartContract : -Q extends 'contract_solidityscan_report' ? SolidityscanReport : +Q extends 'contract_solidity_scan_report' ? unknown : Q extends 'verified_contracts' ? VerifiedContractsResponse : Q extends 'verified_contracts_counters' ? VerifiedContractsCounters : Q extends 'visualize_sol2uml' ? visualizer.VisualizeResponse : -Q extends 'contract_verification_config' ? SmartContractVerificationConfig : -Q extends 'withdrawals' ? WithdrawalsResponse : -Q extends 'withdrawals_counters' ? WithdrawalsCounters : +Q extends 'contract_verification_config' ? SmartContractVerificationConfigRaw : Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse : Q extends 'optimistic_l2_withdrawals' ? OptimisticL2WithdrawalsResponse : Q extends 'optimistic_l2_deposits' ? OptimisticL2DepositsResponse : Q extends 'optimistic_l2_txn_batches' ? OptimisticL2TxnBatchesResponse : +Q extends 'optimistic_l2_txn_batches_count' ? number : +Q extends 'optimistic_l2_txn_batch' ? OptimismL2TxnBatch : +Q extends 'optimistic_l2_txn_batch_txs' ? OptimismL2BatchTxs : +Q extends 'optimistic_l2_txn_batch_blocks' ? OptimismL2BatchBlocks : Q extends 'optimistic_l2_dispute_games' ? OptimisticL2DisputeGamesResponse : Q extends 'optimistic_l2_output_roots_count' ? number : Q extends 'optimistic_l2_withdrawals_count' ? number : Q extends 'optimistic_l2_deposits_count' ? number : -Q extends 'optimistic_l2_txn_batches_count' ? number : Q extends 'optimistic_l2_dispute_games_count' ? number : never; // !!! IMPORTANT !!! // See comment above -/* eslint-enable @typescript-eslint/indent */ +/* eslint-enable @stylistic/indent */ -/* eslint-disable @typescript-eslint/indent */ +/* eslint-disable @stylistic/indent */ export type ResourcePayloadB = Q extends 'config_backend_version' ? BackendVersionConfig : +Q extends 'config_csv_export' ? CsvExportConfig : Q extends 'address_metadata_info' ? AddressMetadataInfo : Q extends 'address_metadata_tag_types' ? PublicTagTypesResponse : Q extends 'blob' ? Blob : Q extends 'marketplace_dapps' ? Array : Q extends 'marketplace_dapp' ? MarketplaceAppOverview : -Q extends 'validators' ? ValidatorsResponse : -Q extends 'validators_counters' ? ValidatorsCountersResponse : +Q extends 'validators_stability' ? ValidatorsStabilityResponse : +Q extends 'validators_stability_counters' ? ValidatorsStabilityCountersResponse : +Q extends 'validators_blackfort' ? ValidatorsBlackfortResponse : +Q extends 'validators_blackfort_counters' ? ValidatorsBlackfortCountersResponse : Q extends 'shibarium_withdrawals' ? ShibariumWithdrawalsResponse : Q extends 'shibarium_deposits' ? ShibariumDepositsResponse : Q extends 'shibarium_withdrawals_count' ? number : @@ -1041,6 +1280,7 @@ Q extends 'zksync_l2_txn_batch' ? ZkSyncBatch : Q extends 'zksync_l2_txn_batch_txs' ? ZkSyncBatchTxs : Q extends 'contract_security_audits' ? SmartContractSecurityAudits : Q extends 'addresses_lookup' ? bens.LookupAddressResponse : +Q extends 'address_domain' ? bens.GetAddressResponse : Q extends 'domain_info' ? bens.DetailedDomain : Q extends 'domain_events' ? bens.ListDomainEventsResponse : Q extends 'domains_lookup' ? bens.LookupDomainNameResponse : @@ -1048,12 +1288,33 @@ Q extends 'domain_protocols' ? bens.GetProtocolsResponse : Q extends 'user_ops' ? UserOpsResponse : Q extends 'user_op' ? UserOp : Q extends 'user_ops_account' ? UserOpsAccount : -Q extends 'user_op_interpretation'? TxInterpretationResponse : +Q extends 'user_op_interpretation' ? TxInterpretationResponse : Q extends 'noves_transaction' ? NovesResponseData : Q extends 'noves_address_history' ? NovesAccountHistoryResponse : Q extends 'noves_describe_txs' ? NovesDescribeTxsResponse : +Q extends 'mud_worlds' ? MudWorldsResponse : +Q extends 'address_mud_tables' ? AddressMudTables : +Q extends 'address_mud_tables_count' ? number : +Q extends 'address_mud_records' ? AddressMudRecords : +Q extends 'address_mud_record' ? AddressMudRecord : +Q extends 'contract_mud_systems' ? SmartContractMudSystemsResponse : +Q extends 'contract_mud_system_info' ? SmartContractMudSystemInfo : +Q extends 'address_epoch_rewards' ? AddressEpochRewardsResponse : +Q extends 'withdrawals' ? WithdrawalsResponse : +Q extends 'withdrawals_counters' ? WithdrawalsCounters : +Q extends 'rewards_config' ? RewardsConfigResponse : +Q extends 'rewards_check_ref_code' ? RewardsCheckRefCodeResponse : +Q extends 'rewards_nonce' ? RewardsNonceResponse : +Q extends 'rewards_check_user' ? RewardsCheckUserResponse : +Q extends 'rewards_login' ? RewardsLoginResponse : +Q extends 'rewards_user_balances' ? RewardsUserBalancesResponse : +Q extends 'rewards_user_daily_check' ? RewardsUserDailyCheckResponse : +Q extends 'rewards_user_daily_claim' ? RewardsUserDailyClaimResponse : +Q extends 'rewards_user_referrals' ? RewardsUserReferralsResponse : +Q extends 'token_transfers_all' ? TokenTransferResponse : +Q extends 'address_xstar_score' ? AddressXStarResponse : never; -/* eslint-enable @typescript-eslint/indent */ +/* eslint-enable @stylistic/indent */ export type ResourcePayload = ResourcePayloadA | ResourcePayloadB; export type PaginatedResponseItems = Q extends PaginatedResources ? ResourcePayloadA['items'] | ResourcePayloadB['items'] : never; @@ -1061,7 +1322,7 @@ export type PaginatedResponseNextPageParams = Q extends ResourcePayloadA['next_page_params'] | ResourcePayloadB['next_page_params'] : never; -/* eslint-disable @typescript-eslint/indent */ +/* eslint-disable @stylistic/indent */ export type PaginationFilters = Q extends 'blocks' ? BlockFilters : Q extends 'block_txs' ? TTxsWithBlobsFilters : @@ -1070,6 +1331,7 @@ Q extends 'txs_with_blobs' ? TTxsWithBlobsFilters : Q extends 'tx_token_transfers' ? TokenTransferFilters : Q extends 'token_transfers' ? TokenTransferFilters : Q extends 'address_txs' | 'address_internal_txs' ? AddressTxsFilters : +Q extends 'addresses_metadata_search' ? AddressesMetadataSearchFilters : Q extends 'address_token_transfers' ? AddressTokenTransferFilters : Q extends 'address_tokens' ? AddressTokensFilter : Q extends 'address_nfts' ? AddressNFTTokensFilter : @@ -1082,11 +1344,14 @@ Q extends 'verified_contracts' ? VerifiedContractsFilters : Q extends 'addresses_lookup' ? EnsAddressLookupFilters : Q extends 'domains_lookup' ? EnsDomainLookupFilters : Q extends 'user_ops' ? UserOpsFilters : -Q extends 'validators' ? ValidatorsFilters : +Q extends 'validators_stability' ? ValidatorsStabilityFilters : +Q extends 'address_mud_tables' ? AddressMudTablesFilter : +Q extends 'address_mud_records' ? AddressMudRecordsFilter : +Q extends 'token_transfers_all' ? TokenTransferFilters : never; -/* eslint-enable @typescript-eslint/indent */ +/* eslint-enable @stylistic/indent */ -/* eslint-disable @typescript-eslint/indent */ +/* eslint-disable @stylistic/indent */ export type PaginationSorting = Q extends 'tokens' ? TokensSorting : Q extends 'tokens_bridged' ? TokensSorting : @@ -1094,6 +1359,8 @@ Q extends 'verified_contracts' ? VerifiedContractsSorting : Q extends 'address_txs' ? TransactionsSorting : Q extends 'addresses_lookup' ? EnsLookupSorting : Q extends 'domains_lookup' ? EnsLookupSorting : -Q extends 'validators' ? ValidatorsSorting : +Q extends 'validators_stability' ? ValidatorsStabilitySorting : +Q extends 'validators_blackfort' ? ValidatorsBlackfortSorting : +Q extends 'address_mud_records' ? AddressMudRecordsSorting : never; -/* eslint-enable @typescript-eslint/indent */ +/* eslint-enable @stylistic/indent */ diff --git a/lib/api/useApiFetch.tsx b/lib/api/useApiFetch.tsx index 85da773ab1..86e5fa9ea9 100644 --- a/lib/api/useApiFetch.tsx +++ b/lib/api/useApiFetch.tsx @@ -19,7 +19,7 @@ import type { ApiResource, ResourceName, ResourcePathParams } from './resources' export interface Params { pathParams?: ResourcePathParams; - queryParams?: Record | number | boolean | undefined>; + queryParams?: Record | number | boolean | undefined | null>; fetchParams?: Pick; } diff --git a/lib/api/useApiInfiniteQuery.tsx b/lib/api/useApiInfiniteQuery.tsx new file mode 100644 index 0000000000..98300d0d64 --- /dev/null +++ b/lib/api/useApiInfiniteQuery.tsx @@ -0,0 +1,42 @@ +import type { InfiniteData, QueryKey, UseInfiniteQueryResult, UseInfiniteQueryOptions } from '@tanstack/react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; + +import type { PaginatedResources, ResourceError, ResourcePayload } from 'lib/api/resources'; +import useApiFetch from 'lib/api/useApiFetch'; +import type { Params as ApiFetchParams } from 'lib/api/useApiFetch'; + +import { getResourceKey } from './useApiQuery'; + +type TQueryData = ResourcePayload; +type TError = ResourceError; +type TPageParam = ApiFetchParams['queryParams'] | null; + +export interface Params { + resourceName: R; + // eslint-disable-next-line max-len + queryOptions?: Omit, TError, InfiniteData>, TQueryData, QueryKey, TPageParam>, 'queryKey' | 'queryFn' | 'getNextPageParam' | 'initialPageParam'>; + pathParams?: ApiFetchParams['pathParams']; +} + +type ReturnType = UseInfiniteQueryResult>, ResourceError>; + +export default function useApiInfiniteQuery({ + resourceName, + queryOptions, + pathParams, +}: Params): ReturnType { + const apiFetch = useApiFetch(); + + return useInfiniteQuery, TError, InfiniteData>, QueryKey, TPageParam>({ + queryKey: getResourceKey(resourceName, { pathParams }), + queryFn: (context) => { + const queryParams = 'pageParam' in context ? (context.pageParam || undefined) : undefined; + return apiFetch(resourceName, { pathParams, queryParams }) as Promise>; + }, + initialPageParam: null, + getNextPageParam: (lastPage) => { + return lastPage.next_page_params as TPageParam; + }, + ...queryOptions, + }); +} diff --git a/lib/api/useApiQuery.tsx b/lib/api/useApiQuery.tsx index 91b2ab6ae5..8dfa918b8c 100644 --- a/lib/api/useApiQuery.tsx +++ b/lib/api/useApiQuery.tsx @@ -1,12 +1,16 @@ import type { UseQueryOptions } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; -import type { ResourceError, ResourceName, ResourcePayload } from './resources'; -import type { Params as ApiFetchParams } from './useApiFetch'; +import type { Params as FetchParams } from 'lib/hooks/useFetch'; + +import type { ResourceError, ResourceName, ResourcePathParams, ResourcePayload } from './resources'; import useApiFetch from './useApiFetch'; -export interface Params> extends ApiFetchParams { - queryOptions?: Omit, ResourceError, D>, 'queryKey' | 'queryFn'>; +export interface Params> { + pathParams?: ResourcePathParams; + queryParams?: Record | number | boolean | undefined>; + fetchParams?: Pick; + queryOptions?: Partial, ResourceError, D>, 'queryFn'>>; } export function getResourceKey(resource: R, { pathParams, queryParams }: Params = {}) { @@ -24,13 +28,12 @@ export default function useApiQuery, ResourceError, D>({ - // eslint-disable-next-line @tanstack/query/exhaustive-deps - queryKey: getResourceKey(resource, { pathParams, queryParams }), - queryFn: async() => { + queryKey: queryOptions?.queryKey || getResourceKey(resource, { pathParams, queryParams }), + queryFn: async({ signal }) => { // all errors and error typing is handled by react-query // so error response will never go to the data // that's why we are safe here to do type conversion "as Promise>" - return apiFetch(resource, { pathParams, queryParams, fetchParams }) as Promise>; + return apiFetch(resource, { pathParams, queryParams, fetchParams: { ...fetchParams, signal } }) as Promise>; }, ...queryOptions, }); diff --git a/lib/blob/guessDataType.ts b/lib/blob/guessDataType.ts index fb409019e3..ea149236a0 100644 --- a/lib/blob/guessDataType.ts +++ b/lib/blob/guessDataType.ts @@ -5,7 +5,7 @@ import hexToBytes from 'lib/hexToBytes'; import removeNonSignificantZeroBytes from './removeNonSignificantZeroBytes'; export default function guessDataType(data: string) { - const bytes = new Uint8Array(hexToBytes(data)); + const bytes = hexToBytes(data); const filteredBytes = removeNonSignificantZeroBytes(bytes); return filetype(filteredBytes)[0]; diff --git a/lib/block/getBlockReward.ts b/lib/block/getBlockReward.ts index 3c00ac8d19..dfe2558232 100644 --- a/lib/block/getBlockReward.ts +++ b/lib/block/getBlockReward.ts @@ -3,7 +3,7 @@ import BigNumber from 'bignumber.js'; import type { Block } from 'types/api/block'; export default function getBlockReward(block: Block) { - const txFees = BigNumber(block.tx_fees || 0); + const txFees = BigNumber(block.transaction_fees || 0); const burntFees = BigNumber(block.burnt_fees || 0); const minerReward = block.rewards?.find(({ type }) => type === 'Miner Reward' || type === 'Validator Reward')?.reward; const totalReward = BigNumber(minerReward || 0); diff --git a/lib/bytesToHex.ts b/lib/bytesToHex.ts new file mode 100644 index 0000000000..be2a1c2757 --- /dev/null +++ b/lib/bytesToHex.ts @@ -0,0 +1,8 @@ +export default function bytesToBase64(bytes: Uint8Array) { + let result = ''; + for (const byte of bytes) { + result += Number(byte).toString(16).padStart(2, '0'); + } + + return `0x${ result }`; +} diff --git a/lib/capitalizeFirstLetter.ts b/lib/capitalizeFirstLetter.ts new file mode 100644 index 0000000000..ae054d9423 --- /dev/null +++ b/lib/capitalizeFirstLetter.ts @@ -0,0 +1,7 @@ +export default function capitalizeFirstLetter(text: string) { + if (!text || !text.length) { + return ''; + } + + return text.charAt(0).toUpperCase() + text.slice(1); +} diff --git a/lib/contexts/app.tsx b/lib/contexts/app.tsx index dbeceace4d..f7672b5f21 100644 --- a/lib/contexts/app.tsx +++ b/lib/contexts/app.tsx @@ -6,7 +6,7 @@ import type { Props as PageProps } from 'nextjs/getServerSideProps'; type Props = { children: React.ReactNode; pageProps: PageProps; -} +}; const AppContext = createContext({ cookies: '', diff --git a/lib/contexts/chakra.tsx b/lib/contexts/chakra.tsx index d009d3b5f0..151d893dce 100644 --- a/lib/contexts/chakra.tsx +++ b/lib/contexts/chakra.tsx @@ -6,7 +6,7 @@ import { import type { ChakraProviderProps } from '@chakra-ui/react'; import React from 'react'; -import theme from 'theme'; +import theme from 'theme/theme'; interface Props extends ChakraProviderProps { cookies?: string; diff --git a/lib/contexts/marketplace.tsx b/lib/contexts/marketplace.tsx index 2aba76e39b..61cde12624 100644 --- a/lib/contexts/marketplace.tsx +++ b/lib/contexts/marketplace.tsx @@ -3,14 +3,14 @@ import React, { createContext, useContext, useEffect, useState, useMemo } from ' type Props = { children: React.ReactNode; -} +}; type TMarketplaceContext = { isAutoConnectDisabled: boolean; setIsAutoConnectDisabled: (isAutoConnectDisabled: boolean) => void; -} +}; -const MarketplaceContext = createContext({ +export const MarketplaceContext = createContext({ isAutoConnectDisabled: false, setIsAutoConnectDisabled: () => {}, }); diff --git a/lib/contexts/rewards.tsx b/lib/contexts/rewards.tsx new file mode 100644 index 0000000000..c9a93f16bd --- /dev/null +++ b/lib/contexts/rewards.tsx @@ -0,0 +1,289 @@ +import { useBoolean } from '@chakra-ui/react'; +import type { UseQueryResult } from '@tanstack/react-query'; +import { useQueryClient } from '@tanstack/react-query'; +import { useRouter } from 'next/router'; +import React, { createContext, useContext, useEffect, useMemo, useCallback } from 'react'; +import { useSignMessage } from 'wagmi'; + +import type { + RewardsUserBalancesResponse, RewardsUserDailyCheckResponse, + RewardsNonceResponse, RewardsCheckUserResponse, + RewardsLoginResponse, RewardsCheckRefCodeResponse, + RewardsUserDailyClaimResponse, RewardsUserReferralsResponse, + RewardsConfigResponse, +} from 'types/api/rewards'; + +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 { YEAR } from 'lib/consts'; +import * as cookies from 'lib/cookies'; +import decodeJWT from 'lib/decodeJWT'; +import getErrorMessage from 'lib/errors/getErrorMessage'; +import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; +import useToast from 'lib/hooks/useToast'; +import getQueryParamString from 'lib/router/getQueryParamString'; +import removeQueryParam from 'lib/router/removeQueryParam'; +import useAccount from 'lib/web3/useAccount'; +import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; + +const feature = config.features.rewards; + +type ContextQueryResult = + Pick>, 'data' | 'isLoading' | 'refetch' | 'isPending' | 'isFetching' | 'isError'>; + +type TRewardsContext = { + balancesQuery: ContextQueryResult; + dailyRewardQuery: ContextQueryResult; + referralsQuery: ContextQueryResult; + rewardsConfigQuery: ContextQueryResult; + checkUserQuery: ContextQueryResult; + apiToken: string | undefined; + isInitialized: boolean; + isLoginModalOpen: boolean; + openLoginModal: () => void; + closeLoginModal: () => void; + login: (refCode: string) => Promise<{ isNewUser?: boolean; invalidRefCodeError?: boolean }>; + claim: () => Promise; +}; + +const defaultQueryResult = { + data: undefined, + isLoading: false, + isPending: false, + isFetching: false, + isError: false, + refetch: () => Promise.resolve({} as never), +}; + +const initialState = { + balancesQuery: defaultQueryResult, + dailyRewardQuery: defaultQueryResult, + referralsQuery: defaultQueryResult, + rewardsConfigQuery: defaultQueryResult, + checkUserQuery: defaultQueryResult, + apiToken: undefined, + isInitialized: false, + isLoginModalOpen: false, + openLoginModal: () => {}, + closeLoginModal: () => {}, + login: async() => ({}), + claim: async() => {}, +}; + +const RewardsContext = createContext(initialState); + +// Message to sign for the rewards program +function getMessageToSign(address: string, nonce: string, isLogin?: boolean, refCode?: string) { + const signInText = 'Sign-In for the Blockscout Merits program.'; + const signUpText = 'Sign-Up for the Blockscout Merits program. I accept Terms of Service: https://merits.blockscout.com/terms. I love capybaras.'; + const referralText = refCode ? ` Referral code: ${ refCode }` : ''; + const body = isLogin ? signInText : signUpText + referralText; + + const urlObj = window.location.hostname === 'localhost' && feature.isEnabled ? + new URL(feature.api.endpoint) : + window.location; + + return [ + `${ urlObj.hostname } wants you to sign in with your Ethereum account:`, + address, + '', + body, + '', + `URI: ${ urlObj.origin }`, + 'Version: 1', + `Chain ID: ${ config.chain.id }`, + `Nonce: ${ nonce }`, + `Issued At: ${ new Date().toISOString() }`, + `Expiration Time: ${ new Date(Date.now() + YEAR).toISOString() }`, + ].join('\n'); +} + +// Get the registered address from the JWT token +function getRegisteredAddress(token: string) { + const decodedToken = decodeJWT(token); + return decodedToken?.payload.sub; +} + +type Props = { + children: React.ReactNode; +}; + +export function RewardsContextProvider({ children }: Props) { + const router = useRouter(); + const queryClient = useQueryClient(); + const apiFetch = useApiFetch(); + const toast = useToast(); + const { address } = useAccount(); + const { signMessageAsync } = useSignMessage(); + const profileQuery = useProfileQuery(); + + const [ isLoginModalOpen, setIsLoginModalOpen ] = useBoolean(false); + const [ isInitialized, setIsInitialized ] = useBoolean(false); + const [ apiToken, setApiToken ] = React.useState(); + + // Initialize state with the API token from cookies + useEffect(() => { + if (!profileQuery.isLoading) { + const token = cookies.get(cookies.NAMES.REWARDS_API_TOKEN); + const registeredAddress = getRegisteredAddress(token || ''); + if (registeredAddress === profileQuery.data?.address_hash) { + setApiToken(token); + } + setIsInitialized.on(); + } + }, [ setIsInitialized, profileQuery ]); + + // Save the API token to cookies and state + const saveApiToken = useCallback((token: string | undefined) => { + if (token) { + cookies.set(cookies.NAMES.REWARDS_API_TOKEN, token); + } else { + cookies.remove(cookies.NAMES.REWARDS_API_TOKEN); + } + setApiToken(token); + }, []); + + const [ queryOptions, fetchParams ] = useMemo(() => [ + { enabled: Boolean(apiToken) && feature.isEnabled }, + { headers: { Authorization: `Bearer ${ apiToken }` } }, + ], [ apiToken ]); + + const balancesQuery = useApiQuery('rewards_user_balances', { queryOptions, fetchParams }); + const dailyRewardQuery = useApiQuery('rewards_user_daily_check', { queryOptions, fetchParams }); + const referralsQuery = useApiQuery('rewards_user_referrals', { queryOptions, fetchParams }); + const rewardsConfigQuery = useApiQuery('rewards_config', { queryOptions: { enabled: feature.isEnabled } }); + const checkUserQuery = useApiQuery('rewards_check_user', { queryOptions: { enabled: feature.isEnabled }, pathParams: { address } }); + + // Reset queries when the API token is removed + useEffect(() => { + if (isInitialized && !apiToken) { + queryClient.resetQueries({ queryKey: getResourceKey('rewards_user_balances'), exact: true }); + queryClient.resetQueries({ queryKey: getResourceKey('rewards_user_daily_check'), exact: true }); + queryClient.resetQueries({ queryKey: getResourceKey('rewards_user_referrals'), exact: true }); + } + }, [ isInitialized, apiToken, queryClient ]); + + // Handle 401 error + useEffect(() => { + if (apiToken && balancesQuery.error?.status === 401) { + saveApiToken(undefined); + } + }, [ balancesQuery.error, apiToken, saveApiToken ]); + + // Check if the profile address is the same as the registered address + useEffect(() => { + const registeredAddress = getRegisteredAddress(apiToken || ''); + if (registeredAddress && !profileQuery.isLoading && profileQuery.data?.address_hash !== registeredAddress) { + setApiToken(undefined); + } + }, [ apiToken, profileQuery, setApiToken ]); + + // Handle referral code in the URL + useEffect(() => { + const refCode = getQueryParamString(router.query.ref); + if (refCode && isInitialized) { + cookies.set(cookies.NAMES.REWARDS_REFERRAL_CODE, refCode); + removeQueryParam(router, 'ref'); + if (!apiToken) { + setIsLoginModalOpen.on(); + } + } + }, [ router, apiToken, isInitialized, setIsLoginModalOpen ]); + + const errorToast = useCallback((error: unknown) => { + const apiError = getErrorObjPayload<{ message: string }>(error); + toast({ + position: 'top-right', + title: 'Error', + description: apiError?.message || getErrorMessage(error) || 'Something went wrong. Try again later.', + status: 'error', + variant: 'subtle', + isClosable: true, + }); + }, [ toast ]); + + // Login to the rewards program + const login = useCallback(async(refCode: string) => { + try { + if (!address) { + throw new Error(); + } + const [ nonceResponse, checkCodeResponse ] = await Promise.all([ + apiFetch('rewards_nonce') as Promise, + refCode ? + apiFetch('rewards_check_ref_code', { pathParams: { code: refCode } }) as Promise : + Promise.resolve({ valid: true }), + ]); + if (!checkCodeResponse.valid) { + return { invalidRefCodeError: true }; + } + const message = getMessageToSign(address, nonceResponse.nonce, checkUserQuery.data?.exists, refCode); + const signature = await signMessageAsync({ message }); + const loginResponse = await apiFetch('rewards_login', { + fetchParams: { + method: 'POST', + body: { + nonce: nonceResponse.nonce, + message, + signature, + }, + }, + }) as RewardsLoginResponse; + saveApiToken(loginResponse.token); + return { isNewUser: loginResponse.created }; + } catch (_error) { + errorToast(_error); + throw _error; + } + }, [ apiFetch, address, signMessageAsync, errorToast, saveApiToken, checkUserQuery ]); + + // Claim daily reward + const claim = useCallback(async() => { + try { + await apiFetch('rewards_user_daily_claim', { + fetchParams: { + method: 'POST', + ...fetchParams, + }, + }) as RewardsUserDailyClaimResponse; + } catch (_error) { + errorToast(_error); + throw _error; + } + }, [ apiFetch, errorToast, fetchParams ]); + + const value = useMemo(() => { + if (!feature.isEnabled) { + return initialState; + } + return { + balancesQuery, + dailyRewardQuery, + referralsQuery, + rewardsConfigQuery, + checkUserQuery, + apiToken, + isInitialized, + isLoginModalOpen, + openLoginModal: setIsLoginModalOpen.on, + closeLoginModal: setIsLoginModalOpen.off, + login, + claim, + }; + }, [ + isLoginModalOpen, setIsLoginModalOpen, balancesQuery, dailyRewardQuery, checkUserQuery, + apiToken, login, claim, referralsQuery, rewardsConfigQuery, isInitialized, + ]); + + return ( + + { children } + + ); +} + +export function useRewardsContext() { + return useContext(RewardsContext); +} diff --git a/lib/contexts/settings.tsx b/lib/contexts/settings.tsx new file mode 100644 index 0000000000..054248acf9 --- /dev/null +++ b/lib/contexts/settings.tsx @@ -0,0 +1,56 @@ +import React from 'react'; + +import { ADDRESS_FORMATS, type AddressFormat } from 'types/views/address'; + +import * as cookies from 'lib/cookies'; + +import { useAppContext } from './app'; + +interface SettingsProviderProps { + children: React.ReactNode; +} + +interface TSettingsContext { + addressFormat: AddressFormat; + toggleAddressFormat: () => void; +} + +export const SettingsContext = React.createContext(null); + +export function SettingsContextProvider({ children }: SettingsProviderProps) { + const { cookies: appCookies } = useAppContext(); + const initialAddressFormat = cookies.get(cookies.NAMES.ADDRESS_FORMAT, appCookies); + + const [ addressFormat, setAddressFormat ] = React.useState( + initialAddressFormat && ADDRESS_FORMATS.includes(initialAddressFormat as AddressFormat) ? initialAddressFormat as AddressFormat : 'base16', + ); + + const toggleAddressFormat = React.useCallback(() => { + setAddressFormat(prev => { + const nextValue = prev === 'base16' ? 'bech32' : 'base16'; + cookies.set(cookies.NAMES.ADDRESS_FORMAT, nextValue); + return nextValue; + }); + }, []); + + const value = React.useMemo(() => { + return { + addressFormat, + toggleAddressFormat, + }; + }, [ addressFormat, toggleAddressFormat ]); + + return ( + + { children } + + ); +} + +export function useSettingsContext(disabled?: boolean) { + const context = React.useContext(SettingsContext); + if (context === undefined || disabled) { + return null; + } + return context; +} diff --git a/lib/cookies.ts b/lib/cookies.ts index cef6a38a42..fc4f8cc144 100644 --- a/lib/cookies.ts +++ b/lib/cookies.ts @@ -3,19 +3,20 @@ import Cookies from 'js-cookie'; import isBrowser from './isBrowser'; export enum NAMES { - NAV_BAR_COLLAPSED='nav_bar_collapsed', - API_TOKEN='_explorer_key', - INVALID_SESSION='invalid_session', - CONFIRM_EMAIL_PAGE_VIEWED='confirm_email_page_viewed', - TXS_SORT='txs_sort', - COLOR_MODE='chakra-ui-color-mode', - COLOR_MODE_HEX='chakra-ui-color-mode-hex', - ADDRESS_IDENTICON_TYPE='address_identicon_type', - INDEXING_ALERT='indexing_alert', - ADBLOCK_DETECTED='adblock_detected', - MIXPANEL_DEBUG='_mixpanel_debug', - ADDRESS_NFT_DISPLAY_TYPE='address_nft_display_type', - UUID='uuid', + NAV_BAR_COLLAPSED = 'nav_bar_collapsed', + API_TOKEN = '_explorer_key', + REWARDS_API_TOKEN = 'rewards_api_token', + REWARDS_REFERRAL_CODE = 'rewards_ref_code', + TXS_SORT = 'txs_sort', + COLOR_MODE = 'chakra-ui-color-mode', + COLOR_MODE_HEX = 'chakra-ui-color-mode-hex', + ADDRESS_IDENTICON_TYPE = 'address_identicon_type', + ADDRESS_FORMAT = 'address_format', + INDEXING_ALERT = 'indexing_alert', + ADBLOCK_DETECTED = 'adblock_detected', + MIXPANEL_DEBUG = '_mixpanel_debug', + ADDRESS_NFT_DISPLAY_TYPE = 'address_nft_display_type', + UUID = 'uuid', } export function get(name?: NAMES | undefined | null, serverCookie?: string) { @@ -28,12 +29,16 @@ export function get(name?: NAMES | undefined | null, serverCookie?: string) { } } -export function set(name: string, value: string, attributes: Cookies.CookieAttributes = {}) { +export function set(name: NAMES, value: string, attributes: Cookies.CookieAttributes = {}) { attributes.path = '/'; return Cookies.set(name, value, attributes); } +export function remove(name: NAMES, attributes: Cookies.CookieAttributes = {}) { + return Cookies.remove(name, attributes); +} + export function getFromCookieString(cookieString: string, name?: NAMES | undefined | null) { return cookieString.split(`${ name }=`)[1]?.split(';')[0]; } diff --git a/lib/date/dayjs.ts b/lib/date/dayjs.ts index 06b53c01d6..e0191ce1e0 100644 --- a/lib/date/dayjs.ts +++ b/lib/date/dayjs.ts @@ -38,6 +38,7 @@ dayjs.extend(minMax); dayjs.updateLocale('en', { formats: { llll: `MMM DD YYYY HH:mm:ss A (Z${ nbsp }UTC)`, + lll: 'MMM D, YYYY h:mm A', }, relativeTime: { s: '1s', diff --git a/lib/decodeJWT.ts b/lib/decodeJWT.ts new file mode 100644 index 0000000000..d94e21d4de --- /dev/null +++ b/lib/decodeJWT.ts @@ -0,0 +1,47 @@ +interface JWTHeader { + alg: string; + typ?: string; + [key: string]: unknown; +} + +interface JWTPayload { + [key: string]: unknown; +} + +const base64UrlDecode = (str: string): string => { + // Replace characters according to Base64Url standard + str = str.replace(/-/g, '+').replace(/_/g, '/'); + + // Add padding '=' characters for correct decoding + const pad = str.length % 4; + if (pad) { + str += '='.repeat(4 - pad); + } + + // Decode from Base64 to string + const decodedStr = atob(str); + + return decodedStr; +}; + +export default function decodeJWT(token: string): { header: JWTHeader; payload: JWTPayload; signature: string } | null { + try { + const parts = token.split('.'); + + if (parts.length !== 3) { + throw new Error('Invalid JWT format'); + } + + const [ encodedHeader, encodedPayload, signature ] = parts; + + const headerJson = base64UrlDecode(encodedHeader); + const payloadJson = base64UrlDecode(encodedPayload); + + const header = JSON.parse(headerJson) as JWTHeader; + const payload = JSON.parse(payloadJson) as JWTPayload; + + return { header, payload, signature }; + } catch (error) { + return null; + } +} diff --git a/lib/errors/getErrorMessage.ts b/lib/errors/getErrorMessage.ts new file mode 100644 index 0000000000..5e15f29a1c --- /dev/null +++ b/lib/errors/getErrorMessage.ts @@ -0,0 +1,6 @@ +import getErrorObj from './getErrorObj'; + +export default function getErrorMessage(error: unknown): string | undefined { + const errorObj = getErrorObj(error); + return errorObj && 'message' in errorObj && typeof errorObj.message === 'string' ? errorObj.message : undefined; +} diff --git a/lib/errors/throwOnResourceLoadError.ts b/lib/errors/throwOnResourceLoadError.ts index 32e5169fc7..64075213cf 100644 --- a/lib/errors/throwOnResourceLoadError.ts +++ b/lib/errors/throwOnResourceLoadError.ts @@ -8,7 +8,7 @@ type Params = ({ error: null; }) & { resource?: ResourceName; -} +}; export const RESOURCE_LOAD_ERROR_MESSAGE = 'Resource load error'; diff --git a/lib/getArbitrumVerificationStepStatus.ts b/lib/getArbitrumVerificationStepStatus.ts deleted file mode 100644 index e86114d115..0000000000 --- a/lib/getArbitrumVerificationStepStatus.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { ArbitrumBatchStatus, ArbitrumL2TxData } from 'types/api/arbitrumL2'; - -type Args = { - status: ArbitrumBatchStatus; - commitment_transaction: ArbitrumL2TxData; - confirmation_transaction: ArbitrumL2TxData; -} - -export default function getArbitrumVerificationStepStatus({ - status, - commitment_transaction: commitTx, - confirmation_transaction: confirmTx, -}: Args) { - if (status === 'Sent to base') { - if (commitTx.status === 'unfinalized') { - return 'pending'; - } - } - if (status === 'Confirmed on base') { - if (confirmTx.status === 'unfinalized') { - return 'pending'; - } - } - return 'finalized'; -} diff --git a/lib/growthbook/init.ts b/lib/growthbook/init.ts index 4aef06e705..d98b2b94b7 100644 --- a/lib/growthbook/init.ts +++ b/lib/growthbook/init.ts @@ -7,7 +7,6 @@ import { STORAGE_KEY, STORAGE_LIMIT } from './consts'; export interface GrowthBookFeatures { test_value: string; - action_button_exp: boolean; } export const growthBook = (() => { diff --git a/lib/hexToBase64.ts b/lib/hexToBase64.ts index 5b1366a6da..6fced2dc39 100644 --- a/lib/hexToBase64.ts +++ b/lib/hexToBase64.ts @@ -2,7 +2,7 @@ import bytesToBase64 from './bytesToBase64'; import hexToBytes from './hexToBytes'; export default function hexToBase64(hex: string) { - const bytes = new Uint8Array(hexToBytes(hex)); + const bytes = hexToBytes(hex); return bytesToBase64(bytes); } diff --git a/lib/hexToBytes.ts b/lib/hexToBytes.ts index e34435fbf4..d42c931930 100644 --- a/lib/hexToBytes.ts +++ b/lib/hexToBytes.ts @@ -5,5 +5,5 @@ export default function hexToBytes(hex: string) { for (let c = startIndex; c < hex.length; c += 2) { bytes.push(parseInt(hex.substring(c, c + 2), 16)); } - return bytes; + return new Uint8Array(bytes); } diff --git a/lib/hexToUtf8.ts b/lib/hexToUtf8.ts index 8766ee25cc..95e40ba090 100644 --- a/lib/hexToUtf8.ts +++ b/lib/hexToUtf8.ts @@ -2,7 +2,7 @@ import hexToBytes from 'lib/hexToBytes'; export default function hexToUtf8(hex: string) { const utf8decoder = new TextDecoder(); - const bytes = new Uint8Array(hexToBytes(hex)); + const bytes = hexToBytes(hex); return utf8decoder.decode(bytes); } diff --git a/lib/hooks/useAddressProfileApiQuery.tsx b/lib/hooks/useAddressProfileApiQuery.tsx new file mode 100644 index 0000000000..26298637fc --- /dev/null +++ b/lib/hooks/useAddressProfileApiQuery.tsx @@ -0,0 +1,44 @@ +import { useQuery } from '@tanstack/react-query'; +import * as v from 'valibot'; + +import config from 'configs/app'; +import type { ResourceError } from 'lib/api/resources'; +import useFetch from 'lib/hooks/useFetch'; + +const feature = config.features.addressProfileAPI; + +type AddressInfoApiQueryResponse = v.InferOutput; + +const AddressInfoSchema = v.object({ + user_profile: v.object({ + username: v.union([ v.string(), v.null() ]), + }), +}); + +const ERROR_NAME = 'Invalid response schema'; + +export default function useAddressProfileApiQuery(hash: string | undefined, isEnabled = true) { + const fetch = useFetch(); + + return useQuery, AddressInfoApiQueryResponse>({ + queryKey: [ 'username_api', hash ], + queryFn: async() => { + if (!feature.isEnabled || !hash) { + return Promise.reject(); + } + + return fetch(feature.apiUrlTemplate.replace('{address}', hash), undefined, { omitSentryErrorLog: true }); + }, + enabled: isEnabled && Boolean(hash), + refetchOnMount: false, + select: (response) => { + const parsedResponse = v.safeParse(AddressInfoSchema, response); + + if (!parsedResponse.success) { + throw Error(ERROR_NAME); + } + + return parsedResponse.output; + }, + }); +} diff --git a/lib/hooks/useContractTabs.tsx b/lib/hooks/useContractTabs.tsx deleted file mode 100644 index 24667f69a1..0000000000 --- a/lib/hooks/useContractTabs.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useRouter } from 'next/router'; -import React from 'react'; - -import type { Address } from 'types/api/address'; - -import useApiQuery from 'lib/api/useApiQuery'; -import * as cookies from 'lib/cookies'; -import getQueryParamString from 'lib/router/getQueryParamString'; -import useSocketChannel from 'lib/socket/useSocketChannel'; -import * as stubs from 'stubs/contract'; -import ContractCode from 'ui/address/contract/ContractCode'; -import ContractMethodsCustom from 'ui/address/contract/methods/ContractMethodsCustom'; -import ContractMethodsProxy from 'ui/address/contract/methods/ContractMethodsProxy'; -import ContractMethodsRegular from 'ui/address/contract/methods/ContractMethodsRegular'; -import { divideAbiIntoMethodTypes } from 'ui/address/contract/methods/utils'; - -const CONTRACT_TAB_IDS = [ - 'contract_code', - 'read_contract', - 'read_contract_rpc', - 'read_proxy', - 'read_custom_methods', - 'write_contract', - 'write_contract_rpc', - 'write_proxy', - 'write_custom_methods', -] as const; - -interface ContractTab { - id: typeof CONTRACT_TAB_IDS[number]; - title: string; - component: JSX.Element; -} - -interface ReturnType { - tabs: Array; - isLoading: boolean; -} - -export default function useContractTabs(data: Address | undefined, isPlaceholderData: boolean): ReturnType { - const [ isQueryEnabled, setIsQueryEnabled ] = React.useState(false); - - const router = useRouter(); - const tab = getQueryParamString(router.query.tab); - - const isEnabled = Boolean(data?.hash) && data?.is_contract && !isPlaceholderData && CONTRACT_TAB_IDS.concat('contract' as never).includes(tab); - - const enableQuery = React.useCallback(() => { - setIsQueryEnabled(true); - }, []); - - const contractQuery = useApiQuery('contract', { - pathParams: { hash: data?.hash }, - queryOptions: { - enabled: isEnabled && isQueryEnabled, - refetchOnMount: false, - placeholderData: data?.is_verified ? stubs.CONTRACT_CODE_VERIFIED : stubs.CONTRACT_CODE_UNVERIFIED, - }, - }); - - const customAbiQuery = useApiQuery('custom_abi', { - queryOptions: { - enabled: isEnabled && isQueryEnabled && Boolean(cookies.get(cookies.NAMES.API_TOKEN)), - refetchOnMount: false, - }, - }); - - const channel = useSocketChannel({ - topic: `addresses:${ data?.hash?.toLowerCase() }`, - isDisabled: !isEnabled, - onJoin: enableQuery, - onSocketError: enableQuery, - }); - - const methods = React.useMemo(() => divideAbiIntoMethodTypes(contractQuery.data?.abi ?? []), [ contractQuery.data?.abi ]); - const methodsCustomAbi = React.useMemo(() => { - return divideAbiIntoMethodTypes( - customAbiQuery.data - ?.find((item) => data && item.contract_address_hash.toLowerCase() === data.hash.toLowerCase()) - ?.abi ?? - [], - ); - }, [ customAbiQuery.data, data ]); - - const verifiedImplementations = React.useMemo(() => { - return data?.implementations?.filter(({ name, address }) => name && address && address !== data?.hash) || []; - }, [ data?.hash, data?.implementations ]); - - return React.useMemo(() => { - return { - tabs: [ - { - id: 'contract_code' as const, - title: 'Code', - component: , - }, - methods.read.length > 0 && { - id: 'read_contract' as const, - title: 'Read contract', - component: , - }, - methodsCustomAbi.read.length > 0 && { - id: 'read_custom_methods' as const, - title: 'Read custom', - component: , - }, - verifiedImplementations.length > 0 && { - id: 'read_proxy' as const, - title: 'Read proxy', - component: ( - - ), - }, - methods.write.length > 0 && { - id: 'write_contract' as const, - title: 'Write contract', - component: , - }, - methodsCustomAbi.write.length > 0 && { - id: 'write_custom_methods' as const, - title: 'Write custom', - component: , - }, - verifiedImplementations.length > 0 && { - id: 'write_proxy' as const, - title: 'Write proxy', - component: ( - - ), - }, - ].filter(Boolean), - isLoading: contractQuery.isPlaceholderData, - }; - }, [ contractQuery, channel, data?.hash, verifiedImplementations, methods.read, methods.write, methodsCustomAbi.read, methodsCustomAbi.write ]); -} diff --git a/lib/hooks/useGetCsrfToken.tsx b/lib/hooks/useGetCsrfToken.tsx index 297f1567c2..a301b25594 100644 --- a/lib/hooks/useGetCsrfToken.tsx +++ b/lib/hooks/useGetCsrfToken.tsx @@ -10,7 +10,7 @@ import useFetch from 'lib/hooks/useFetch'; export default function useGetCsrfToken() { const nodeApiFetch = useFetch(); - useQuery({ + return useQuery({ queryKey: getResourceKey('csrf'), queryFn: async() => { if (!isNeedProxy()) { diff --git a/lib/hooks/useGraphLinks.tsx b/lib/hooks/useGraphLinks.tsx new file mode 100644 index 0000000000..b8f83b4cf8 --- /dev/null +++ b/lib/hooks/useGraphLinks.tsx @@ -0,0 +1,19 @@ +import { useQuery } from '@tanstack/react-query'; + +import config from 'configs/app'; +import type { ResourceError } from 'lib/api/resources'; +import useFetch from 'lib/hooks/useFetch'; + +const feature = config.features.marketplace; + +export default function useGraphLinks() { + const fetch = useFetch(); + + return useQuery, Record>>({ + queryKey: [ 'graph-links' ], + queryFn: async() => fetch((feature.isEnabled && feature.graphLinksUrl) ? feature.graphLinksUrl : '', undefined, { resource: 'graph-links' }), + enabled: feature.isEnabled && Boolean(feature.graphLinksUrl), + staleTime: Infinity, + placeholderData: {}, + }); +} diff --git a/lib/hooks/useIsAccountActionAllowed.tsx b/lib/hooks/useIsAccountActionAllowed.tsx deleted file mode 100644 index c8df0912a3..0000000000 --- a/lib/hooks/useIsAccountActionAllowed.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useQueryClient } from '@tanstack/react-query'; -import React from 'react'; - -import type { UserInfo } from 'types/api/account'; - -import { resourceKey } from 'lib/api/resources'; -import useLoginUrl from 'lib/hooks/useLoginUrl'; - -export default function useIsAccountActionAllowed() { - const queryClient = useQueryClient(); - - const profileData = queryClient.getQueryData([ resourceKey('user_info') ]); - const isAuth = Boolean(profileData); - const loginUrl = useLoginUrl(); - - return React.useCallback(() => { - if (!loginUrl) { - return false; - } - - if (!isAuth) { - window.location.assign(loginUrl); - return false; - } - - return true; - }, [ isAuth, loginUrl ]); -} diff --git a/lib/hooks/useLazyRenderedList.tsx b/lib/hooks/useLazyRenderedList.tsx index 245d8a0b0b..3f2d828fe0 100644 --- a/lib/hooks/useLazyRenderedList.tsx +++ b/lib/hooks/useLazyRenderedList.tsx @@ -5,12 +5,12 @@ import { useInView } from 'react-intersection-observer'; const STEP = 10; const MIN_ITEMS_NUM = 50; -export default function useLazyRenderedList(list: Array, isEnabled: boolean) { - const [ renderedItemsNum, setRenderedItemsNum ] = React.useState(MIN_ITEMS_NUM); +export default function useLazyRenderedList(list: Array, isEnabled: boolean, minItemsNum: number = MIN_ITEMS_NUM) { + const [ renderedItemsNum, setRenderedItemsNum ] = React.useState(minItemsNum); const { ref, inView } = useInView({ rootMargin: '200px', triggerOnce: false, - skip: !isEnabled || list.length <= MIN_ITEMS_NUM, + skip: !isEnabled || list.length <= minItemsNum, }); React.useEffect(() => { diff --git a/lib/hooks/useLoginUrl.tsx b/lib/hooks/useLoginUrl.tsx deleted file mode 100644 index a111d1eebb..0000000000 --- a/lib/hooks/useLoginUrl.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { useRouter } from 'next/router'; - -import { route } from 'nextjs-routes'; - -import config from 'configs/app'; - -const feature = config.features.account; - -export default function useLoginUrl() { - const router = useRouter(); - return feature.isEnabled ? - feature.authUrl + route({ pathname: '/auth/auth0', query: { path: router.asPath } }) : - undefined; -} diff --git a/lib/hooks/useNavItems.tsx b/lib/hooks/useNavItems.tsx index 608a6b6d0d..a064d37e32 100644 --- a/lib/hooks/useNavItems.tsx +++ b/lib/hooks/useNavItems.tsx @@ -5,12 +5,10 @@ import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/naviga import config from 'configs/app'; import { rightLineArrow } from 'lib/html-entities'; -import UserAvatar from 'ui/shared/UserAvatar'; interface ReturnType { mainNavItems: Array; accountNavItems: Array; - profileItem: NavItem; } export function isGroupItem(item: NavItem | NavGroupItem): item is NavGroupItem { @@ -102,6 +100,12 @@ export default function useNavItems(): ReturnType { icon: 'games', isActive: pathname === '/dispute-games', } : null; + const mudWorlds = config.features.mudFramework.isEnabled ? { + text: 'MUD worlds', + nextRoute: { pathname: '/mud-worlds' as const }, + icon: 'MUD_menu', + isActive: pathname === '/mud-worlds', + } : null; const rollupFeature = config.features.rollup; @@ -116,11 +120,12 @@ export default function useNavItems(): ReturnType { blocks, rollupTxnBatches, rollupDisputeGames, - rollupFeature.type === 'optimistic' ? rollupOutputRoots : undefined, + rollupFeature.outputRootsEnabled ? rollupOutputRoots : undefined, ].filter(Boolean), [ userOps, topAccounts, + mudWorlds, validators, verifiedContracts, ensLookup, @@ -174,6 +179,21 @@ export default function useNavItems(): ReturnType { ].filter(Boolean); } + const tokensNavItems = [ + { + text: 'Tokens', + nextRoute: { pathname: '/tokens' as const }, + icon: 'token', + isActive: pathname === '/tokens' || pathname.startsWith('/token/'), + }, + { + text: 'Token transfers', + nextRoute: { pathname: '/token-transfers' as const }, + icon: 'token-transfers', + isActive: pathname === '/token-transfers', + }, + ]; + const apiNavItems: Array = [ config.features.restApiDocs.isEnabled ? { text: 'REST API', @@ -227,9 +247,9 @@ export default function useNavItems(): ReturnType { }, { text: 'Tokens', - nextRoute: { pathname: '/tokens' as const }, icon: 'token', - isActive: pathname.startsWith('/token'), + isActive: tokensNavItems.flat().some(item => isInternalItem(item) && item.isActive), + subItems: tokensNavItems, }, config.features.marketplace.isEnabled ? { text: 'DApps', @@ -241,7 +261,7 @@ export default function useNavItems(): ReturnType { text: 'Charts & stats', nextRoute: { pathname: '/stats' as const }, icon: 'stats', - isActive: pathname === '/stats', + isActive: pathname.startsWith('/stats'), } : null, apiNavItems.length > 0 && { text: 'API', @@ -290,13 +310,6 @@ export default function useNavItems(): ReturnType { }, ].filter(Boolean); - const profileItem = { - text: 'My profile', - nextRoute: { pathname: '/auth/profile' as const }, - iconComponent: UserAvatar, - isActive: pathname === '/auth/profile', - }; - - return { mainNavItems, accountNavItems, profileItem }; + return { mainNavItems, accountNavItems }; }, [ pathname ]); } diff --git a/lib/hooks/useNewTxsSocket.tsx b/lib/hooks/useNewTxsSocket.tsx index bedae0049d..576ecec46f 100644 --- a/lib/hooks/useNewTxsSocket.tsx +++ b/lib/hooks/useNewTxsSocket.tsx @@ -57,11 +57,11 @@ export default function useNewTxsSocket() { }, [ setNum ]); const handleSocketClose = React.useCallback(() => { - setSocketAlert('Connection is lost. Please reload page.'); + setSocketAlert('Connection is lost. Please reload the page.'); }, []); const handleSocketError = React.useCallback(() => { - setSocketAlert('An error has occurred while fetching new transactions. Please reload page.'); + setSocketAlert('An error has occurred while fetching new transactions. Please reload the page.'); }, []); const channel = useSocketChannel({ diff --git a/lib/hooks/useRedirectForInvalidAuthToken.tsx b/lib/hooks/useRedirectForInvalidAuthToken.tsx deleted file mode 100644 index 24b74ace64..0000000000 --- a/lib/hooks/useRedirectForInvalidAuthToken.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as Sentry from '@sentry/react'; -import { useQueryClient } from '@tanstack/react-query'; -import React from 'react'; - -import { resourceKey } from 'lib/api/resources'; -import type { ResourceError } from 'lib/api/resources'; -import * as cookies from 'lib/cookies'; -import useLoginUrl from 'lib/hooks/useLoginUrl'; - -export default function useRedirectForInvalidAuthToken() { - const queryClient = useQueryClient(); - - const state = queryClient.getQueryState([ resourceKey('user_info') ]); - const errorStatus = state?.error?.status; - const loginUrl = useLoginUrl(); - - React.useEffect(() => { - if (errorStatus === 401) { - const apiToken = cookies.get(cookies.NAMES.API_TOKEN); - - if (apiToken && loginUrl) { - Sentry.captureException(new Error('Invalid API token'), { tags: { source: 'invalid_api_token' } }); - window.location.assign(loginUrl); - } - } - }, [ errorStatus, loginUrl ]); -} diff --git a/lib/hooks/useTimeAgoIncrement.tsx b/lib/hooks/useTimeAgoIncrement.tsx index 270bada27e..2429528e87 100644 --- a/lib/hooks/useTimeAgoIncrement.tsx +++ b/lib/hooks/useTimeAgoIncrement.tsx @@ -19,7 +19,7 @@ function getUnits(diff: number) { return [ DAY, 2 * DAY ]; } -function getUpdateParams(ts: string) { +function getUpdateParams(ts: string | number) { const timeDiff = Date.now() - new Date(ts).getTime(); const [ unit, higherUnit ] = getUnits(timeDiff); @@ -41,7 +41,7 @@ function getUpdateParams(ts: string) { }; } -export default function useTimeAgoIncrement(ts: string | null, isEnabled?: boolean) { +export default function useTimeAgoIncrement(ts: string | number | null, isEnabled?: boolean) { const [ value, setValue ] = React.useState(ts ? dayjs(ts).fromNow() : null); React.useEffect(() => { @@ -78,6 +78,8 @@ export default function useTimeAgoIncrement(ts: string | null, isEnabled?: boole isEnabled && startIncrement(); + !isEnabled && setValue(dayjs(ts).fromNow()); + return () => { timeouts.forEach(window.clearTimeout); intervals.forEach(window.clearInterval); diff --git a/lib/hooks/useToast.tsx b/lib/hooks/useToast.tsx index c9e0f3d63f..7afc6f3e11 100644 --- a/lib/hooks/useToast.tsx +++ b/lib/hooks/useToast.tsx @@ -12,7 +12,8 @@ const defaultOptions: UseToastOptions & { toastComponent?: React.FC position: 'top-right', isClosable: true, containerStyle: { - margin: 8, + margin: 3, + marginBottom: 0, }, variant: 'subtle', }; diff --git a/lib/isMetaKey.tsx b/lib/isMetaKey.tsx index 878462bf8d..ce9b0a9d2e 100644 --- a/lib/isMetaKey.tsx +++ b/lib/isMetaKey.tsx @@ -1,3 +1,5 @@ +import type React from 'react'; + export default function isMetaKey(event: React.KeyboardEvent) { - return event.metaKey || event.getModifierState('Meta') || event.getModifierState('OS'); + return event.metaKey || event.getModifierState('Meta'); } diff --git a/lib/metadata/generate.ts b/lib/metadata/generate.ts index 9282da7fe7..ee9c3f55cd 100644 --- a/lib/metadata/generate.ts +++ b/lib/metadata/generate.ts @@ -20,7 +20,7 @@ export default function generate(route: Rout }; const title = compileValue(templates.title.make(route.pathname, Boolean(apiData)), params); - const description = compileValue(templates.description.make(route.pathname), params); + const description = compileValue(templates.description.make(route.pathname, Boolean(apiData)), params); const pageOgType = getPageOgType(route.pathname); diff --git a/lib/metadata/getPageOgType.ts b/lib/metadata/getPageOgType.ts index 8d709db84e..b0c6580b4e 100644 --- a/lib/metadata/getPageOgType.ts +++ b/lib/metadata/getPageOgType.ts @@ -9,7 +9,10 @@ const OG_TYPE_DICT: Record = { '/tx/[hash]': 'Regular page', '/blocks': 'Root page', '/block/[height_or_hash]': 'Regular page', + '/block/countdown': 'Regular page', + '/block/countdown/[height]': 'Regular page', '/accounts': 'Root page', + '/accounts/label/[slug]': 'Root page', '/address/[hash]': 'Regular page', '/verified-contracts': 'Root page', '/contract-verification': 'Root page', @@ -20,10 +23,12 @@ const OG_TYPE_DICT: Record = { '/apps': 'Root page', '/apps/[id]': 'Regular page', '/stats': 'Root page', + '/stats/[id]': 'Regular page', '/api-docs': 'Regular page', '/graphiql': 'Regular page', '/search-results': 'Regular page', '/auth/profile': 'Root page', + '/account/rewards': 'Regular page', '/account/watchlist': 'Regular page', '/account/api-key': 'Regular page', '/account/custom-abi': 'Regular page', @@ -46,11 +51,14 @@ const OG_TYPE_DICT: Record = { '/name-domains/[name]': 'Regular page', '/validators': 'Root page', '/gas-tracker': 'Root page', + '/mud-worlds': 'Root page', + '/token-transfers': 'Root page', // service routes, added only to make typescript happy '/login': 'Regular page', '/sprite': 'Regular page', '/api/metrics': 'Regular page', + '/api/monitoring/invalid-api-schema': 'Regular page', '/api/log': 'Regular page', '/api/media-type': 'Regular page', '/api/proxy': 'Regular page', @@ -58,8 +66,6 @@ const OG_TYPE_DICT: Record = { '/api/healthz': 'Regular page', '/api/config': 'Regular page', '/api/sprite': 'Regular page', - '/auth/auth0': 'Regular page', - '/auth/unverified-email': 'Regular page', }; export default function getPageOgType(pathname: Route['pathname']) { diff --git a/lib/metadata/templates/description.ts b/lib/metadata/templates/description.ts index 5effb299c7..7fb5df04c1 100644 --- a/lib/metadata/templates/description.ts +++ b/lib/metadata/templates/description.ts @@ -2,7 +2,6 @@ import type { Route } from 'nextjs-routes'; // equal og:description -// eslint-disable-next-line max-len const DEFAULT_TEMPLATE = 'Blockscout is the #1 open-source blockchain explorer available today. 100+ chains and counting rely on Blockscout data availability, APIs, and ecosystem tools to support their networks.'; // FIXME all page descriptions will be updated later @@ -13,7 +12,10 @@ const TEMPLATE_MAP: Record = { '/tx/[hash]': 'View transaction %hash% on %network_title%', '/blocks': DEFAULT_TEMPLATE, '/block/[height_or_hash]': 'View the transactions, token transfers, and uncles for block %height_or_hash%', + '/block/countdown': DEFAULT_TEMPLATE, + '/block/countdown/[height]': DEFAULT_TEMPLATE, '/accounts': DEFAULT_TEMPLATE, + '/accounts/label/[slug]': DEFAULT_TEMPLATE, '/address/[hash]': 'View the account balance, transactions, and other data for %hash% on the %network_title%', '/verified-contracts': DEFAULT_TEMPLATE, '/contract-verification': DEFAULT_TEMPLATE, @@ -24,10 +26,12 @@ const TEMPLATE_MAP: Record = { '/apps': DEFAULT_TEMPLATE, '/apps/[id]': DEFAULT_TEMPLATE, '/stats': DEFAULT_TEMPLATE, + '/stats/[id]': DEFAULT_TEMPLATE, '/api-docs': DEFAULT_TEMPLATE, '/graphiql': DEFAULT_TEMPLATE, '/search-results': DEFAULT_TEMPLATE, '/auth/profile': DEFAULT_TEMPLATE, + '/account/rewards': DEFAULT_TEMPLATE, '/account/watchlist': DEFAULT_TEMPLATE, '/account/api-key': DEFAULT_TEMPLATE, '/account/custom-abi': DEFAULT_TEMPLATE, @@ -50,11 +54,14 @@ const TEMPLATE_MAP: Record = { '/name-domains/[name]': DEFAULT_TEMPLATE, '/validators': DEFAULT_TEMPLATE, '/gas-tracker': DEFAULT_TEMPLATE, + '/mud-worlds': DEFAULT_TEMPLATE, + '/token-transfers': DEFAULT_TEMPLATE, // service routes, added only to make typescript happy '/login': DEFAULT_TEMPLATE, '/sprite': DEFAULT_TEMPLATE, '/api/metrics': DEFAULT_TEMPLATE, + '/api/monitoring/invalid-api-schema': DEFAULT_TEMPLATE, '/api/log': DEFAULT_TEMPLATE, '/api/media-type': DEFAULT_TEMPLATE, '/api/proxy': DEFAULT_TEMPLATE, @@ -62,12 +69,12 @@ const TEMPLATE_MAP: Record = { '/api/healthz': DEFAULT_TEMPLATE, '/api/config': DEFAULT_TEMPLATE, '/api/sprite': DEFAULT_TEMPLATE, - '/auth/auth0': DEFAULT_TEMPLATE, - '/auth/unverified-email': DEFAULT_TEMPLATE, }; -export function make(pathname: Route['pathname']) { - const template = TEMPLATE_MAP[pathname]; +const TEMPLATE_MAP_ENHANCED: Partial> = { + '/stats/[id]': '%description%', +}; - return template ?? ''; +export function make(pathname: Route['pathname'], isEnriched = false) { + return (isEnriched ? TEMPLATE_MAP_ENHANCED[pathname] : undefined) ?? TEMPLATE_MAP[pathname] ?? ''; } diff --git a/lib/metadata/templates/title.ts b/lib/metadata/templates/title.ts index c0934b13a8..dca9185e32 100644 --- a/lib/metadata/templates/title.ts +++ b/lib/metadata/templates/title.ts @@ -9,7 +9,10 @@ const TEMPLATE_MAP: Record = { '/tx/[hash]': '%network_name% transaction %hash%', '/blocks': '%network_name% blocks', '/block/[height_or_hash]': '%network_name% block %height_or_hash%', + '/block/countdown': '%network_name% block countdown index', + '/block/countdown/[height]': '%network_name% block %height% countdown', '/accounts': '%network_name% top accounts', + '/accounts/label/[slug]': '%network_name% addresses search by label', '/address/[hash]': '%network_name% address details for %hash%', '/verified-contracts': 'Verified %network_name% contracts lookup - %network_name% explorer', '/contract-verification': '%network_name% verify contract', @@ -20,10 +23,12 @@ const TEMPLATE_MAP: Record = { '/apps': '%network_name% DApps - Explore top apps', '/apps/[id]': '%network_name% marketplace app', '/stats': '%network_name% stats - %network_name% network insights', + '/stats/[id]': '%network_name% stats - %id% chart', '/api-docs': '%network_name% API docs - %network_name% developer tools', '/graphiql': 'GraphQL for %network_name% - %network_name% data query', '/search-results': '%network_name% search result for %q%', '/auth/profile': '%network_name% - my profile', + '/account/rewards': '%network_name% - rewards', '/account/watchlist': '%network_name% - watchlist', '/account/api-key': '%network_name% - API keys', '/account/custom-abi': '%network_name% - custom ABI', @@ -36,8 +41,8 @@ const TEMPLATE_MAP: Record = { '/deposits': '%network_name% deposits (L1 > L2)', '/output-roots': '%network_name% output roots', '/dispute-games': '%network_name% dispute games', - '/batches': '%network_name% tx batches (L2 blocks)', - '/batches/[number]': '%network_name% L2 tx batch %number%', + '/batches': '%network_name% txn batches', + '/batches/[number]': '%network_name% L2 txn batch %number%', '/blobs/[hash]': '%network_name% blob %hash% details', '/ops': 'User operations on %network_name% - %network_name% explorer', '/op/[hash]': '%network_name% user operation %hash%', @@ -46,11 +51,14 @@ const TEMPLATE_MAP: Record = { '/name-domains/[name]': '%network_name% %name% domain details', '/validators': '%network_name% validators list', '/gas-tracker': '%network_name% gas tracker - Current gas fees', + '/mud-worlds': '%network_name% MUD worlds list', + '/token-transfers': '%network_name% token transfers', // service routes, added only to make typescript happy '/login': '%network_name% login', '/sprite': '%network_name% SVG sprite', '/api/metrics': '%network_name% node API prometheus metrics', + '/api/monitoring/invalid-api-schema': '%network_name% node API prometheus metrics', '/api/log': '%network_name% node API request log', '/api/media-type': '%network_name% node API media type', '/api/proxy': '%network_name% node API proxy', @@ -58,8 +66,6 @@ const TEMPLATE_MAP: Record = { '/api/healthz': '%network_name% node API health check', '/api/config': '%network_name% node API app config', '/api/sprite': '%network_name% node API SVG sprite content', - '/auth/auth0': '%network_name% authentication', - '/auth/unverified-email': '%network_name% unverified email', }; const TEMPLATE_MAP_ENHANCED: Partial> = { @@ -67,6 +73,7 @@ const TEMPLATE_MAP_ENHANCED: Partial> = { '/token/[hash]/instance/[id]': '%network_name% token instance for %symbol%', '/apps/[id]': '%network_name% - %app_name%', '/address/[hash]': '%network_name% address details for %domain_name%', + '/stats/[id]': '%title% chart on %network_name%', }; export function make(pathname: Route['pathname'], isEnriched = false) { diff --git a/lib/metadata/types.ts b/lib/metadata/types.ts index fda74301ba..4cd6d62c37 100644 --- a/lib/metadata/types.ts +++ b/lib/metadata/types.ts @@ -1,14 +1,16 @@ +import type { LineChart } from '@blockscout/stats-types'; import type { TokenInfo } from 'types/api/token'; import type { Route } from 'nextjs-routes'; -/* eslint-disable @typescript-eslint/indent */ +/* eslint-disable @stylistic/indent */ export type ApiData = ( Pathname extends '/address/[hash]' ? { domain_name: string } : Pathname extends '/token/[hash]' ? TokenInfo : Pathname extends '/token/[hash]/instance/[id]' ? { symbol: string } : Pathname extends '/apps/[id]' ? { app_name: string } : + Pathname extends '/stats/[id]' ? LineChart['info'] : never ) | null; diff --git a/lib/mixpanel/getPageType.ts b/lib/mixpanel/getPageType.ts index 13a60c1dd8..71e52d5b90 100644 --- a/lib/mixpanel/getPageType.ts +++ b/lib/mixpanel/getPageType.ts @@ -7,7 +7,10 @@ export const PAGE_TYPE_DICT: Record = { '/tx/[hash]': 'Transaction details', '/blocks': 'Blocks', '/block/[height_or_hash]': 'Block details', + '/block/countdown': 'Block countdown search', + '/block/countdown/[height]': 'Block countdown', '/accounts': 'Top accounts', + '/accounts/label/[slug]': 'Addresses search by label', '/address/[hash]': 'Address details', '/verified-contracts': 'Verified contracts', '/contract-verification': 'Contract verification', @@ -18,10 +21,12 @@ export const PAGE_TYPE_DICT: Record = { '/apps': 'DApps', '/apps/[id]': 'DApp', '/stats': 'Stats', + '/stats/[id]': 'Stats chart', '/api-docs': 'REST API', '/graphiql': 'GraphQL', '/search-results': 'Search results', '/auth/profile': 'Profile', + '/account/rewards': 'Merits', '/account/watchlist': 'Watchlist', '/account/api-key': 'API keys', '/account/custom-abi': 'Custom ABI', @@ -34,8 +39,8 @@ export const PAGE_TYPE_DICT: Record = { '/deposits': 'Deposits (L1 > L2)', '/output-roots': 'Output roots', '/dispute-games': 'Dispute games', - '/batches': 'Tx batches (L2 blocks)', - '/batches/[number]': 'L2 tx batch details', + '/batches': 'Txn batches', + '/batches/[number]': 'L2 txn batch details', '/blobs/[hash]': 'Blob details', '/ops': 'User operations', '/op/[hash]': 'User operation details', @@ -44,11 +49,14 @@ export const PAGE_TYPE_DICT: Record = { '/name-domains/[name]': 'Domain details', '/validators': 'Validators list', '/gas-tracker': 'Gas tracker', + '/mud-worlds': 'MUD worlds', + '/token-transfers': 'Token transfers', // service routes, added only to make typescript happy '/login': 'Login', '/sprite': 'Sprite', '/api/metrics': 'Node API: Prometheus metrics', + '/api/monitoring/invalid-api-schema': 'Node API: Prometheus metrics', '/api/log': 'Node API: Request log', '/api/media-type': 'Node API: Media type', '/api/proxy': 'Node API: Proxy', @@ -56,8 +64,6 @@ export const PAGE_TYPE_DICT: Record = { '/api/healthz': 'Node API: Health check', '/api/config': 'Node API: App config', '/api/sprite': 'Node API: SVG sprite content', - '/auth/auth0': 'Auth', - '/auth/unverified-email': 'Unverified email', }; export default function getPageType(pathname: Route['pathname']) { diff --git a/lib/mixpanel/useLogPageView.tsx b/lib/mixpanel/useLogPageView.tsx index 9c3f6a929a..da5a6952f0 100644 --- a/lib/mixpanel/useLogPageView.tsx +++ b/lib/mixpanel/useLogPageView.tsx @@ -1,16 +1,26 @@ +import type { ColorMode } from '@chakra-ui/react'; import { useColorMode } from '@chakra-ui/react'; import { usePathname } from 'next/navigation'; import { useRouter } from 'next/router'; import React from 'react'; import config from 'configs/app'; +import * as cookies from 'lib/cookies'; import getQueryParamString from 'lib/router/getQueryParamString'; +import { COLOR_THEMES } from 'lib/settings/colorTheme'; import getPageType from './getPageType'; import getTabName from './getTabName'; import logEvent from './logEvent'; import { EventTypes } from './utils'; +function getColorTheme(hex: string | undefined, colorMode: ColorMode) { + const colorTheme = COLOR_THEMES.find((theme) => theme.hex === hex) || + COLOR_THEMES.filter((theme) => theme.colorMode === colorMode).slice(-1)[0]; + + return colorTheme.id; +} + export default function useLogPageView(isInited: boolean) { const router = useRouter(); const pathname = usePathname(); @@ -24,11 +34,14 @@ export default function useLogPageView(isInited: boolean) { return; } + const cookieColorModeHex = cookies.get(cookies.NAMES.COLOR_MODE_HEX); + logEvent(EventTypes.PAGE_VIEW, { 'Page type': getPageType(router.pathname), Tab: getTabName(tab), Page: page || undefined, 'Color mode': colorMode, + 'Color theme': getColorTheme(cookieColorModeHex, colorMode), }); // these are only deps that should trigger the effect // in some scenarios page type is not changing (e.g navigation from one address page to another), diff --git a/lib/mixpanel/utils.ts b/lib/mixpanel/utils.ts index bc69e5e9dc..42fa625c27 100644 --- a/lib/mixpanel/utils.ts +++ b/lib/mixpanel/utils.ts @@ -1,4 +1,5 @@ import type { WalletType } from 'types/client/wallets'; +import type { ColorThemeId } from 'types/settings'; export enum EventTypes { PAGE_VIEW = 'Page view', @@ -6,6 +7,8 @@ export enum EventTypes { LOCAL_SEARCH = 'Local search', ADD_TO_WALLET = 'Add to wallet', ACCOUNT_ACCESS = 'Account access', + LOGIN = 'Login', + ACCOUNT_LINK_INFO = 'Account link info', PRIVATE_TAG = 'Private tag', VERIFY_ADDRESS = 'Verify address', VERIFY_TOKEN = 'Verify token', @@ -20,16 +23,18 @@ export enum EventTypes { FILTERS = 'Filters', BUTTON_CLICK = 'Button click', PROMO_BANNER = 'Promo banner', + APP_FEEDBACK = 'App feedback', } -/* eslint-disable @typescript-eslint/indent */ +/* eslint-disable @stylistic/indent */ export type EventPayload = Type extends EventTypes.PAGE_VIEW ? { 'Page type': string; - 'Tab': string; - 'Page'?: string; + Tab: string; + Page?: string; 'Color mode': 'light' | 'dark'; + 'Color theme': ColorThemeId | undefined; } : Type extends EventTypes.SEARCH_QUERY ? { 'Search query': string; @@ -38,50 +43,70 @@ Type extends EventTypes.SEARCH_QUERY ? { } : Type extends EventTypes.LOCAL_SEARCH ? { 'Search query': string; - 'Source': 'Marketplace'; + Source: 'Marketplace'; } : Type extends EventTypes.ADD_TO_WALLET ? ( { - 'Wallet': WalletType; - 'Target': 'network'; + Wallet: WalletType; + Target: 'network'; } | { - 'Wallet': WalletType; - 'Target': 'token'; - 'Token': string; + Wallet: WalletType; + Target: 'token'; + Token: string; } ) : Type extends EventTypes.ACCOUNT_ACCESS ? { - 'Action': 'Auth0 init' | 'Verification email resent' | 'Logged out'; + Action: 'Dropdown open' | 'Logged out'; +} : +Type extends EventTypes.LOGIN ? ( + { + Action: 'Started'; + Source: string; + } | { + Action: 'Wallet' | 'Email'; + Source: 'Options selector'; + } | { + Action: 'OTP sent'; + Source: 'Email'; + } | { + Action: 'Success'; + Source: 'Email' | 'Wallet'; + } +) : +Type extends EventTypes.ACCOUNT_LINK_INFO ? { + Source: 'Profile' | 'Login modal' | 'Profile dropdown' | 'Merits'; + Status: 'Started' | 'OTP sent' | 'Finished'; + Type: 'Email' | 'Wallet'; } : Type extends EventTypes.PRIVATE_TAG ? { - 'Action': 'Form opened' | 'Submit'; + Action: 'Form opened' | 'Submit'; 'Page type': string; 'Tag type': 'Address' | 'Tx'; } : Type extends EventTypes.VERIFY_ADDRESS ? ( { - 'Action': 'Form opened' | 'Address entered'; + Action: 'Form opened' | 'Address entered'; 'Page type': string; } | { - 'Action': 'Sign ownership'; + Action: 'Sign ownership'; 'Page type': string; 'Sign method': 'wallet' | 'manual'; } ) : Type extends EventTypes.VERIFY_TOKEN ? { - 'Action': 'Form opened' | 'Submit'; + Action: 'Form opened' | 'Submit'; } : Type extends EventTypes.WALLET_CONNECT ? { - 'Source': 'Header' | 'Smart contracts' | 'Swap button'; - 'Status': 'Started' | 'Connected'; + Source: 'Header' | 'Login' | 'Profile' | 'Profile dropdown' | 'Smart contracts' | 'Swap button' | 'Merits'; + Status: 'Started' | 'Connected'; } : Type extends EventTypes.WALLET_ACTION ? ( { - 'Action': 'Open' | 'Address click'; + Action: 'Open' | 'Address click'; } | { - 'Action': 'Send Transaction' | 'Sign Message' | 'Sign Typed Data'; - 'Address': string | undefined; - 'AppId': string; + Action: 'Send Transaction' | 'Sign Message' | 'Sign Typed Data'; + Address: string | undefined; + AppId: string; } ) : Type extends EventTypes.CONTRACT_INTERACTION ? { @@ -89,51 +114,60 @@ Type extends EventTypes.CONTRACT_INTERACTION ? { 'Method name': string; } : Type extends EventTypes.CONTRACT_VERIFICATION ? { - 'Method': string; - 'Status': 'Method selected' | 'Finished'; + Method: string; + Status: 'Method selected' | 'Finished'; } : Type extends EventTypes.QR_CODE ? { 'Page type': string; } : Type extends EventTypes.PAGE_WIDGET ? ( { - 'Type': 'Tokens dropdown' | 'Tokens show all (icon)' | 'Add to watchlist' | 'Address actions (more button)'; + Type: 'Tokens dropdown' | 'Tokens show all (icon)' | 'Add to watchlist' | 'Address actions (more button)'; } | { - 'Type': 'Favorite app' | 'More button' | 'Security score' | 'Total contracts' | 'Verified contracts' | 'Analyzed contracts'; - 'Info': string; - 'Source': 'Discovery view' | 'Security view' | 'App modal' | 'App page' | 'Security score popup' | 'Banner'; + Type: 'Favorite app' | 'More button' | 'Security score' | 'Total contracts' | 'Verified contracts' | 'Analyzed contracts'; + Info: string; + Source: 'Discovery view' | 'Security view' | 'App modal' | 'App page' | 'Security score popup' | 'Banner'; } | { - 'Type': 'Security score'; - 'Source': 'Analyzed contracts popup'; + Type: 'Security score'; + Source: 'Analyzed contracts popup'; } | { - 'Type': 'Action button'; - 'Info': string; - 'Source': 'Txn' | 'NFT collection' | 'NFT item'; + Type: 'Action button'; + Info: string; + Source: 'Txn' | 'NFT collection' | 'NFT item'; } | { - 'Type': 'Address tag'; - 'Info': string; - 'URL': string; + Type: 'Address tag'; + Info: string; + URL: string; + } | { + Type: 'Share chart'; + Info: string; } ) : Type extends EventTypes.TX_INTERPRETATION_INTERACTION ? { - 'Type': 'Address click' | 'Token click' | 'Domain click'; + Type: 'Address click' | 'Token click' | 'Domain click'; } : Type extends EventTypes.EXPERIMENT_STARTED ? { 'Experiment name': string; 'Variant name': string; - 'Source': 'growthbook'; + Source: 'growthbook'; } : Type extends EventTypes.FILTERS ? { - 'Source': 'Marketplace'; + Source: 'Marketplace'; 'Filter name': string; } : Type extends EventTypes.BUTTON_CLICK ? { - 'Content': string; - 'Source': string; + Content: string; + Source: string; } : Type extends EventTypes.PROMO_BANNER ? { - 'Source': 'Marketplace'; - 'Link': string; + Source: 'Marketplace'; + Link: string; +} : +Type extends EventTypes.APP_FEEDBACK ? { + Action: 'Rating'; + Source: 'Discovery' | 'App modal' | 'App page'; + AppId: string; + Score: number; } : undefined; -/* eslint-enable @typescript-eslint/indent */ +/* eslint-enable @stylistic/indent */ diff --git a/lib/monitoring/metrics.ts b/lib/monitoring/metrics.ts index f6f6b1a3e7..05bdad660b 100644 --- a/lib/monitoring/metrics.ts +++ b/lib/monitoring/metrics.ts @@ -8,6 +8,12 @@ const metrics = (() => { promClient.register.clear(); + const invalidApiSchema = new promClient.Counter({ + name: 'invalid_api_schema', + help: 'Number of invalid external API schema events', + labelNames: [ 'resource', 'url' ] as const, + }); + const socialPreviewBotRequests = new promClient.Counter({ name: 'social_preview_bot_requests_total', help: 'Number of incoming requests from social preview bots', @@ -27,7 +33,7 @@ const metrics = (() => { buckets: [ 0.2, 0.5, 1, 3, 10 ], }); - return { socialPreviewBotRequests, searchEngineBotRequests, apiRequestDuration }; + return { invalidApiSchema, socialPreviewBotRequests, searchEngineBotRequests, apiRequestDuration }; })(); export default metrics; diff --git a/lib/networks/getNetworkValidationActionText.ts b/lib/networks/getNetworkValidationActionText.ts new file mode 100644 index 0000000000..c6a36f1a3d --- /dev/null +++ b/lib/networks/getNetworkValidationActionText.ts @@ -0,0 +1,21 @@ +import config from 'configs/app'; + +export default function getNetworkValidationActionText() { + switch (config.chain.verificationType) { + case 'validation': { + return 'validated'; + } + case 'mining': { + return 'mined'; + } + case 'posting': { + return 'posted'; + } + case 'sequencing': { + return 'sequenced'; + } + default: { + return 'miner'; + } + } +} diff --git a/lib/networks/getNetworkValidatorTitle.ts b/lib/networks/getNetworkValidatorTitle.ts index 7435ee0293..a0c7977cd6 100644 --- a/lib/networks/getNetworkValidatorTitle.ts +++ b/lib/networks/getNetworkValidatorTitle.ts @@ -1,5 +1,21 @@ import config from 'configs/app'; export default function getNetworkValidatorTitle() { - return config.chain.verificationType === 'validation' ? 'validator' : 'miner'; + switch (config.chain.verificationType) { + case 'validation': { + return 'validator'; + } + case 'mining': { + return 'miner'; + } + case 'posting': { + return 'poster'; + } + case 'sequencing': { + return 'sequencer'; + } + default: { + return 'miner'; + } + } } diff --git a/lib/regexp.ts b/lib/regexp.ts index d0d708db7a..ab2b3288e3 100644 --- a/lib/regexp.ts +++ b/lib/regexp.ts @@ -5,3 +5,5 @@ export const IPFS_PREFIX = /^ipfs:\/\//i; export const HEX_REGEXP = /^(?:0x)?[\da-fA-F]+$/; export const FILE_EXTENSION = /\.([\da-z]+)$/i; + +export const BLOCK_HEIGHT = /^\d+$/; diff --git a/lib/rollups/arbitrum.ts b/lib/rollups/arbitrum.ts new file mode 100644 index 0000000000..7dbaac58d3 --- /dev/null +++ b/lib/rollups/arbitrum.ts @@ -0,0 +1,41 @@ +import { ARBITRUM_L2_TX_BATCH_STATUSES, type ArbitrumBatchStatus, type ArbitrumL2TxData } from 'types/api/arbitrumL2'; + +import config from 'configs/app'; + +const rollupFeature = config.features.rollup; + +type Args = { + status: ArbitrumBatchStatus; + commitment_transaction: ArbitrumL2TxData; + confirmation_transaction: ArbitrumL2TxData; +}; + +export const VERIFICATION_STEPS_MAP: Record = { + 'Processed on rollup': 'Processed on rollup', + 'Sent to base': rollupFeature.isEnabled && rollupFeature.parentChainName ? `Sent to ${ rollupFeature.parentChainName }` : 'Sent to parent chain', + 'Confirmed on base': rollupFeature.isEnabled && rollupFeature.parentChainName ? + `Confirmed on ${ rollupFeature.parentChainName }` : + 'Confirmed on parent chain', +}; + +export const verificationSteps = (() => { + return ARBITRUM_L2_TX_BATCH_STATUSES.map((status) => VERIFICATION_STEPS_MAP[status]); +})(); + +export function getVerificationStepStatus({ + status, + commitment_transaction: commitTx, + confirmation_transaction: confirmTx, +}: Args) { + if (status === 'Sent to base') { + if (commitTx.status === 'unfinalized') { + return 'pending'; + } + } + if (status === 'Confirmed on base') { + if (confirmTx.status === 'unfinalized') { + return 'pending'; + } + } + return 'finalized'; +} diff --git a/lib/socket/types.ts b/lib/socket/types.ts index 96ec44ca7e..11ae7db150 100644 --- a/lib/socket/types.ts +++ b/lib/socket/types.ts @@ -1,6 +1,7 @@ import type { Channel } from 'phoenix'; import type { AddressCoinBalanceHistoryItem, AddressTokensBalancesSocketMessage } from 'types/api/address'; +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'; @@ -16,7 +17,8 @@ SocketMessage.TxStatusUpdate | SocketMessage.TxRawTrace | SocketMessage.NewTx | SocketMessage.NewPendingTx | -SocketMessage.NewDeposits | +SocketMessage.NewOptimisticDeposits | +SocketMessage.NewArbitrumDeposits | SocketMessage.AddressBalance | SocketMessage.AddressCurrentCoinBalance | SocketMessage.AddressTokenBalance | @@ -36,6 +38,7 @@ SocketMessage.TokenTotalSupply | SocketMessage.TokenInstanceMetadataFetched | SocketMessage.ContractVerification | SocketMessage.NewZkEvmL2Batch | +SocketMessage.NewArbitrumL2Batch | SocketMessage.Unknown; interface SocketMessageParamsGeneric { @@ -44,16 +47,16 @@ interface SocketMessageParamsGeneric void; } -// eslint-disable-next-line @typescript-eslint/no-namespace export namespace SocketMessage { export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>; - export type BlocksIndexStatus = SocketMessageParamsGeneric<'block_index_status', {finished: boolean; ratio: string}>; - export type InternalTxsIndexStatus = SocketMessageParamsGeneric<'internal_txs_index_status', {finished: boolean; ratio: string}>; + export type BlocksIndexStatus = SocketMessageParamsGeneric<'block_index_status', { finished: boolean; ratio: string }>; + export type InternalTxsIndexStatus = SocketMessageParamsGeneric<'internal_txs_index_status', { finished: boolean; ratio: string }>; export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>; export type TxRawTrace = SocketMessageParamsGeneric<'raw_trace', RawTracesResponse>; export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>; export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>; - export type NewDeposits = SocketMessageParamsGeneric<'deposits', { deposits: number }>; + export type NewOptimisticDeposits = SocketMessageParamsGeneric<'deposits', { deposits: number }>; + export type NewArbitrumDeposits = SocketMessageParamsGeneric<'new_messages_to_rollup_amount', { new_messages_to_rollup_amount: number }>; export type AddressBalance = SocketMessageParamsGeneric<'balance', { balance: string; block_number: number; exchange_rate: string }>; export type AddressCurrentCoinBalance = SocketMessageParamsGeneric<'current_coin_balance', { coin_balance: string; block_number: number; exchange_rate: string }>; @@ -69,10 +72,11 @@ export namespace SocketMessage { export type AddressChangedBytecode = SocketMessageParamsGeneric<'changed_bytecode', Record>; export type AddressFetchedBytecode = SocketMessageParamsGeneric<'fetched_bytecode', { fetched_bytecode: string }>; export type SmartContractWasVerified = SocketMessageParamsGeneric<'smart_contract_was_verified', Record>; - export type TokenTransfers = SocketMessageParamsGeneric<'token_transfer', {token_transfer: number }>; - export type TokenTotalSupply = SocketMessageParamsGeneric<'total_supply', {total_supply: number }>; + export type TokenTransfers = SocketMessageParamsGeneric<'token_transfer', { token_transfer: number }>; + 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 Unknown = SocketMessageParamsGeneric; } diff --git a/lib/solidityScan/schema.ts b/lib/solidityScan/schema.ts new file mode 100644 index 0000000000..04ade657b4 --- /dev/null +++ b/lib/solidityScan/schema.ts @@ -0,0 +1,25 @@ +import * as v from 'valibot'; + +export const SolidityScanIssueSeverityDistributionSchema = v.object({ + critical: v.number(), + gas: v.number(), + high: v.number(), + informational: v.number(), + low: v.number(), + medium: v.number(), +}); + +export const SolidityScanSchema = v.object({ + scan_report: v.object({ + contractname: v.string(), + scan_status: v.string(), + scan_summary: v.object({ + score_v2: v.string(), + issue_severity_distribution: SolidityScanIssueSeverityDistributionSchema, + }), + scanner_reference_url: v.string(), + }), +}); + +export type SolidityScanReport = v.InferOutput; +export type SolidityScanReportSeverityDistribution = v.InferOutput; diff --git a/lib/solidityScan/useFetchReport.ts b/lib/solidityScan/useFetchReport.ts new file mode 100644 index 0000000000..69e84b6e62 --- /dev/null +++ b/lib/solidityScan/useFetchReport.ts @@ -0,0 +1,51 @@ +import React from 'react'; +import * as v from 'valibot'; + +import buildUrl from 'lib/api/buildUrl'; +import useApiQuery from 'lib/api/useApiQuery'; +import { SOLIDITY_SCAN_REPORT } from 'stubs/contract'; + +import { SolidityScanSchema } from './schema'; + +interface Params { + hash: string; +} + +const RESOURCE_NAME = 'contract_solidity_scan_report'; +const ERROR_NAME = 'Invalid response schema'; + +export default function useFetchReport({ hash }: Params) { + const query = useApiQuery(RESOURCE_NAME, { + pathParams: { hash }, + queryOptions: { + select: (response) => { + const parsedResponse = v.safeParse(SolidityScanSchema, response); + + if (!parsedResponse.success) { + throw Error(ERROR_NAME); + } + + return parsedResponse.output; + }, + enabled: Boolean(hash), + placeholderData: SOLIDITY_SCAN_REPORT, + retry: 0, + }, + }); + + const errorMessage = query.error && 'message' in query.error ? query.error.message : undefined; + + React.useEffect(() => { + if (errorMessage === ERROR_NAME) { + fetch('/node-api/monitoring/invalid-api-schema', { + method: 'POST', + body: JSON.stringify({ + resource: RESOURCE_NAME, + url: buildUrl(RESOURCE_NAME, { hash }, undefined, true), + }), + }); + } + }, [ errorMessage, hash ]); + + return query; +} diff --git a/lib/token/metadata/attributesParser.ts b/lib/token/metadata/attributesParser.ts index 9f1ce7d53c..63e572fe72 100644 --- a/lib/token/metadata/attributesParser.ts +++ b/lib/token/metadata/attributesParser.ts @@ -75,5 +75,6 @@ export default function attributesParser(attributes: Array): Metadata[' trait_type: _upperFirst(trait || 'property'), }; }) + .filter((item) => item?.value) .filter(Boolean); } diff --git a/lib/tx/arbitrumMessageStatusDescription.ts b/lib/tx/arbitrumMessageStatusDescription.ts new file mode 100644 index 0000000000..8f2a635a12 --- /dev/null +++ b/lib/tx/arbitrumMessageStatusDescription.ts @@ -0,0 +1,10 @@ +/* eslint-disable max-len */ +import type { ArbitrumMessageStatus } from 'types/api/transaction'; + +export const MESSAGE_DESCRIPTIONS: Record = { + 'Syncing with base layer': 'The incoming message was discovered on the rollup, but the corresponding message on L1 has not yet been found', + 'Settlement pending': 'The transaction with the message was included in a rollup block, but there is no batch on L1 containing the block yet', + 'Waiting for confirmation': 'The rollup block with the transaction containing the message was included in a batch on L1, but it is still waiting for the expiration of the fraud proof countdown', + 'Ready for relay': 'The rollup state was confirmed successfully, and the message can be executed—funds can be claimed on L1', + Relayed: '', +}; diff --git a/lib/validations/address.ts b/lib/validations/address.ts deleted file mode 100644 index cfca644c41..0000000000 --- a/lib/validations/address.ts +++ /dev/null @@ -1,5 +0,0 @@ -// maybe it depends on the network?? - -export const ADDRESS_REGEXP = /^0x[a-fA-F\d]{40}$/; - -export const ADDRESS_LENGTH = 42; diff --git a/lib/validations/url.ts b/lib/validations/url.ts deleted file mode 100644 index b4b30d4e05..0000000000 --- a/lib/validations/url.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const validator = (value: string | undefined) => { - if (!value) { - return true; - } - - try { - new URL(value); - return true; - } catch (error) { - return 'Incorrect URL'; - } -}; diff --git a/lib/web3/useAccountWithDomain.ts b/lib/web3/useAccountWithDomain.ts new file mode 100644 index 0000000000..0eeb18e4c5 --- /dev/null +++ b/lib/web3/useAccountWithDomain.ts @@ -0,0 +1,31 @@ +import React from 'react'; + +import config from 'configs/app'; +import useApiQuery from 'lib/api/useApiQuery'; + +import useAccount from './useAccount'; + +export default function useAccountWithDomain(isEnabled: boolean) { + const { address } = useAccount(); + + const isQueryEnabled = config.features.nameService.isEnabled && Boolean(address) && Boolean(isEnabled); + + const domainQuery = useApiQuery('address_domain', { + pathParams: { + chainId: config.chain.id, + address, + }, + queryOptions: { + enabled: isQueryEnabled, + refetchOnMount: false, + }, + }); + + return React.useMemo(() => { + return { + address: isEnabled ? address : undefined, + domain: domainQuery.data?.domain?.name, + isLoading: isQueryEnabled && domainQuery.isLoading, + }; + }, [ address, domainQuery.data?.domain?.name, domainQuery.isLoading, isEnabled, isQueryEnabled ]); +} diff --git a/lib/web3/useAddOrSwitchChain.tsx b/lib/web3/useAddOrSwitchChain.tsx index 05811de6d7..71114f5642 100644 --- a/lib/web3/useAddOrSwitchChain.tsx +++ b/lib/web3/useAddOrSwitchChain.tsx @@ -1,3 +1,4 @@ +import _get from 'lodash/get'; import React from 'react'; import config from 'configs/app'; @@ -24,9 +25,10 @@ export default function useAddOrSwitchChain() { const errorObj = getErrorObj(error); const code = errorObj && 'code' in errorObj ? errorObj.code : undefined; + const originalErrorCode = _get(errorObj, 'data.originalError.code'); // This error code indicates that the chain has not been added to Wallet. - if (code === 4902) { + if (code === 4902 || originalErrorCode === 4902) { const params = [ { chainId: hexadecimalChainId, chainName: config.chain.name, @@ -39,7 +41,6 @@ export default function useAddOrSwitchChain() { blockExplorerUrls: [ config.app.baseUrl ], } ] as never; // in wagmi types for wallet_addEthereumChain method is not provided - // eslint-disable-next-line @typescript-eslint/no-explicit-any return await provider.request({ method: 'wallet_addEthereumChain', diff --git a/ui/snippets/walletMenu/useWallet.tsx b/lib/web3/useWallet.ts similarity index 62% rename from ui/snippets/walletMenu/useWallet.tsx rename to lib/web3/useWallet.ts index 9b252dded9..951ccdf555 100644 --- a/ui/snippets/walletMenu/useWallet.tsx +++ b/lib/web3/useWallet.ts @@ -1,18 +1,19 @@ import { useWeb3Modal, useWeb3ModalState } from '@web3modal/wagmi/react'; import React from 'react'; -import { useAccount, useDisconnect, useAccountEffect } from 'wagmi'; +import { useDisconnect, useAccountEffect } from 'wagmi'; import * as mixpanel from 'lib/mixpanel/index'; +import useAccount from 'lib/web3/useAccount'; interface Params { source: mixpanel.EventPayload['Source']; } -export default function useWallet({ source }: Params) { - const { open } = useWeb3Modal(); +export default function useWeb3Wallet({ source }: Params) { + const { open: openModal } = useWeb3Modal(); const { open: isOpen } = useWeb3ModalState(); const { disconnect } = useDisconnect(); - const [ isModalOpening, setIsModalOpening ] = React.useState(false); + const [ isOpening, setIsOpening ] = React.useState(false); const [ isClientLoaded, setIsClientLoaded ] = React.useState(false); const isConnectionStarted = React.useRef(false); @@ -21,12 +22,12 @@ export default function useWallet({ source }: Params) { }, []); const handleConnect = React.useCallback(async() => { - setIsModalOpening(true); - await open(); - setIsModalOpening(false); + setIsOpening(true); + await openModal(); + setIsOpening(false); mixpanel.logEvent(mixpanel.EventTypes.WALLET_CONNECT, { Source: source, Status: 'Started' }); isConnectionStarted.current = true; - }, [ open, source ]); + }, [ openModal, source ]); const handleAccountConnected = React.useCallback(({ isReconnected }: { isReconnected: boolean }) => { if (!isReconnected && isConnectionStarted.current) { @@ -44,17 +45,17 @@ export default function useWallet({ source }: Params) { useAccountEffect({ onConnect: handleAccountConnected }); - const { address, isDisconnected } = useAccount(); + const account = useAccount(); + const address = account.address; + const isConnected = isClientLoaded && !account.isDisconnected && account.address !== undefined; - const isWalletConnected = isClientLoaded && !isDisconnected && address !== undefined; - - return { - openModal: open, - isWalletConnected, - address: address || '', + return React.useMemo(() => ({ connect: handleConnect, disconnect: handleDisconnect, - isModalOpening, - isModalOpen: isOpen, - }; + isOpen: isOpening || isOpen, + isConnected, + isReconnecting: account.isReconnecting, + address, + openModal, + }), [ handleConnect, handleDisconnect, isOpening, isOpen, isConnected, account.isReconnecting, address, openModal ]); } diff --git a/lib/web3/wagmiConfig.ts b/lib/web3/wagmiConfig.ts index 22abb95a81..d8824f5f68 100644 --- a/lib/web3/wagmiConfig.ts +++ b/lib/web3/wagmiConfig.ts @@ -35,7 +35,10 @@ const wagmiConfig = (() => { url: config.app.baseUrl, icons: [ config.UI.navigation.icon.default ].filter(Boolean), }, - enableEmail: true, + auth: { + email: true, + socials: [], + }, ssr: true, batch: { multicall: { wait: 100 } }, }); diff --git a/lib/xStarScore/useFetchXStarScore.ts b/lib/xStarScore/useFetchXStarScore.ts new file mode 100644 index 0000000000..3247c95a31 --- /dev/null +++ b/lib/xStarScore/useFetchXStarScore.ts @@ -0,0 +1,51 @@ +import React from 'react'; +import * as v from 'valibot'; + +import config from 'configs/app'; +import buildUrl from 'lib/api/buildUrl'; +import useApiQuery from 'lib/api/useApiQuery'; + +interface Params { + hash: string; +} + +const RESOURCE_NAME = 'address_xstar_score'; +const ERROR_NAME = 'Invalid response schema'; + +export default function useFetchXStarScore({ hash }: Params) { + const query = useApiQuery(RESOURCE_NAME, { + pathParams: { hash }, + queryOptions: { + select: (response) => { + const parsedResponse = v.safeParse(v.object({ data: v.object({ level: v.nullable(v.string()) }) }), response); + + if (!parsedResponse.success) { + throw Error(ERROR_NAME); + } + + return parsedResponse.output; + }, + enabled: Boolean(hash) && config.features.xStarScore.isEnabled, + placeholderData: { + data: { level: 'Base' }, + }, + retry: 0, + }, + }); + + const errorMessage = query.error && 'message' in query.error ? query.error.message : undefined; + + React.useEffect(() => { + if (errorMessage === ERROR_NAME) { + fetch('/node-api/monitoring/invalid-api-schema', { + method: 'POST', + body: JSON.stringify({ + resource: RESOURCE_NAME, + url: buildUrl(RESOURCE_NAME, { hash }, undefined, true), + }), + }); + } + }, [ errorMessage, hash ]); + + return query; +} diff --git a/middleware.ts b/middleware.ts index 2f017d9cd5..cd820f475e 100644 --- a/middleware.ts +++ b/middleware.ts @@ -22,6 +22,7 @@ export function middleware(req: NextRequest) { const res = NextResponse.next(); middlewares.colorTheme(req, res); + middlewares.addressFormat(req, res); const end = Date.now(); diff --git a/mocks/address/address.ts b/mocks/address/address.ts index e58097c2f5..b3ad305aea 100644 --- a/mocks/address/address.ts +++ b/mocks/address/address.ts @@ -73,7 +73,7 @@ export const token: Address = { token: tokenInfo, block_number_balance_updated_at: 8201413, coin_balance: '1', - creation_tx_hash: '0xc38cf7377bf72d6436f63c37b01b24d032101f20ec1849286dc703c712f10c98', + creation_transaction_hash: '0xc38cf7377bf72d6436f63c37b01b24d032101f20ec1849286dc703c712f10c98', creator_address_hash: '0x34A9c688512ebdB575e82C50c9803F6ba2916E72', exchange_rate: '0.04311', has_decompiled_code: false, @@ -87,7 +87,7 @@ export const token: Address = { export const eoa: Address = { block_number_balance_updated_at: 30811263, coin_balance: '2782650189688719421432220500', - creation_tx_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e', + creation_transaction_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e', creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943', exchange_rate: '0.04311', has_decompiled_code: false, @@ -111,7 +111,7 @@ export const eoa: Address = { export const contract: Address = { block_number_balance_updated_at: 30811263, coin_balance: '27826501896887194214322205', - creation_tx_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e', + creation_transaction_hash: '0xf2aff6501b632604c39978b47d309813d8a1bcca721864bbe86abf59704f195e', creator_address_hash: '0x803ad3F50b9e1fF68615e8B053A186e1be288943', exchange_rate: '0.04311', has_decompiled_code: false, @@ -137,7 +137,7 @@ export const contract: Address = { export const validator: Address = { block_number_balance_updated_at: 30811932, coin_balance: '22910462800601256910890', - creation_tx_hash: null, + creation_transaction_hash: null, creator_address_hash: null, exchange_rate: '0.00432018', has_decompiled_code: false, @@ -157,3 +157,12 @@ export const validator: Address = { watchlist_address_id: null, ens_domain_name: null, }; + +export const filecoin = { + ...validator, + filecoin: { + actor_type: 'evm' as const, + id: 'f02977693', + robust: 'f410fuiwj6a3yxajbohrl5vu6ns6o2e2jriul52lvzci', + }, +}; diff --git a/mocks/address/epochRewards.ts b/mocks/address/epochRewards.ts new file mode 100644 index 0000000000..5b22a66cd8 --- /dev/null +++ b/mocks/address/epochRewards.ts @@ -0,0 +1,50 @@ +import type { AddressEpochRewardsResponse } from 'types/api/address'; + +import { tokenInfo } from 'mocks/tokens/tokenInfo'; + +import { withEns, withName, withoutName } from './address'; + +export const epochRewards: AddressEpochRewardsResponse = { + items: [ + { + type: 'delegated_payment', + amount: '136609473658452408568', + account: withName, + associated_account: withName, + block_hash: '0x', + block_number: 26369280, + block_timestamp: '2022-05-15T13:16:24Z', + epoch_number: 1526, + token: tokenInfo, + }, + { + type: 'group', + amount: '117205842355246195095', + account: withoutName, + associated_account: withoutName, + block_hash: '0x', + block_number: 26352000, + block_timestamp: '2022-05-15T13:16:24Z', + epoch_number: 1525, + token: tokenInfo, + }, + { + type: 'validator', + amount: '125659647325556554060', + account: withEns, + associated_account: withEns, + block_hash: '0x', + block_number: 26300160, + block_timestamp: '2022-05-15T13:16:24Z', + epoch_number: 1524, + token: tokenInfo, + }, + ], + next_page_params: { + amount: '71952055594478242556', + associated_account_address_hash: '0x30d060f129817c4de5fbc1366d53e19f43c8c64f', + block_number: 25954560, + items_count: 50, + type: 'delegated_payment', + }, +}; diff --git a/mocks/address/tokens.ts b/mocks/address/tokens.ts index c494c9bbe3..7505eebc57 100644 --- a/mocks/address/tokens.ts +++ b/mocks/address/tokens.ts @@ -184,22 +184,27 @@ const nftInstance = { value: '11', }; +const nftInstanceWithoutImage = { + ...nftInstance, + image_url: null, +}; + export const collections: AddressCollectionsResponse = { items: [ { token: tokens.tokenInfoERC1155a, amount: '100', - token_instances: Array(5).fill(nftInstance), + token_instances: Array(5).fill(nftInstanceWithoutImage), }, { token: tokens.tokenInfoERC20LongSymbol, amount: '100', - token_instances: Array(5).fill(nftInstance), + token_instances: Array(5).fill(nftInstanceWithoutImage), }, { token: tokens.tokenInfoERC1155WithoutName, amount: '1', - token_instances: [ nftInstance ], + token_instances: [ nftInstanceWithoutImage ], }, ], next_page_params: { diff --git a/mocks/apps/ratings.ts b/mocks/apps/ratings.ts new file mode 100644 index 0000000000..3a0322850d --- /dev/null +++ b/mocks/apps/ratings.ts @@ -0,0 +1,13 @@ +import { apps } from './apps'; + +export const ratings = { + records: [ + { + fields: { + appId: apps[0].id, + rating: 4.3, + count: 15, + }, + }, + ], +}; diff --git a/mocks/apps/securityReports.ts b/mocks/apps/securityReports.ts index 824a6fbe13..33457ddf2f 100644 --- a/mocks/apps/securityReports.ts +++ b/mocks/apps/securityReports.ts @@ -1,6 +1,8 @@ +import { apps } from './apps'; + export const securityReports = [ { - appName: 'token-approval-tracker', + appName: apps[0].id, doc: 'http://docs.li.fi/smart-contracts/deployments#mainnet', chainsData: { '1': { diff --git a/mocks/arbitrum/deposits.ts b/mocks/arbitrum/deposits.ts index 8bc7f290e8..b1f47d3f76 100644 --- a/mocks/arbitrum/deposits.ts +++ b/mocks/arbitrum/deposits.ts @@ -1,4 +1,4 @@ -import type { ArbitrumL2MessagesResponse } from 'types/api/arbitrumL2'; +import type { ArbitrumL2MessagesResponse, ArbitrumLatestDepositsResponse } from 'types/api/arbitrumL2'; export const baseResponse: ArbitrumL2MessagesResponse = { items: [ @@ -27,3 +27,20 @@ export const baseResponse: ArbitrumL2MessagesResponse = { direction: 'to-rollup', }, }; + +export const latestDepositsResponse: ArbitrumLatestDepositsResponse = { + items: [ + { + completion_transaction_hash: '0x3ccdf87449d3de6a9dcd3eddb7bc9ecdf1770d4631f03cdf12a098911618d138', + origination_transaction_block_number: 123400, + origination_transaction_hash: '0x210d9f70f411de1079e32a98473b04345a5ea6ff2340a8511ebc2df641274436', + origination_timestamp: '2023-06-01T14:46:48.000000Z', + }, + { + completion_transaction_hash: '0xd16d918b2f95a5cdf66824f6291b6d5eb80b6f4acab3f9fb82ee0ec4109646a0', + origination_timestamp: null, + origination_transaction_block_number: null, + origination_transaction_hash: null, + }, + ], +}; diff --git a/mocks/arbitrum/txnBatch.ts b/mocks/arbitrum/txnBatch.ts index ec86fa2944..5d163ba6bc 100644 --- a/mocks/arbitrum/txnBatch.ts +++ b/mocks/arbitrum/txnBatch.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ import type { ArbitrumL2TxnBatch } from 'types/api/arbitrumL2'; import { finalized } from './txnBatches'; @@ -8,4 +9,32 @@ export const batchData: ArbitrumL2TxnBatch = { before_acc: '0x2ed7c4985eb778d76ec400a43805e7feecc8c2afcdb492dbe5caf227de6d37bc', start_block: 1245209, end_block: 1245490, + data_availability: { + batch_data_container: 'in_blob4844', + }, +}; + +export const batchDataAnytrust: ArbitrumL2TxnBatch = { + ...finalized, + after_acc: '0xcd064f3409015e8e6407e492e5275a185e492c6b43ccf127f22092d8057a9ffb', + before_acc: '0x2ed7c4985eb778d76ec400a43805e7feecc8c2afcdb492dbe5caf227de6d37bc', + start_block: 1245209, + end_block: 1245490, + data_availability: { + batch_data_container: 'in_anytrust', + bls_signature: '0x142577943e30b1ad1b4e40a1c08e00c24a68d6c366f953e361048b7127e327b5bdb8f168ba986beae40cfaf79ea2788004d750555684751e361d6f6445e5c521b45ac93a76da24add241a4a5410ca3a09fa82cf0aafd78801cbd0ad99d5be6b3', + data_hash: '0x4ffada101d8185bcba227f2cff9e0ea0a4deeb08f328601a898131429a436ebe', + timeout: '2024-08-22T12:39:22Z', + signers: [ + { + key: '0x0c6694955b524d718ca445831c5375393773401f33725a79661379dddabd5fff28619dc070befd9ed73d699e5c236c1a163be58ba81002b6130709bc064af5d7ba947130b72056bf17263800f1a3ab2269c6a510ef8e7412fd56d1ef1b916a1306e3b1d9c82c099371bd9861582acaada3a16e9dfee5d0ebce61096598a82f112d0a935e8cab5c48d82e3104b0c7ba79157dad1a019a3e7f6ad077b8e6308b116fec0f58239622463c3631fa01e2b4272409215b8009422c16715dbede590906', + proof: '0x06dcb5e56764bb72e6a45e6deb301ca85d8c4315c1da2efa29927f2ac8fb25571ce31d2d603735fe03196f6d56bcbf9a1999a89a74d5369822c4445d676c15ed52e5008daa775dc9a839c99ff963a19946ac740579874dac4f639907ae1bc69f', + trusted: false, + }, + { + key: '0x0ee5aaeabd57313285207eb89366b411286cf3f1c5e30eb7e355f55385308b91d5807284323ee89a9743c70676f4949504ced3ed41612cbfda06ad55200c1c77d3fb3700059befd64c44bc4a57cb567ec1481ee564cf6cd6cf1f2f4a2dee6db00c547c38400ab118dedae8afd5bab93b703f76a0991baa5d43fbb125194c06b5461f8c738a3c4278a3d98e5456aec0720883c0d28919537a36e2ffd5f731e742b6653557d154c164e068ef983b367ef626faaed46f4eadecbb12b7e55f23175d', + trusted: true, + }, + ], + }, }; diff --git a/mocks/arbitrum/txnBatches.ts b/mocks/arbitrum/txnBatches.ts index 5d61f7a4ec..54ab913fa4 100644 --- a/mocks/arbitrum/txnBatches.ts +++ b/mocks/arbitrum/txnBatches.ts @@ -10,6 +10,7 @@ export const finalized: ArbitrumL2TxnBatchesItem = { hash: '0x262e7215739d6a7e33b2c20b45a838801a0f5f080f20bec8e54eb078420c4661', status: 'finalized', }, + batch_data_container: 'in_blob4844', }; export const unfinalized: ArbitrumL2TxnBatchesItem = { @@ -22,6 +23,8 @@ export const unfinalized: ArbitrumL2TxnBatchesItem = { hash: '0x262e7215739d6a7e33b2c20b45a838801a0f5f080f20bec8e54eb078420c4661', status: 'unfinalized', }, + batch_data_container: null, + }; export const baseResponse: ArbitrumL2TxnBatchesResponse = { diff --git a/mocks/blocks/block.ts b/mocks/blocks/block.ts index 2676c72c0e..e6402fca53 100644 --- a/mocks/blocks/block.ts +++ b/mocks/blocks/block.ts @@ -1,5 +1,12 @@ /* eslint-disable max-len */ -import type { Block, BlocksResponse } from 'types/api/block'; +import type { RpcBlock } from 'viem'; + +import type { Block, BlocksResponse, ZilliqaBlockData } from 'types/api/block'; + +import { ZERO_ADDRESS } from 'lib/consts'; + +import * as addressMock from '../address/address'; +import * as tokenMock from '../tokens/tokenInfo'; export const base: Block = { base_fee_per_gas: '10000000000', @@ -45,8 +52,8 @@ export const base: Block = { state_root: 'TODO', timestamp: '2022-11-11T11:59:35Z', total_difficulty: '10258276095980170141167591583995189665817672619', - tx_count: 5, - tx_fees: '26853607500000000', + transaction_count: 5, + transaction_fees: '26853607500000000', type: 'block', uncles_hashes: [], }; @@ -82,8 +89,8 @@ export const genesis: Block = { state_root: 'TODO', timestamp: '2017-12-16T00:13:24.000000Z', total_difficulty: '131072', - tx_count: 0, - tx_fees: '0', + transaction_count: 0, + transaction_fees: '0', type: 'block', uncles_hashes: [], }; @@ -104,7 +111,7 @@ export const base2: Block = { ens_domain_name: null, }, timestamp: '2022-11-11T11:46:05Z', - tx_count: 253, + transaction_count: 253, gas_target_percentage: 23.6433, gas_used: '6333342', gas_used_percentage: 87.859504, @@ -135,6 +142,73 @@ export const rootstock: Block = { minimum_gas_price: '59240000', }; +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, + is_epoch_block: true, + }, +}; + +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, +}; + export const withBlobTxs: Block = { ...base, blob_gas_price: '21518435987', @@ -144,6 +218,11 @@ export const withBlobTxs: Block = { blob_tx_count: 1, }; +export const withWithdrawals: Block = { + ...base, + withdrawals_count: 2, +}; + export const baseListResponse: BlocksResponse = { items: [ base, @@ -151,3 +230,107 @@ export const baseListResponse: BlocksResponse = { ], next_page_params: null, }; + +export const rpcBlockBase: RpcBlock = { + difficulty: '0x37fcc04bef8', + extraData: '0x476574682f76312e302e312d38326566323666362f6c696e75782f676f312e34', + gasLimit: '0x2fefd8', + gasUsed: '0x0', + hash: '0xfbafb4b7b6f6789338d15ff046f40dc608a42b1a33b093e109c6d7a36cd76f61', + logsBloom: '0x0', + miner: '0xe6a7a1d47ff21b6321162aea7c6cb457d5476bca', + mixHash: '0x038956b9df89d0c1f980fd656d045e912beafa515cff7d7fd3c5f34ffdcb9e4b', + nonce: '0xd8d3392f340bbb22', + number: '0x1869f', + parentHash: '0x576fd45e598c9f86835f50fe2c6e6d11df2d4c4b01f19e4241b7e793d852f9e4', + receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', + size: '0x225', + stateRoot: '0x32356228651d64cc5e6e7be87a556ecdbf40e876251dc867ba9e4bb82a0124a3', + timestamp: '0x55d19741', + totalDifficulty: '0x259e89748daae17', + transactions: [ + '0x0e70849f10e22fe2e53fe6755f86a572aa6bb2fc472f0b87d9e561efa1fc2e1f', + '0xae5624c77f06d0164301380afa7780ebe49debe77eb3d5167004d69bd188a09f', + ], + transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', + uncles: [], + baseFeePerGas: null, + blobGasUsed: `0x0`, + excessBlobGas: `0x0`, + sealFields: [], + withdrawals: [ + { address: '0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f', amount: '0x12128cd', index: '0x3216bbb', validatorIndex: '0x4dca3' }, + { address: '0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f', amount: '0x12027dd', index: '0x3216bbc', validatorIndex: '0x4dca4' }, + ], +}; + +export const rpcBlockWithTxsInfo: RpcBlock = { + ...rpcBlockBase, + transactions: [ + { + accessList: [ + { + address: '0x7af661a6463993e05a171f45d774cf37e761c83f', + storageKeys: [ + '0x0000000000000000000000000000000000000000000000000000000000000007', + '0x000000000000000000000000000000000000000000000000000000000000000c', + '0x0000000000000000000000000000000000000000000000000000000000000008', + '0x0000000000000000000000000000000000000000000000000000000000000006', + '0x0000000000000000000000000000000000000000000000000000000000000009', + '0x000000000000000000000000000000000000000000000000000000000000000a', + ], + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + storageKeys: [ + '0x0d726f311404f8052d44e7004a6ffb747709a6d3666a62ce3f5aad13374680ab', + '0x1a824a6850dcbd9223afea4418727593881e2911ed2e734272a263153159fe26', + '0xfae3a383c82daf853bbd8bbcd21280410599b135c274c01354ea7d3a5e09f43c', + ], + }, + ], + blockHash: '0xeb37ebc94e31773e5c5703073fd3911b2ab596f099d00d18b55ae3ac8203c1d5', + blockNumber: '0x136058d', + chainId: '0x1', + from: '0x111527f1386c6725a2f5986230f3060bdcac041f', + gas: '0xf4240', + gasPrice: '0x1780b2ff9', + hash: '0x0e70849f10e22fe2e53fe6755f86a572aa6bb2fc472f0b87d9e561efa1fc2e1f', + input: '0x258d7af661a6463993e05a171f45d774cf37e761c83f402ab3277301b3574863a151d042dc870fb1b3f0c72cbbdd53a85898f62415fe124406f6608d8802269d1283cdb2a5a329649e5cb4cdcee91ab6', + // maxFeePerGas: '0x3ac1bf7ee', + // maxPriorityFeePerGas: '0x0', + nonce: '0x127b2', + r: '0x3c47223f880a3fb7b1eca368d9d7320d2278f0b679109d9ed0af4080ee386f23', + s: '0x587a441f9472b312ff302d7132547aa250ea06c6203c76831d56a46ec188e664', + to: '0x000000d40b595b94918a28b27d1e2c66f43a51d3', + transactionIndex: '0x0', + type: '0x1', + v: '0x1', + value: '0x31', + yParity: '0x1', + }, + { + accessList: [], + blockHash: '0xeb37ebc94e31773e5c5703073fd3911b2ab596f099d00d18b55ae3ac8203c1d5', + blockNumber: '0x136058d', + chainId: '0x1', + from: '0xe25d2cb47b606bb6fd9272125457a7230e26f956', + gas: '0x47bb0', + gasPrice: '0x1ba875cb6', + hash: '0xae5624c77f06d0164301380afa7780ebe49debe77eb3d5167004d69bd188a09f', + input: '0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006696237b00000000000000000000000000000000000000000000000000000000000000040b080604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000006d1aaedfab0f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000d84d4e8e1e8f268e027c29fa4d48c4b7e4d422990000000000000000000000000000000000000000000000000000000000000060000000000000000000000000d84d4e8e1e8f268e027c29fa4d48c4b7e4d42299000000000000000000000000000000fee13a103a10d593b9ae06b3e05f2e7e1c00000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000d84d4e8e1e8f268e027c29fa4d48c4b7e4d42299000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000006cd4db3c8c8d', + // maxFeePerGas: '0x23493c9cd', + // maxPriorityFeePerGas: '0x427c2cbd', + nonce: '0x32b', + r: '0x6566181b3cfd01702b24a2124ea7698b8cc815c7f37d1ea55800f176ca7a94cf', + s: '0x34f8dd837f37746ccd18f4fa71e05de98a2212f1c931f740598e491518616bb3', + to: '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad', + transactionIndex: '0x1', + type: '0x1', + v: '0x1', + value: '0xb1a2bc2ec50000', + yParity: '0x1', + }, + ], +}; diff --git a/mocks/blocks/epoch.ts b/mocks/blocks/epoch.ts new file mode 100644 index 0000000000..58f614fbe6 --- /dev/null +++ b/mocks/blocks/epoch.ts @@ -0,0 +1,57 @@ +import _padStart from 'lodash/padStart'; + +import type { BlockEpoch, BlockEpochElectionRewardDetails, BlockEpochElectionRewardDetailsResponse } from 'types/api/block'; + +import * as addressMock from '../address/address'; +import * as tokenMock from '../tokens/tokenInfo'; +import * as tokenTransferMock from '../tokens/tokenTransfer'; + +export const blockEpoch1: BlockEpoch = { + number: 1486, + distribution: { + carbon_offsetting_transfer: tokenTransferMock.erc20, + community_transfer: tokenTransferMock.erc20, + reserve_bolster_transfer: null, + }, + aggregated_election_rewards: { + delegated_payment: { + count: 0, + total: '71210001063118670575', + token: tokenMock.tokenInfoERC20d, + }, + group: { + count: 10, + total: '157705500305820107521', + token: tokenMock.tokenInfoERC20b, + }, + validator: { + count: 10, + total: '1348139501689262297152', + token: tokenMock.tokenInfoERC20c, + }, + voter: { + count: 38, + total: '2244419545166303388', + token: tokenMock.tokenInfoERC20a, + }, + }, +}; + +function getRewardDetailsItem(index: number): BlockEpochElectionRewardDetails { + return { + amount: `${ 100 - index }210001063118670575`, + account: { + ...addressMock.withoutName, + hash: `0x30D060F129817c4DE5fBc1366d53e19f43c8c6${ _padStart(String(index), 2, '0') }`, + }, + associated_account: { + ...addressMock.withoutName, + hash: `0x456f41406B32c45D59E539e4BBA3D7898c3584${ _padStart(String(index), 2, '0') }`, + }, + }; +} + +export const electionRewardDetails1: BlockEpochElectionRewardDetailsResponse = { + items: Array(15).fill('').map((item, index) => getRewardDetailsItem(index)), + next_page_params: null, +}; diff --git a/mocks/contract/info.ts b/mocks/contract/info.ts index ae11557f84..ab4ee68af8 100644 --- a/mocks/contract/info.ts +++ b/mocks/contract/info.ts @@ -24,7 +24,7 @@ export const verified: SmartContract = { verified_at: '2021-08-03T10:40:41.679421Z', decoded_constructor_args: [ [ '0xc59615da2da226613b1c78f0c6676cac497910bc', { internalType: 'address', name: '_token', type: 'address' } ], - [ '1800', { internalType: 'uint256', name: '_duration', type: 'uint256' } ], + [ [ 1800, 3600, 7200 ], { internalType: 'uint256[]', name: '_durations', type: 'uint256[]' } ], [ '900000000', { internalType: 'uint256', name: '_totalSupply', type: 'uint256' } ], ], external_libraries: [ @@ -43,7 +43,7 @@ export const verified: SmartContract = { file_path: '', additional_sources: [], verified_twin_address_hash: null, - minimal_proxy_address_hash: null, + proxy_type: null, }; export const certified: SmartContract = { @@ -85,7 +85,7 @@ export const withProxyAddress: SmartContract = { ...verified, is_verified: false, verified_twin_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8', - minimal_proxy_address_hash: '0xa62744bee8646e237441cdbfdedd3458861748a8', + proxy_type: 'eip1967', }; export const selfDestructed: SmartContract = { @@ -99,6 +99,13 @@ export const withChangedByteCode: SmartContract = { is_blueprint: true, }; +export const zkSync: SmartContract = { + ...verified, + zk_compiler_version: 'v1.2.5', + optimization_enabled: true, + optimization_runs: 's', +}; + export const nonVerified: SmartContract = { is_verified: false, is_blueprint: false, @@ -126,7 +133,7 @@ export const nonVerified: SmartContract = { additional_sources: [], external_libraries: null, verified_twin_address_hash: null, - minimal_proxy_address_hash: null, + proxy_type: null, language: null, license_type: null, }; diff --git a/mocks/contract/methods.ts b/mocks/contract/methods.ts index 492cdc7319..5fa714921c 100644 --- a/mocks/contract/methods.ts +++ b/mocks/contract/methods.ts @@ -129,6 +129,6 @@ export const write: Array = [ payable: false, stateMutability: 'nonpayable', type: 'function', - method_id: '0x06', + is_invalid: true, }, ]; diff --git a/mocks/contract/solidityscanReport.ts b/mocks/contract/solidityscanReport.ts index 41cbc785f2..e3d83d590d 100644 --- a/mocks/contract/solidityscanReport.ts +++ b/mocks/contract/solidityscanReport.ts @@ -1,6 +1,6 @@ -import type { SolidityscanReport } from 'types/api/contract'; +import type { SolidityScanReport } from 'lib/solidityScan/schema'; -export const solidityscanReportAverage: SolidityscanReport = { +export const solidityscanReportAverage: SolidityScanReport = { scan_report: { contractname: 'foo', scan_status: 'scan_done', @@ -13,17 +13,13 @@ export const solidityscanReportAverage: SolidityscanReport = { low: 2, medium: 0, }, - lines_analyzed_count: 18, - scan_time_taken: 1, - score: '3.61', score_v2: '72.22', - threat_score: '94.74', }, scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout', }, }; -export const solidityscanReportGreat: SolidityscanReport = { +export const solidityscanReportGreat: SolidityScanReport = { scan_report: { contractname: 'foo', scan_status: 'scan_done', @@ -36,17 +32,13 @@ export const solidityscanReportGreat: SolidityscanReport = { low: 0, medium: 0, }, - lines_analyzed_count: 18, - scan_time_taken: 1, - score: '3.61', score_v2: '100', - threat_score: '94.74', }, scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout', }, }; -export const solidityscanReportLow: SolidityscanReport = { +export const solidityscanReportLow: SolidityScanReport = { scan_report: { contractname: 'foo', scan_status: 'scan_done', @@ -59,11 +51,7 @@ export const solidityscanReportLow: SolidityscanReport = { low: 2, medium: 10, }, - lines_analyzed_count: 18, - scan_time_taken: 1, - score: '3.61', score_v2: '22.22', - threat_score: '94.74', }, scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout', }, diff --git a/mocks/contracts/index.ts b/mocks/contracts/index.ts index 86a0a0855c..e640c7a1df 100644 --- a/mocks/contracts/index.ts +++ b/mocks/contracts/index.ts @@ -18,7 +18,7 @@ export const contract1: VerifiedContract = { language: 'solidity', market_cap: null, optimization_enabled: false, - tx_count: 7334224, + transaction_count: 7334224, verified_at: '2022-09-16T18:49:29.605179Z', license_type: 'mit', }; @@ -42,7 +42,7 @@ export const contract2: VerifiedContract = { language: 'vyper', market_cap: null, optimization_enabled: true, - tx_count: 440, + transaction_count: 440, verified_at: '2021-09-07T20:01:56.076979Z', license_type: 'bsd_3_clause', }; diff --git a/mocks/ens/domain.ts b/mocks/ens/domain.ts index 552174ff26..6cf446b833 100644 --- a/mocks/ens/domain.ts +++ b/mocks/ens/domain.ts @@ -63,6 +63,14 @@ export const ensDomainA: bens.DetailedDomain = { NEAR: 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near', }, protocol: protocolA, + resolver_address: { + hash: '0xD578780f1dA7404d9CC0eEbC9D684c140CC4b638', + }, + resolved_with_wildcard: true, + stored_offchain: true, + wrapped_owner: { + hash: '0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401', + }, }; export const ensDomainB: bens.DetailedDomain = { @@ -81,6 +89,8 @@ export const ensDomainB: bens.DetailedDomain = { expiry_date: undefined, other_addresses: {}, protocol: undefined, + resolved_with_wildcard: false, + stored_offchain: false, }; export const ensDomainC: bens.DetailedDomain = { @@ -101,6 +111,8 @@ export const ensDomainC: bens.DetailedDomain = { expiry_date: '2022-11-01T13:10:36.000Z', other_addresses: {}, protocol: undefined, + resolved_with_wildcard: false, + stored_offchain: false, }; export const ensDomainD: bens.DetailedDomain = { @@ -119,4 +131,6 @@ export const ensDomainD: bens.DetailedDomain = { expiry_date: '2027-09-23T13:10:36.000Z', other_addresses: {}, protocol: undefined, + resolved_with_wildcard: false, + stored_offchain: false, }; diff --git a/mocks/l2deposits/deposits.ts b/mocks/l2deposits/deposits.ts deleted file mode 100644 index b94c2dc657..0000000000 --- a/mocks/l2deposits/deposits.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const data = { - items: [ - { - l1_block_number: 8382841, - l1_block_timestamp: '2022-05-27T01:13:48.000000Z', - l1_tx_hash: '0xaf3e5f4ef03eac22a622b3434c5dc9f4465aa291900a86bcf0ad9fb14429f05e', - l1_tx_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5', - l2_tx_gas_limit: '2156928', - l2_tx_hash: '0xb9212c76069b926917816767e4c5a0ef80e519b1ac1c3d3fb5818078f4984667', - }, - { - l1_block_number: 8382841, - l1_block_timestamp: '2022-05-27T01:13:48.000000Z', - l1_tx_hash: '0xa280f18cc72f9ad904087eb262c236048e935ad184a85bbd042d544c172c10bf', - l1_tx_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5', - l2_tx_gas_limit: '1216064', - l2_tx_hash: '0xaaaeb47a78b5c42d870f8d831a683a7cefe1b031a992170b28b43b82bd08318c', - }, - { - l1_block_number: 8382834, - l1_block_timestamp: '2022-06-27T01:11:48.000000Z', - l1_tx_hash: '0xfca8cc5440bffa8b975873c02bba3ff3380dd75fbc3260d10179e282cf72d6d4', - l1_tx_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5', - l2_tx_gas_limit: '405824', - l2_tx_hash: '0xa0604ebf2614ad708aeefa83f766fb25928dadb5ffb2f45028f5b4f1fa4d9358', - }, - ], - next_page_params: { - items_count: 50, - l1_block_number: 8382363, - tx_hash: '0x2012f0ce966ce6573e7826e9235f227edf5a2f8382b8d646c979f85a77e15c05', - }, -}; diff --git a/mocks/l2txnBatches/txnBatches.ts b/mocks/l2txnBatches/txnBatches.ts deleted file mode 100644 index f3c086c855..0000000000 --- a/mocks/l2txnBatches/txnBatches.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const txnBatchesData = { - items: [ - { - l1_tx_hashes: [ - '0x5bc94d02b65743dfaa9e10a2d6e175aff2a05cce2128c8eaf848bd84ab9325c5', - '0x92a51bc623111dbb91f243e3452e60fab6f090710357f9d9b75ac8a0f67dfd9d', - ], - l1_timestamp: '2023-02-24T10:16:12.000000Z', - l2_block_number: 5902836, - tx_count: 0, - }, - { - l1_tx_hashes: [ - '0xc45f846ee28ce9ba116ce2d378d3dd00b55d324b833b3ecd4241c919c572c4aa', - ], - l1_timestamp: '2023-02-24T10:16:00.000000Z', - l2_block_number: 5902835, - tx_count: 0, - }, - { - l1_tx_hashes: [ - '0x48139721f792d3a68c3781b4cf50e66e8fc7dbb38adff778e09066ea5be9adb8', - ], - l1_timestamp: '2023-02-24T10:16:00.000000Z', - l2_block_number: 5902834, - tx_count: 0, - }, - ], - next_page_params: { - block_number: 5902834, - items_count: 50, - }, -}; diff --git a/mocks/metadata/address.ts b/mocks/metadata/address.ts index f708fcb319..186711cc04 100644 --- a/mocks/metadata/address.ts +++ b/mocks/metadata/address.ts @@ -90,3 +90,26 @@ export const warpcastTag: AddressMetadataTagApi = { warpcastHandle: 'duckYduck', }, }; + +export const noteTag: AddressMetadataTagApi = { + slug: 'scam-tag', + name: 'Phish 🐟', + tagType: 'note', + ordinal: 100, + meta: { + alertBgColor: 'deeppink', + alertTextColor: 'white', + data: 'Warning! This is scam! See the report', + }, +}; + +export const noteTag2: AddressMetadataTagApi = { + slug: 'note0', + name: 'note_0', + tagType: 'note', + ordinal: 0, + meta: { + alertStatus: 'info', + data: 'The token MILF was launched on May 13, 2021. The maximum total supply of the token is 100 billion.', + }, +}; diff --git a/mocks/mud/mudTables.ts b/mocks/mud/mudTables.ts new file mode 100644 index 0000000000..b5f0186c91 --- /dev/null +++ b/mocks/mud/mudTables.ts @@ -0,0 +1,94 @@ +/* eslint-disable max-len */ +import type { AddressMudRecord, AddressMudRecords, AddressMudRecordsItem, AddressMudTables } from 'types/api/address'; +import type { MudWorldSchema, MudWorldTable } from 'types/api/mudWorlds'; + +export const table1: MudWorldTable = { + table_full_name: 'tb.store.Tables', + table_id: '0x746273746f72650000000000000000005461626c657300000000000000000000', + table_name: 'Tables', + table_namespace: 'store', + table_type: 'onchain', +}; + +export const table2: MudWorldTable = { + table_full_name: 'ot.world.FunctionSignatur', + table_id: '0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572', + table_name: 'FunctionSignatur', + table_namespace: 'world', + table_type: 'offchain', +}; + +export const schema1: MudWorldSchema = { + key_names: [ 'moduleAddress', 'argumentsHash' ], + key_types: [ 'address', 'bytes32' ], + value_names: [ 'fieldLayout', 'keySchema', 'valueSchema', 'abiEncodedKeyNames', 'abiEncodedFieldNames' ], + value_types: [ 'bytes32', 'bytes32', 'bytes32', 'bytes', 'bytes' ], +}; + +export const schema2: MudWorldSchema = { + key_names: [], + key_types: [], + value_names: [ 'value' ], + value_types: [ 'address' ], +}; + +export const mudTables: AddressMudTables = { + items: [ + { + table: table1, + schema: schema1, + }, + { + table: table2, + schema: schema2, + }, + ], + next_page_params: { + items_count: 50, + table_id: '1', + }, +}; + +const record: AddressMudRecordsItem = { + decoded: { + abiEncodedFieldNames: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000006706c617965720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000974696d657374616d700000000000000000000000000000000000000000000000', + abiEncodedKeyNames: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000026964000000000000000000000000000000000000000000000000000000000000', + goldCosts: [ '100000', '150000', '200000', '250000', '400000', '550000', '700000' ], + prototypeIds: [ + '0x53776f7264736d616e0000000000000000000000000000000000000000000000', + '0x50696b656d616e00000000000000000000000000000000000000000000000000', + '0x50696b656d616e00000000000000000000000000000000000000000000000000', + '0x4172636865720000000000000000000000000000000000000000000000000000', + '0x4b6e696768740000000000000000000000000000000000000000000000000000', + ], + keySchema: '0x002001001f000000000000000000000000000000000000000000000000000000', + tableId: '0x6f74000000000000000000000000000044726177557064617465000000000000', + valueSchema: '0x00540300611f1f00000000000000000000000000000000000000000000000000', + }, + id: '0x007a651a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007', + is_deleted: false, + timestamp: '2024-05-09T15:14:32.000000Z', +}; + +export const mudRecords: AddressMudRecords = { + items: [ record, record ], + next_page_params: { + items_count: 50, + key0: '1', + key1: '2', + key_bytes: '3', + }, + schema: { + key_names: [ 'tableId' ], + key_types: [ 'bytes32' ], + value_names: [ 'prototypeIds', 'goldCosts', 'keySchema', 'valueSchema', 'abiEncodedKeyNames', 'abiEncodedFieldNames' ], + value_types: [ 'bytes32[]', 'int32[]', 'bytes32', 'bytes32', 'bytes32', 'bytes', 'bytes' ], + }, + table: table1, +}; + +export const mudRecord: AddressMudRecord = { + record, + schema: mudRecords.schema, + table: table1, +}; diff --git a/mocks/mud/mudWorlds.ts b/mocks/mud/mudWorlds.ts new file mode 100644 index 0000000000..beb384a33c --- /dev/null +++ b/mocks/mud/mudWorlds.ts @@ -0,0 +1,27 @@ +import type { MudWorldsResponse } from 'types/api/mudWorlds'; + +import { withName, withoutName } from 'mocks/address/address'; + +export const mudWorlds: MudWorldsResponse = { + items: [ + { + address: withName, + coin_balance: '300000000000000000', + transaction_count: 3938, + }, + { + address: withoutName, + coin_balance: '0', + transaction_count: 0, + }, + { + address: withoutName, + coin_balance: '0', + transaction_count: 0, + }, + ], + next_page_params: { + items_count: 50, + world: '0x18f01f12ca21b6fc97b917c3e32f671f8a933caa', + }, +}; diff --git a/mocks/optimism/deposits.ts b/mocks/optimism/deposits.ts new file mode 100644 index 0000000000..e2f0ba1e9e --- /dev/null +++ b/mocks/optimism/deposits.ts @@ -0,0 +1,33 @@ +export const data = { + items: [ + { + l1_block_number: 8382841, + l1_block_timestamp: '2022-05-27T01:13:48.000000Z', + l1_transaction_hash: '0xaf3e5f4ef03eac22a622b3434c5dc9f4465aa291900a86bcf0ad9fb14429f05e', + l1_transaction_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5', + l2_transaction_gas_limit: '2156928', + l2_transaction_hash: '0xb9212c76069b926917816767e4c5a0ef80e519b1ac1c3d3fb5818078f4984667', + }, + { + l1_block_number: 8382841, + l1_block_timestamp: '2022-05-27T01:13:48.000000Z', + l1_transaction_hash: '0xa280f18cc72f9ad904087eb262c236048e935ad184a85bbd042d544c172c10bf', + l1_transaction_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5', + l2_transaction_gas_limit: '1216064', + l2_transaction_hash: '0xaaaeb47a78b5c42d870f8d831a683a7cefe1b031a992170b28b43b82bd08318c', + }, + { + l1_block_number: 8382834, + l1_block_timestamp: '2022-06-27T01:11:48.000000Z', + l1_transaction_hash: '0xfca8cc5440bffa8b975873c02bba3ff3380dd75fbc3260d10179e282cf72d6d4', + l1_transaction_origin: '0x6197d1eef304eb5284a0f6720f79403b4e9bf3a5', + l2_transaction_gas_limit: '405824', + l2_transaction_hash: '0xa0604ebf2614ad708aeefa83f766fb25928dadb5ffb2f45028f5b4f1fa4d9358', + }, + ], + next_page_params: { + items_count: 50, + l1_block_number: 8382363, + transaction_hash: '0x2012f0ce966ce6573e7826e9235f227edf5a2f8382b8d646c979f85a77e15c05', + }, +}; diff --git a/mocks/l2disputeGames/disputeGames.ts b/mocks/optimism/disputeGames.ts similarity index 100% rename from mocks/l2disputeGames/disputeGames.ts rename to mocks/optimism/disputeGames.ts diff --git a/mocks/l2outputRoots/outputRoots.ts b/mocks/optimism/outputRoots.ts similarity index 74% rename from mocks/l2outputRoots/outputRoots.ts rename to mocks/optimism/outputRoots.ts index 9f21572297..0be0426fee 100644 --- a/mocks/l2outputRoots/outputRoots.ts +++ b/mocks/optimism/outputRoots.ts @@ -3,7 +3,7 @@ export const outputRootsData = { { l1_block_number: 8456113, l1_timestamp: '2022-02-08T12:08:48.000000Z', - l1_tx_hash: '0x19455a53758d5de89070164ff09c40d93f1b4447e721090f03aa150f6159265a', + l1_transaction_hash: '0x19455a53758d5de89070164ff09c40d93f1b4447e721090f03aa150f6159265a', l2_block_number: 5214988, l2_output_index: 9926, output_root: '0xa7de9bd3986ce5ca8de9f0ab6c7473f4cebe225fb13b57cc5c8472de84a8bab3', @@ -11,7 +11,7 @@ export const outputRootsData = { { l1_block_number: 8456099, l1_timestamp: '2022-02-08T12:05:24.000000Z', - l1_tx_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b', + l1_transaction_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b', l2_block_number: 5214868, l2_output_index: 9925, output_root: '0x4ec2822d2f7b4f834d693d88f8a4cf15899882915980a21756d29cfd9f9f3898', @@ -19,7 +19,7 @@ export const outputRootsData = { { l1_block_number: 8456078, l1_timestamp: '2022-02-08T12:00:48.000000Z', - l1_tx_hash: '0x4238988b0959e41a7b09cef67f58698e05e3bcc29b8d2f60e6c77dc68c91f16e', + l1_transaction_hash: '0x4238988b0959e41a7b09cef67f58698e05e3bcc29b8d2f60e6c77dc68c91f16e', l2_block_number: 5214748, l2_output_index: 9924, output_root: '0x78b2e13c20f4bbfb4a008127edaaf25aa476f933669edd4856305bf4ab64a92b', diff --git a/mocks/optimism/txnBatches.ts b/mocks/optimism/txnBatches.ts new file mode 100644 index 0000000000..3344f6420d --- /dev/null +++ b/mocks/optimism/txnBatches.ts @@ -0,0 +1,104 @@ +import type { + OptimismL2TxnBatchTypeCallData, + OptimismL2TxnBatchTypeCelestia, + OptimismL2TxnBatchTypeEip4844, + OptimisticL2TxnBatchesResponse, +} from 'types/api/optimisticL2'; + +export const txnBatchesData: OptimisticL2TxnBatchesResponse = { + items: [ + { + batch_data_container: 'in_blob4844', + internal_id: 260998, + l1_timestamp: '2022-11-10T11:29:11.000000Z', + l1_transaction_hashes: [ + '0x9553351f6bd1577f4e782738c087be08697fb11f3b91745138d71ba166d62c3b', + ], + l2_block_end: 124882074, + l2_block_start: 124881833, + transaction_count: 4011, + }, + { + batch_data_container: 'in_calldata', + internal_id: 260997, + l1_timestamp: '2022-11-03T11:20:59.000000Z', + l1_transaction_hashes: [ + '0x80f5fba70d5685bc2b70df836942e892b24afa7bba289a2fac0ca8f4d554cc72', + ], + l2_block_end: 124881832, + l2_block_start: 124881613, + transaction_count: 4206, + }, + { + internal_id: 260996, + l1_timestamp: '2024-09-03T11:14:23.000000Z', + l1_transaction_hashes: [ + '0x39f4c46cae57bae936acb9159e367794f41f021ed3788adb80ad93830edb5f22', + ], + l2_block_end: 124881612, + l2_block_start: 124881380, + transaction_count: 4490, + }, + ], + next_page_params: { + id: 5902834, + items_count: 50, + }, +}; + +export const txnBatchTypeCallData: OptimismL2TxnBatchTypeCallData = { + batch_data_container: 'in_calldata', + internal_id: 309123, + l1_timestamp: '2022-08-10T10:30:24.000000Z', + l1_transaction_hashes: [ + '0x478c45f182631ae6f7249d40f31fdac36f41d88caa2e373fba35340a7345ca67', + ], + l2_block_end: 10146784, + l2_block_start: 10145379, + transaction_count: 1608, +}; + +export const txnBatchTypeCelestia: OptimismL2TxnBatchTypeCelestia = { + batch_data_container: 'in_celestia', + blobs: [ + { + commitment: '0x39c18c21c6b127d58809b8d3b5931472421f9b51532959442f53038f10b78f2a', + height: 2584868, + l1_timestamp: '2024-08-28T16:51:12.000000Z', + l1_transaction_hash: '0x2bb0b96a8ba0f063a243ac3dee0b2f2d87edb2ba9ef44bfcbc8ed191af1c4c24', + namespace: '0x00000000000000000000000000000000000000000008e5f679bf7116cb', + }, + ], + internal_id: 309667, + l1_timestamp: '2022-08-28T16:51:12.000000Z', + l1_transaction_hashes: [ + '0x2bb0b96a8ba0f063a243ac3dee0b2f2d87edb2ba9ef44bfcbc8ed191af1c4c24', + ], + l2_block_end: 10935879, + l2_block_start: 10934514, + transaction_count: 1574, +}; + +export const txnBatchTypeEip4844: OptimismL2TxnBatchTypeEip4844 = { + batch_data_container: 'in_blob4844', + blobs: [ + { + hash: '0x012a4f0c6db6bce9d3d357b2bf847764320bcb0107ab318f3a532f637bc60dfe', + l1_timestamp: '2022-08-23T03:59:12.000000Z', + l1_transaction_hash: '0x3870f136497e5501dc20d0974daf379c8636c958794d59a9c90d4f8a9f0ed20a', + }, + { + hash: '0x01d1097cce23229931afbc2fd1cf0d707da26df7b39cef1c542276ae718de4f6', + l1_timestamp: '2022-08-23T03:59:12.000000Z', + l1_transaction_hash: '0x3870f136497e5501dc20d0974daf379c8636c958794d59a9c90d4f8a9f0ed20a', + }, + ], + internal_id: 2538459, + l1_timestamp: '2022-08-23T03:59:12.000000Z', + l1_transaction_hashes: [ + '0x3870f136497e5501dc20d0974daf379c8636c958794d59a9c90d4f8a9f0ed20a', + ], + l2_block_end: 16291502, + l2_block_start: 16291373, + transaction_count: 704, +}; diff --git a/mocks/l2withdrawals/withdrawals.ts b/mocks/optimism/withdrawals.ts similarity index 71% rename from mocks/l2withdrawals/withdrawals.ts rename to mocks/optimism/withdrawals.ts index 046b467402..b9d9588cdc 100644 --- a/mocks/l2withdrawals/withdrawals.ts +++ b/mocks/optimism/withdrawals.ts @@ -15,9 +15,9 @@ export const data: OptimisticL2WithdrawalsResponse = { watchlist_names: [], ens_domain_name: null, }, - l1_tx_hash: '0x1a235bee32ac10cb7efdad98415737484ca66386e491cde9e17d42b136dca684', + l1_transaction_hash: '0x1a235bee32ac10cb7efdad98415737484ca66386e491cde9e17d42b136dca684', l2_timestamp: '2022-02-15T12:50:02.000000Z', - l2_tx_hash: '0x918cd8c5c24c17e06cd02b0379510c4ad56324bf153578fb9caaaa2fe4e7dc35', + l2_transaction_hash: '0x918cd8c5c24c17e06cd02b0379510c4ad56324bf153578fb9caaaa2fe4e7dc35', msg_nonce: 396, msg_nonce_version: 1, status: 'Ready to prove', @@ -25,9 +25,9 @@ export const data: OptimisticL2WithdrawalsResponse = { { challenge_period_end: null, from: null, - l1_tx_hash: null, + l1_transaction_hash: null, l2_timestamp: null, - l2_tx_hash: '0x2f117bee32ac10cb7efdad98415737484ca66386e491cde9e17d42b136def593', + l2_transaction_hash: '0x2f117bee32ac10cb7efdad98415737484ca66386e491cde9e17d42b136def593', msg_nonce: 391, msg_nonce_version: 1, status: 'Ready to prove', @@ -35,9 +35,9 @@ export const data: OptimisticL2WithdrawalsResponse = { { challenge_period_end: '2022-11-11T12:50:02.000000Z', from: null, - l1_tx_hash: null, + l1_transaction_hash: null, l2_timestamp: null, - l2_tx_hash: '0xe14b1f46838176702244a5343629bcecf728ca2d9881d47b4ce46e00c387d7e3', + l2_transaction_hash: '0xe14b1f46838176702244a5343629bcecf728ca2d9881d47b4ce46e00c387d7e3', msg_nonce: 390, msg_nonce_version: 1, status: 'Ready for relay', diff --git a/mocks/rewards/balance.ts b/mocks/rewards/balance.ts new file mode 100644 index 0000000000..8c78eab127 --- /dev/null +++ b/mocks/rewards/balance.ts @@ -0,0 +1,10 @@ +import type { RewardsUserBalancesResponse } from 'types/api/rewards'; + +export const base: RewardsUserBalancesResponse = { + total: '250', + staked: '0', + unstaked: '0', + total_staking_rewards: '0', + total_referral_rewards: '0', + pending_referral_rewards: '0', +}; diff --git a/mocks/rewards/dailyReward.ts b/mocks/rewards/dailyReward.ts new file mode 100644 index 0000000000..c60377e43e --- /dev/null +++ b/mocks/rewards/dailyReward.ts @@ -0,0 +1,9 @@ +import type { RewardsUserDailyCheckResponse } from 'types/api/rewards'; + +export const base: RewardsUserDailyCheckResponse = { + available: true, + daily_reward: '10', + pending_referral_rewards: '0', + date: '', + reset_at: '', +}; diff --git a/mocks/rewards/referrals.ts b/mocks/rewards/referrals.ts new file mode 100644 index 0000000000..bac2b26042 --- /dev/null +++ b/mocks/rewards/referrals.ts @@ -0,0 +1,7 @@ +import type { RewardsUserReferralsResponse } from 'types/api/rewards'; + +export const base: RewardsUserReferralsResponse = { + code: 'QWERTY', + link: 'https://example.com?ref=QWERTY', + referrals: '15', +}; diff --git a/mocks/rewards/rewardsConfig.ts b/mocks/rewards/rewardsConfig.ts new file mode 100644 index 0000000000..25ae8b7277 --- /dev/null +++ b/mocks/rewards/rewardsConfig.ts @@ -0,0 +1,10 @@ +import type { RewardsConfigResponse } from 'types/api/rewards'; + +export const base: RewardsConfigResponse = { + rewards: { + registration: '100', + registration_with_referral: '200', + daily_claim: '10', + referral_share: '0.1', + }, +}; diff --git a/mocks/search/index.ts b/mocks/search/index.ts index 34015b5157..c0510f0225 100644 --- a/mocks/search/index.ts +++ b/mocks/search/index.ts @@ -114,7 +114,7 @@ export const label1: SearchResultLabel = { }; export const tx1: SearchResultTx = { - tx_hash: '0x349d4025d03c6faec117ee10ac0bce7c7a805dd2cbff7a9f101304d9a8a525dd', + transaction_hash: '0x349d4025d03c6faec117ee10ac0bce7c7a805dd2cbff7a9f101304d9a8a525dd', type: 'transaction' as const, timestamp: '2022-12-11T17:55:20Z', url: '/tx/0x349d4025d03c6faec117ee10ac0bce7c7a805dd2cbff7a9f101304d9a8a525dd', diff --git a/mocks/stats/daily_txs.ts b/mocks/stats/daily_txs.ts index afb2dcca58..76653e9240 100644 --- a/mocks/stats/daily_txs.ts +++ b/mocks/stats/daily_txs.ts @@ -2,148 +2,148 @@ export const base = { chart_data: [ { date: '2022-11-28', - tx_count: 26815, + transaction_count: 26815, }, { date: '2022-11-27', - tx_count: 34784, + transaction_count: 34784, }, { date: '2022-11-26', - tx_count: 77527, + transaction_count: 77527, }, { date: '2022-11-25', - tx_count: 39687, + transaction_count: 39687, }, { date: '2022-11-24', - tx_count: 40752, + transaction_count: 40752, }, { date: '2022-11-23', - tx_count: 32569, + transaction_count: 32569, }, { date: '2022-11-22', - tx_count: 34449, + transaction_count: 34449, }, { date: '2022-11-21', - tx_count: 106047, + transaction_count: 106047, }, { date: '2022-11-20', - tx_count: 107713, + transaction_count: 107713, }, { date: '2022-11-19', - tx_count: 96311, + transaction_count: 96311, }, { date: '2022-11-18', - tx_count: 30828, + transaction_count: 30828, }, { date: '2022-11-17', - tx_count: 27422, + transaction_count: 27422, }, { date: '2022-11-16', - tx_count: 75898, + transaction_count: 75898, }, { date: '2022-11-15', - tx_count: 84084, + transaction_count: 84084, }, { date: '2022-11-14', - tx_count: 62266, + transaction_count: 62266, }, { date: '2022-11-13', - tx_count: 22338, + transaction_count: 22338, }, { date: '2022-11-12', - tx_count: 86764, + transaction_count: 86764, }, { date: '2022-11-11', - tx_count: 79493, + transaction_count: 79493, }, { date: '2022-11-10', - tx_count: 92887, + transaction_count: 92887, }, { date: '2022-11-09', - tx_count: 43691, + transaction_count: 43691, }, { date: '2022-11-08', - tx_count: 74197, + transaction_count: 74197, }, { date: '2022-11-07', - tx_count: 58131, + transaction_count: 58131, }, { date: '2022-11-06', - tx_count: 62477, + transaction_count: 62477, }, { date: '2022-11-05', - tx_count: 82897, + transaction_count: 82897, }, { date: '2022-11-04', - tx_count: 91725, + transaction_count: 91725, }, { date: '2022-11-03', - tx_count: 83667, + transaction_count: 83667, }, { date: '2022-11-02', - tx_count: 63743, + transaction_count: 63743, }, { date: '2022-11-01', - tx_count: 152059, + transaction_count: 152059, }, { date: '2022-10-31', - tx_count: 62519, + transaction_count: 62519, }, { date: '2022-10-30', - tx_count: 48569, + transaction_count: 48569, }, { date: '2022-10-29', - tx_count: 36789, + transaction_count: 36789, }, ], }; export const partialData = { chart_data: [ - { date: '2022-11-28', tx_count: 26815 }, - { date: '2022-11-27', tx_count: 34784 }, - { date: '2022-11-26', tx_count: 77527 }, - { date: '2022-11-25', tx_count: null }, - { date: '2022-11-24', tx_count: null }, - { date: '2022-11-23', tx_count: null }, - { date: '2022-11-22', tx_count: 63433 }, - { date: '2022-11-21', tx_count: null }, + { date: '2022-11-28', transaction_count: 26815 }, + { date: '2022-11-27', transaction_count: 34784 }, + { date: '2022-11-26', transaction_count: 77527 }, + { date: '2022-11-25', transaction_count: null }, + { date: '2022-11-24', transaction_count: null }, + { date: '2022-11-23', transaction_count: null }, + { date: '2022-11-22', transaction_count: 63433 }, + { date: '2022-11-21', transaction_count: null }, ], }; export const noData = { chart_data: [ - { date: '2022-11-25', tx_count: null }, - { date: '2022-11-24', tx_count: null }, - { date: '2022-11-23', tx_count: null }, + { date: '2022-11-25', transaction_count: null }, + { date: '2022-11-24', transaction_count: null }, + { date: '2022-11-23', transaction_count: null }, ], }; diff --git a/mocks/stats/index.ts b/mocks/stats/index.ts index e6fecc8d98..f4bc3f53e3 100644 --- a/mocks/stats/index.ts +++ b/mocks/stats/index.ts @@ -64,9 +64,15 @@ export const withoutBothPrices: HomeStats = { gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null, fiat_price: null }) : null), }; +export const withoutGasInfo: HomeStats = { + ...base, + gas_prices: null, +}; + export const withSecondaryCoin: HomeStats = { ...base, secondary_coin_price: '3.398', + secondary_coin_image: 'http://localhost:3100/secondary_utia.jpg', }; export const noChartData: HomeStats = { diff --git a/mocks/stats/line.ts b/mocks/stats/line.ts index 799cff4c45..0c10eed840 100644 --- a/mocks/stats/line.ts +++ b/mocks/stats/line.ts @@ -4,158 +4,195 @@ export const averageGasPrice: stats.LineChart = { chart: [ { date: '2023-12-22', + date_to: '2023-12-22', value: '37.7804422597599', is_approximate: false, }, { date: '2023-12-23', + date_to: '2023-12-23', value: '25.84889883009387', is_approximate: false, }, { date: '2023-12-24', + date_to: '2023-12-24', value: '25.818463227198574', is_approximate: false, }, { date: '2023-12-25', + date_to: '2023-12-25', value: '26.045513050051298', is_approximate: false, }, { date: '2023-12-26', + date_to: '2023-12-26', value: '21.42600692652399', is_approximate: false, }, { date: '2023-12-27', + date_to: '2023-12-27', value: '31.066730409846656', is_approximate: false, }, { date: '2023-12-28', + date_to: '2023-12-28', value: '33.63955781902089', is_approximate: false, }, { date: '2023-12-29', + date_to: '2023-12-29', value: '28.064736756058384', is_approximate: false, }, { date: '2023-12-30', + date_to: '2023-12-30', value: '23.074500869678175', is_approximate: false, }, { date: '2023-12-31', + date_to: '2023-12-31', value: '17.651005734615133', is_approximate: false, }, { date: '2024-01-01', + date_to: '2023-01-01', value: '14.906085174476441', is_approximate: false, }, { date: '2024-01-02', + date_to: '2023-01-02', value: '22.28459059038656', is_approximate: false, }, { date: '2024-01-03', + date_to: '2023-01-03', value: '39.8311646806592', is_approximate: false, }, { date: '2024-01-04', + date_to: '2023-01-04', value: '26.09989322256083', is_approximate: false, }, { date: '2024-01-05', + date_to: '2023-01-05', value: '22.821996688111998', is_approximate: false, }, { date: '2024-01-06', + date_to: '2023-01-06', value: '20.32680041262083', is_approximate: false, }, { date: '2024-01-07', + date_to: '2023-01-07', value: '32.535045831809704', is_approximate: false, }, { date: '2024-01-08', + date_to: '2023-01-08', value: '27.443477102139482', is_approximate: false, }, { date: '2024-01-09', + date_to: '2023-01-09', value: '20.7911332558055', is_approximate: false, }, { date: '2024-01-10', + date_to: '2023-01-10', value: '42.10740192523919', is_approximate: false, }, { date: '2024-01-11', + date_to: '2023-01-11', value: '35.75215680343582', is_approximate: false, }, { date: '2024-01-12', + date_to: '2023-01-12', value: '27.430414798093253', is_approximate: false, }, { date: '2024-01-13', + date_to: '2023-01-13', value: '20.170934096589875', is_approximate: false, }, { date: '2024-01-14', + date_to: '2023-01-14', value: '38.79660984371034', is_approximate: false, }, { date: '2024-01-15', + date_to: '2023-01-15', value: '26.140740484554204', is_approximate: false, }, { date: '2024-01-16', + date_to: '2023-01-16', value: '36.708543184194156', is_approximate: false, }, { date: '2024-01-17', + date_to: '2023-01-17', value: '40.325438794298876', is_approximate: false, }, { date: '2024-01-18', + date_to: '2023-01-18', value: '37.55145309930694', is_approximate: false, }, { date: '2024-01-19', + date_to: '2023-01-19', value: '33.271450114434664', is_approximate: false, }, { date: '2024-01-20', + date_to: '2023-01-20', value: '19.303304377685638', is_approximate: false, }, { date: '2024-01-21', + date_to: '2023-01-21', value: '14.375908594704976', is_approximate: false, }, ], + info: { + title: 'Chart title', + description: 'Chart description', + id: 'chart', + resolutions: [ 'DAY', 'MONTH' ], + }, }; diff --git a/mocks/stats/lines.ts b/mocks/stats/lines.ts index 9f8870249f..9dfccb0fdf 100644 --- a/mocks/stats/lines.ts +++ b/mocks/stats/lines.ts @@ -11,18 +11,21 @@ export const base: stats.LineCharts = { title: 'Accounts growth', description: 'Cumulative accounts number per period', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'activeAccounts', title: 'Active accounts', description: 'Active accounts number per period', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'newAccounts', title: 'New accounts', description: 'New accounts number per day', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, ], }, @@ -35,30 +38,35 @@ export const base: stats.LineCharts = { title: 'Average transaction fee', description: 'The average amount in ETH spent per transaction', units: 'ETH', + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'newTxns', title: 'New transactions', description: 'New transactions number', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'txnsFee', title: 'Transactions fees', description: 'Amount of tokens paid as fees', units: 'ETH', + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'txnsGrowth', title: 'Transactions growth', description: 'Cumulative transactions number', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'txnsSuccessRate', title: 'Transactions success rate', description: 'Successful transactions rate per day', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, ], }, @@ -71,18 +79,21 @@ export const base: stats.LineCharts = { title: 'Average block rewards', description: 'Average amount of distributed reward in tokens per day', units: 'ETH', + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'averageBlockSize', title: 'Average block size', description: 'Average size of blocks in bytes', units: 'Bytes', + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'newBlocks', title: 'New blocks', description: 'New blocks number', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, ], }, @@ -95,6 +106,7 @@ export const base: stats.LineCharts = { title: 'New ETH transfers', description: 'New token transfers number for the period', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, ], }, @@ -107,18 +119,21 @@ export const base: stats.LineCharts = { title: 'Average gas limit', description: 'Average gas limit per block for the period', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'averageGasPrice', title: 'Average gas price', description: 'Average gas price for the period (Gwei)', units: 'Gwei', + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'gasUsedGrowth', title: 'Gas used growth', description: 'Cumulative gas used for the period', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, ], }, @@ -131,12 +146,14 @@ export const base: stats.LineCharts = { title: 'New verified contracts', description: 'New verified contracts number for the period', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, { id: 'verifiedContractsGrowth', title: 'Verified contracts growth', description: 'Cumulative number verified contracts for the period', units: undefined, + resolutions: [ 'DAY', 'MONTH' ], }, ], }, diff --git a/mocks/tokens/tokenTransfer.ts b/mocks/tokens/tokenTransfer.ts index b88cd499ff..f17da56ec4 100644 --- a/mocks/tokens/tokenTransfer.ts +++ b/mocks/tokens/tokenTransfer.ts @@ -1,3 +1,4 @@ +import type { TokenInfo } from 'types/api/token'; import type { TokenTransfer, TokenTransferResponse } from 'types/api/tokenTransfer'; export const erc20: TokenTransfer = { @@ -39,9 +40,10 @@ export const erc20: TokenTransfer = { decimals: '18', value: '31567373703130350', }, - tx_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193', + transaction_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193', type: 'token_transfer', timestamp: '2022-10-10T14:34:30.000000Z', + block_number: '12345', block_hash: '1', log_index: '1', method: 'updateSmartAsset', @@ -85,9 +87,10 @@ export const erc721: TokenTransfer = { total: { token_id: '875879856', }, - tx_hash: '0xf13bc7afe5e02b494dd2f22078381d36a4800ef94a0ccc147431db56c301e6cc', + transaction_hash: '0xf13bc7afe5e02b494dd2f22078381d36a4800ef94a0ccc147431db56c301e6cc', type: 'token_transfer', timestamp: '2022-10-10T14:34:30.000000Z', + block_number: '12345', block_hash: '1', log_index: '1', method: 'updateSmartAsset', @@ -133,9 +136,10 @@ export const erc1155A: TokenTransfer = { value: '42', decimals: null, }, - tx_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746', + transaction_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746', type: 'token_minting', timestamp: '2022-10-10T14:34:30.000000Z', + block_number: '12345', block_hash: '1', log_index: '1', }; @@ -143,7 +147,7 @@ export const erc1155A: TokenTransfer = { export const erc1155B: TokenTransfer = { ...erc1155A, token: { - ...erc1155A.token, + ...(erc1155A.token as TokenInfo<'ERC-1155'>), name: 'SastanaNFT', symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY', }, @@ -153,7 +157,7 @@ export const erc1155B: TokenTransfer = { export const erc1155C: TokenTransfer = { ...erc1155A, token: { - ...erc1155A.token, + ...(erc1155A.token as TokenInfo<'ERC-1155'>), name: 'SastanaNFT', symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY', }, @@ -163,7 +167,7 @@ export const erc1155C: TokenTransfer = { export const erc1155D: TokenTransfer = { ...erc1155A, token: { - ...erc1155A.token, + ...(erc1155A.token as TokenInfo<'ERC-1155'>), name: 'SastanaNFT', symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY', }, @@ -210,10 +214,11 @@ export const erc404A: TokenTransfer = { decimals: '18', token_id: null, }, - tx_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746', + transaction_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746', type: 'token_transfer', method: 'swap', timestamp: '2022-10-10T14:34:30.000000Z', + block_number: '12345', block_hash: '1', log_index: '1', }; @@ -221,7 +226,7 @@ export const erc404A: TokenTransfer = { export const erc404B: TokenTransfer = { ...erc404A, token: { - ...erc404A.token, + ...(erc404A.token as TokenInfo<'ERC-404'>), name: 'SastanaNFT', symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY', }, diff --git a/mocks/txs/tx.ts b/mocks/txs/tx.ts index 595af11b47..f8a302eced 100644 --- a/mocks/txs/tx.ts +++ b/mocks/txs/tx.ts @@ -1,6 +1,7 @@ /* eslint-disable max-len */ import type { Transaction } from 'types/api/transaction'; +import * as addressMock from 'mocks/address/address'; import { publicTag, privateTag, watchlistName } from 'mocks/address/tag'; import * as tokenTransferMock from 'mocks/tokens/tokenTransfer'; import * as decodedInputDataMock from 'mocks/txs/decodedInputData'; @@ -47,7 +48,7 @@ export const base: Transaction = { status: 'ok', timestamp: '2022-10-10T14:34:30.000000Z', to: { - hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', + hash: addressMock.hash, implementations: null, is_contract: false, is_verified: true, @@ -110,7 +111,7 @@ export const withTokenTransfer: Transaction = { ...base, hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3196', to: { - hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', + hash: addressMock.hash, implementations: null, is_contract: true, is_verified: true, @@ -166,7 +167,7 @@ export const withRawRevertReason: Transaction = { raw: '4f6e6c79206368616972706572736f6e2063616e206769766520726967687420746f20766f74652e', }, to: { - hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', + hash: addressMock.hash, implementations: null, is_verified: true, is_contract: true, @@ -339,12 +340,39 @@ export const celoTxn: Transaction = { }, }; +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: null, + message_status: 'Relayed', + }, + network_fee: '1283130000000', + poster_fee: '1297730000000', + status: 'Sent to base', + }, +}; + export const base2 = { ...base, hash: '0x02d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193', from: { ...base.from, - hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', + hash: addressMock.hash, }, }; @@ -353,7 +381,7 @@ export const base3 = { hash: '0x12d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193', from: { ...base.from, - hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', + hash: addressMock.hash, }, }; @@ -375,3 +403,23 @@ export const withBlob = { tx_types: [ 'blob_transaction' as const ], type: 3, }; + +export const withRecipientName = { + ...base, + to: addressMock.withName, +}; + +export const withRecipientEns = { + ...base, + to: addressMock.withEns, +}; + +export const withRecipientNameTag = { + ...withRecipientEns, + to: addressMock.withNameTag, +}; + +export const withRecipientContract = { + ...withRecipientEns, + to: addressMock.contract, +}; diff --git a/mocks/txs/txInterpretation.ts b/mocks/txs/txInterpretation.ts index 061164088d..764d697a6b 100644 --- a/mocks/txs/txInterpretation.ts +++ b/mocks/txs/txInterpretation.ts @@ -1,5 +1,7 @@ import type { TxInterpretationResponse } from 'types/api/txInterpretation'; +import { hash } from 'mocks/address/address'; + export const txInterpretation: TxInterpretationResponse = { data: { summaries: [ { @@ -25,7 +27,7 @@ export const txInterpretation: TxInterpretationResponse = { to_address: { type: 'address', value: { - hash: '0x48c04ed5691981C42154C6167398f95e8f38a7fF', + hash: hash, implementations: null, is_contract: false, is_verified: false, diff --git a/mocks/user/profile.ts b/mocks/user/profile.ts index 955f872e01..7794447c25 100644 --- a/mocks/user/profile.ts +++ b/mocks/user/profile.ts @@ -1,13 +1,25 @@ -export const base = { +import type { UserInfo } from 'types/api/account'; + +export const base: UserInfo = { avatar: 'https://avatars.githubusercontent.com/u/22130104', email: 'tom@ohhhh.me', name: 'tom goriunov', nickname: 'tom2drum', + address_hash: null, }; -export const withoutEmail = { +export const withoutEmail: UserInfo = { avatar: 'https://avatars.githubusercontent.com/u/22130104', email: null, name: 'tom goriunov', nickname: 'tom2drum', + address_hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', +}; + +export const withEmailAndWallet: UserInfo = { + avatar: 'https://avatars.githubusercontent.com/u/22130104', + email: 'tom@ohhhh.me', + name: 'tom goriunov', + nickname: 'tom2drum', + address_hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', }; diff --git a/mocks/userOps/userOp.ts b/mocks/userOps/userOp.ts index e7368c20d5..d1508aad6c 100644 --- a/mocks/userOps/userOp.ts +++ b/mocks/userOps/userOp.ts @@ -41,7 +41,7 @@ export const userOpData: UserOp = { }, sponsor_type: 'paymaster_sponsor', raw: { - // eslint-disable-next-line max-len + call_data: '0xb61d27f600000000000000000000000059f6aa952df7f048fd076e33e0ea8bb552d5ffd8000000000000000000000000000000000000000000000000003f3d017500800000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000', call_gas_limit: '26624', init_code: '0x', diff --git a/mocks/validators/blackfort.ts b/mocks/validators/blackfort.ts new file mode 100644 index 0000000000..6a5232a321 --- /dev/null +++ b/mocks/validators/blackfort.ts @@ -0,0 +1,41 @@ +import type { + ValidatorBlackfort, + ValidatorsBlackfortCountersResponse, + ValidatorsBlackfortResponse, +} from 'types/api/validators'; + +import * as addressMock from '../address/address'; + +export const validator1: ValidatorBlackfort = { + address: addressMock.withName, + name: 'testnet-3', + commission: 10, + delegated_amount: '0', + self_bonded_amount: '10000', +}; + +export const validator2: ValidatorBlackfort = { + address: addressMock.withEns, + name: 'GooseGanG GooseGanG GooseGanG GooseGanG GooseGanG GooseGanG GooseGanG', + commission: 5000, + delegated_amount: '10000', + self_bonded_amount: '100', +}; + +export const validator3: ValidatorBlackfort = { + address: addressMock.withoutName, + name: 'testnet-1', + commission: 0, + delegated_amount: '0', + self_bonded_amount: '10000', +}; + +export const validatorsResponse: ValidatorsBlackfortResponse = { + items: [ validator1, validator2, validator3 ], + next_page_params: null, +}; + +export const validatorsCountersResponse: ValidatorsBlackfortCountersResponse = { + new_validators_counter_24h: '11', + validators_counter: '140', +}; diff --git a/mocks/validators/index.ts b/mocks/validators/stability.ts similarity index 56% rename from mocks/validators/index.ts rename to mocks/validators/stability.ts index 22081cae8c..a85f596674 100644 --- a/mocks/validators/index.ts +++ b/mocks/validators/stability.ts @@ -1,31 +1,35 @@ -import type { Validator, ValidatorsCountersResponse, ValidatorsResponse } from 'types/api/validators'; +import type { + ValidatorStability, + ValidatorsStabilityCountersResponse, + ValidatorsStabilityResponse, +} from 'types/api/validators'; import * as addressMock from '../address/address'; -export const validator1: Validator = { +export const validator1: ValidatorStability = { address: addressMock.withName, blocks_validated_count: 7334224, state: 'active', }; -export const validator2: Validator = { +export const validator2: ValidatorStability = { address: addressMock.withEns, blocks_validated_count: 8937453, state: 'probation', }; -export const validator3: Validator = { +export const validator3: ValidatorStability = { address: addressMock.withoutName, blocks_validated_count: 1234, state: 'inactive', }; -export const validatorsResponse: ValidatorsResponse = { +export const validatorsResponse: ValidatorsStabilityResponse = { items: [ validator1, validator2, validator3 ], next_page_params: null, }; -export const validatorsCountersResponse: ValidatorsCountersResponse = { +export const validatorsCountersResponse: ValidatorsStabilityCountersResponse = { active_validators_counter: '42', active_validators_percentage: 7.14, new_validators_counter_24h: '11', diff --git a/mocks/zkEvm/txnBatches.ts b/mocks/zkEvm/txnBatches.ts index bcaf55d941..194148c16b 100644 --- a/mocks/zkEvm/txnBatches.ts +++ b/mocks/zkEvm/txnBatches.ts @@ -4,14 +4,14 @@ export const txnBatchData: ZkEvmL2TxnBatch = { acc_input_hash: '0x4bf88aabe33713b7817266d7860912c58272d808da7397cdc627ca53b296fad3', global_exit_root: '0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5', number: 5, - sequence_tx_hash: '0x7ae010e9758441b302db10282807358af460f38c49c618d26a897592f64977f7', + sequence_transaction_hash: '0x7ae010e9758441b302db10282807358af460f38c49c618d26a897592f64977f7', state_root: '0x183b4a38a4a6027947ceb93b323cc94c548c8c05cf605d73ca88351d77cae1a3', status: 'Finalized', timestamp: '2023-10-20T10:08:18.000000Z', transactions: [ '0xb5d432c270057c223b973f3b5f00dbad32823d9ef26f3e8d97c819c7c573453a', ], - verify_tx_hash: '0x6f7eeaa0eb966e63d127bba6bf8f9046d303c2a1185b542f0b5083f682a0e87f', + verify_transaction_hash: '0x6f7eeaa0eb966e63d127bba6bf8f9046d303c2a1185b542f0b5083f682a0e87f', }; export const txnBatchesData: ZkEvmL2TxnBatchesResponse = { @@ -19,18 +19,18 @@ export const txnBatchesData: ZkEvmL2TxnBatchesResponse = { { timestamp: '2023-06-01T14:46:48.000000Z', status: 'Finalized', - verify_tx_hash: '0x48139721f792d3a68c3781b4cf50e66e8fc7dbb38adff778e09066ea5be9adb8', - sequence_tx_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b', + verify_transaction_hash: '0x48139721f792d3a68c3781b4cf50e66e8fc7dbb38adff778e09066ea5be9adb8', + sequence_transaction_hash: '0x6aa081e8e33a085e4ec7124fcd8a5f7d36aac0828f176e80d4b70e313a11695b', number: 5218590, - tx_count: 9, + transaction_count: 9, }, { timestamp: '2023-06-01T14:46:48.000000Z', status: 'Unfinalized', - verify_tx_hash: null, - sequence_tx_hash: null, + verify_transaction_hash: null, + sequence_transaction_hash: null, number: 5218591, - tx_count: 9, + transaction_count: 9, }, ], next_page_params: { diff --git a/mocks/zkSync/zkSyncTxnBatch.ts b/mocks/zkSync/zkSyncTxnBatch.ts index ab865dc513..79c5e28b39 100644 --- a/mocks/zkSync/zkSyncTxnBatch.ts +++ b/mocks/zkSync/zkSyncTxnBatch.ts @@ -7,9 +7,9 @@ export const base: ZkSyncBatch = { execute_transaction_hash: '0x110b9a19afbabd5818a996ab2b493a9b23c888d73d95f1ab5272dbae503e103a', execute_transaction_timestamp: '2022-03-19T10:29:05.358066Z', l1_gas_price: '4173068062', - l1_tx_count: 0, + l1_transaction_count: 0, l2_fair_gas_price: '100000000', - l2_tx_count: 287, + l2_transaction_count: 287, number: 8051, prove_transaction_hash: '0xb424162ba5afe17c710dceb5fc8d15d7d46a66223454dae8c74aa39f6802625b', prove_transaction_timestamp: '2022-03-19T10:29:05.279179Z', diff --git a/mocks/zkSync/zkSyncTxnBatches.ts b/mocks/zkSync/zkSyncTxnBatches.ts index a717308641..9bd3188eca 100644 --- a/mocks/zkSync/zkSyncTxnBatches.ts +++ b/mocks/zkSync/zkSyncTxnBatches.ts @@ -10,7 +10,7 @@ export const sealed: ZkSyncBatchesItem = { prove_transaction_timestamp: null, status: 'Sealed on L2', timestamp: '2022-03-19T12:53:36.000000Z', - tx_count: 738, + transaction_count: 738, }; export const sent: ZkSyncBatchesItem = { @@ -23,7 +23,7 @@ export const sent: ZkSyncBatchesItem = { prove_transaction_timestamp: null, status: 'Sent to L1', timestamp: '2022-03-19T11:36:45.000000Z', - tx_count: 766, + transaction_count: 766, }; export const executed: ZkSyncBatchesItem = { @@ -36,7 +36,7 @@ export const executed: ZkSyncBatchesItem = { prove_transaction_timestamp: '2022-03-19T13:28:16.603104Z', status: 'Executed on L1', timestamp: '2022-03-19T10:01:52.000000Z', - tx_count: 1071, + transaction_count: 1071, }; export const baseResponse: ZkSyncBatchesResponse = { diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03dc6..a4a7b3f5cf 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/next.config.js b/next.config.js index f1c2e19694..e78614249b 100644 --- a/next.config.js +++ b/next.config.js @@ -46,16 +46,10 @@ const moduleExports = { output: 'standalone', productionBrowserSourceMaps: true, experimental: { - instrumentationHook: process.env.NEXT_OPEN_TELEMETRY_ENABLED === 'true', - // disabled as it is not stable yet - // turbo: { - // rules: { - // '*.svg': { - // loaders: [ '@svgr/webpack' ], - // as: '*.js', - // }, - // }, - // }, + staleTimes: { + dynamic: 30, + 'static': 180, + }, }, }; diff --git a/nextjs/csp/generateCspPolicy.ts b/nextjs/csp/generateCspPolicy.ts index bf7b1236ef..64badf9ac1 100644 --- a/nextjs/csp/generateCspPolicy.ts +++ b/nextjs/csp/generateCspPolicy.ts @@ -6,14 +6,18 @@ function generateCspPolicy() { descriptors.app(), descriptors.ad(), descriptors.cloudFlare(), + descriptors.gasHawk(), descriptors.googleAnalytics(), descriptors.googleFonts(), descriptors.googleReCaptcha(), descriptors.growthBook(), + descriptors.helia(), + descriptors.marketplace(), descriptors.mixpanel(), descriptors.monaco(), descriptors.safe(), descriptors.sentry(), + descriptors.usernameApi(), descriptors.walletConnect(), ); diff --git a/nextjs/csp/policies/app.ts b/nextjs/csp/policies/app.ts index 5734f85a76..60b4cbb697 100644 --- a/nextjs/csp/policies/app.ts +++ b/nextjs/csp/policies/app.ts @@ -30,9 +30,19 @@ const getCspReportUrl = () => { } }; -export function app(): CspDev.DirectiveDescriptor { - const marketplaceFeaturePayload = getFeaturePayload(config.features.marketplace); +const externalFontsDomains = (() => { + try { + return [ + config.UI.fonts.heading?.url, + config.UI.fonts.body?.url, + ] + .filter(Boolean) + .map((urlString) => new URL(urlString)) + .map((url) => url.hostname); + } catch (error) {} +})(); +export function app(): CspDev.DirectiveDescriptor { return { 'default-src': [ // KEY_WORDS.NONE, @@ -57,7 +67,7 @@ export function app(): CspDev.DirectiveDescriptor { getFeaturePayload(config.features.addressVerification)?.api.endpoint, getFeaturePayload(config.features.nameService)?.api.endpoint, getFeaturePayload(config.features.addressMetadata)?.api.endpoint, - marketplaceFeaturePayload && 'api' in marketplaceFeaturePayload ? marketplaceFeaturePayload.api.endpoint : '', + getFeaturePayload(config.features.rewards)?.api.endpoint, // chain RPC server config.chain.rpcUrl, @@ -75,8 +85,9 @@ export function app(): CspDev.DirectiveDescriptor { // https://github.com/vercel/next.js/issues/14221#issuecomment-657258278 config.app.isDev ? KEY_WORDS.UNSAFE_EVAL : '', - // hash of ColorModeScript + // hash of ColorModeScript: system + dark '\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'', + '\'sha256-9A7qFFHmxdWjZMQmfzYD2XWaNHLu1ZmQB0Ds4Go764k=\'', ], 'style-src': [ @@ -112,12 +123,14 @@ export function app(): CspDev.DirectiveDescriptor { ], 'media-src': [ + KEY_WORDS.BLOB, '*', // see comment for img-src directive ], 'font-src': [ KEY_WORDS.DATA, ...MAIN_DOMAINS, + ...(externalFontsDomains || []), ], 'object-src': [ @@ -135,6 +148,9 @@ export function app(): CspDev.DirectiveDescriptor { 'frame-ancestors': [ KEY_WORDS.SELF, + + // allow remix.ethereum.org to embed our contract page in iframe + 'remix.ethereum.org', ], ...((() => { diff --git a/nextjs/csp/policies/gasHawk.ts b/nextjs/csp/policies/gasHawk.ts new file mode 100644 index 0000000000..6a1a8e624a --- /dev/null +++ b/nextjs/csp/policies/gasHawk.ts @@ -0,0 +1,30 @@ +import type CspDev from 'csp-dev'; + +import config from 'configs/app'; + +const feature = config.features.saveOnGas; + +export function gasHawk(): CspDev.DirectiveDescriptor { + if (!feature.isEnabled) { + return {}; + } + + const apiOrigin = (() => { + try { + const url = new URL(feature.apiUrlTemplate); + return url.origin; + } catch (error) { + return ''; + } + })(); + + if (!apiOrigin) { + return {}; + } + + return { + 'connect-src': [ + apiOrigin, + ], + }; +} diff --git a/nextjs/csp/policies/googleReCaptcha.ts b/nextjs/csp/policies/googleReCaptcha.ts index d759e70d59..55826d250d 100644 --- a/nextjs/csp/policies/googleReCaptcha.ts +++ b/nextjs/csp/policies/googleReCaptcha.ts @@ -3,7 +3,7 @@ import type CspDev from 'csp-dev'; import config from 'configs/app'; export function googleReCaptcha(): CspDev.DirectiveDescriptor { - if (!config.services.reCaptcha.siteKey) { + if (!config.services.reCaptchaV3.siteKey) { return {}; } diff --git a/nextjs/csp/policies/helia.ts b/nextjs/csp/policies/helia.ts new file mode 100644 index 0000000000..c6e925d378 --- /dev/null +++ b/nextjs/csp/policies/helia.ts @@ -0,0 +1,16 @@ +import type CspDev from 'csp-dev'; + +import config from 'configs/app'; + +export function helia(): CspDev.DirectiveDescriptor { + if (!config.UI.views.nft.verifiedFetch.isEnabled) { + return {}; + } + + return { + 'connect-src': [ + 'https://delegated-ipfs.dev', + 'https://trustless-gateway.link', + ], + }; +} diff --git a/nextjs/csp/policies/index.ts b/nextjs/csp/policies/index.ts index af8e24b2db..ebf3db4238 100644 --- a/nextjs/csp/policies/index.ts +++ b/nextjs/csp/policies/index.ts @@ -1,12 +1,16 @@ export { ad } from './ad'; export { app } from './app'; export { cloudFlare } from './cloudFlare'; +export { gasHawk } from './gasHawk'; export { googleAnalytics } from './googleAnalytics'; export { googleFonts } from './googleFonts'; export { googleReCaptcha } from './googleReCaptcha'; export { growthBook } from './growthBook'; +export { helia } from './helia'; +export { marketplace } from './marketplace'; export { mixpanel } from './mixpanel'; export { monaco } from './monaco'; export { safe } from './safe'; export { sentry } from './sentry'; +export { usernameApi } from './usernameApi'; export { walletConnect } from './walletConnect'; diff --git a/nextjs/csp/policies/marketplace.ts b/nextjs/csp/policies/marketplace.ts new file mode 100644 index 0000000000..08474a4bc1 --- /dev/null +++ b/nextjs/csp/policies/marketplace.ts @@ -0,0 +1,22 @@ +import type CspDev from 'csp-dev'; + +import config from 'configs/app'; + +const feature = config.features.marketplace; + +export function marketplace(): CspDev.DirectiveDescriptor { + if (!feature.isEnabled) { + return {}; + } + + return { + 'connect-src': [ + 'api' in feature ? feature.api.endpoint : '', + feature.rating ? 'https://api.airtable.com' : '', + ], + + 'frame-src': [ + '*', + ], + }; +} diff --git a/nextjs/csp/policies/monaco.ts b/nextjs/csp/policies/monaco.ts index edf8ac5f1c..72ad70f6e2 100644 --- a/nextjs/csp/policies/monaco.ts +++ b/nextjs/csp/policies/monaco.ts @@ -11,6 +11,12 @@ export function monaco(): CspDev.DirectiveDescriptor { 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/editor/editor.main.nls.js', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/basic-languages/solidity/solidity.js', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/basic-languages/elixir/elixir.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/basic-languages/javascript/javascript.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/basic-languages/typescript/typescript.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/language/json/jsonMode.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/language/json/jsonWorker.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/language/typescript/tsMode.js', + 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/language/typescript/tsWorker.js', 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/base/worker/workerMain.js', ], 'style-src': [ diff --git a/nextjs/csp/policies/usernameApi.ts b/nextjs/csp/policies/usernameApi.ts new file mode 100644 index 0000000000..4b2c2bd912 --- /dev/null +++ b/nextjs/csp/policies/usernameApi.ts @@ -0,0 +1,26 @@ +import type CspDev from 'csp-dev'; + +import config from 'configs/app'; + +const feature = config.features.addressProfileAPI; + +export function usernameApi(): CspDev.DirectiveDescriptor { + if (!feature.isEnabled) { + return {}; + } + + const apiOrigin = (() => { + try { + const url = new URL(feature.apiUrlTemplate); + return url.origin; + } catch (error) { + return ''; + } + })(); + + return { + 'connect-src': [ + apiOrigin, + ], + }; +} diff --git a/nextjs/csp/policies/walletConnect.ts b/nextjs/csp/policies/walletConnect.ts index e4c0c0e3fa..03f2dc0825 100644 --- a/nextjs/csp/policies/walletConnect.ts +++ b/nextjs/csp/policies/walletConnect.ts @@ -12,7 +12,9 @@ export function walletConnect(): CspDev.DirectiveDescriptor { return { 'connect-src': [ '*.web3modal.com', + '*.web3modal.org', '*.walletconnect.com', + '*.walletconnect.org', 'wss://relay.walletconnect.com', 'wss://www.walletlink.org', ], diff --git a/nextjs/getServerSideProps.ts b/nextjs/getServerSideProps.ts index ccaaf9763b..8009d6fc15 100644 --- a/nextjs/getServerSideProps.ts +++ b/nextjs/getServerSideProps.ts @@ -6,9 +6,9 @@ import type { RollupType } from 'types/client/rollup'; import type { Route } from 'nextjs-routes'; import config from 'configs/app'; -import isNeedProxy from 'lib/api/isNeedProxy'; const rollupFeature = config.features.rollup; const adBannerFeature = config.features.adsBanner; +import isNeedProxy from 'lib/api/isNeedProxy'; import type * as metadata from 'lib/metadata'; export interface Props { @@ -102,8 +102,8 @@ export const rollup: GetServerSideProps = async(context) => { return base(context); }; -export const optimisticRollup: GetServerSideProps = async(context) => { - if (!(rollupFeature.isEnabled && rollupFeature.type === 'optimistic')) { +export const outputRoots: GetServerSideProps = async(context) => { + if (!(rollupFeature.isEnabled && rollupFeature.outputRootsEnabled)) { return { notFound: true, }; @@ -112,7 +112,7 @@ export const optimisticRollup: GetServerSideProps = async(context) => { return base(context); }; -const BATCH_ROLLUP_TYPES: Array = [ 'zkEvm', 'zkSync', 'arbitrum' ]; +const BATCH_ROLLUP_TYPES: Array = [ 'zkEvm', 'zkSync', 'arbitrum', 'optimistic' ]; export const batch: GetServerSideProps = async(context) => { if (!(rollupFeature.isEnabled && BATCH_ROLLUP_TYPES.includes(rollupFeature.type))) { return { @@ -144,6 +144,16 @@ export const apiDocs: GetServerSideProps = async(context) => { return base(context); }; +export const graphIQl: GetServerSideProps = async(context) => { + if (!config.features.graphqlApiDocs.isEnabled) { + return { + notFound: true, + }; + } + + return base(context); +}; + export const csvExport: GetServerSideProps = async(context) => { if (!config.features.csvExport.isEnabled) { return { @@ -194,6 +204,16 @@ export const accounts: GetServerSideProps = async(context) => { return base(context); }; +export const accountsLabelSearch: GetServerSideProps = async(context) => { + if (!config.features.addressMetadata.isEnabled || !context.query.tagType) { + return { + notFound: true, + }; + } + + return base(context); +}; + export const userOps: GetServerSideProps = async(context) => { if (!config.features.userOps.isEnabled) { return { @@ -275,3 +295,13 @@ export const disputeGames: GetServerSideProps = async(context) => { return base(context); }; + +export const mud: GetServerSideProps = async(context) => { + if (!config.features.mudFramework.isEnabled) { + return { + notFound: true, + }; + } + + return base(context); +}; diff --git a/nextjs/middlewares/account.ts b/nextjs/middlewares/account.ts index e46c5a07fe..7ab562542f 100644 --- a/nextjs/middlewares/account.ts +++ b/nextjs/middlewares/account.ts @@ -1,10 +1,7 @@ import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; -import { route } from 'nextjs-routes'; - import config from 'configs/app'; -import { DAY } from 'lib/consts'; import * as cookies from 'lib/cookies'; export function account(req: NextRequest) { @@ -25,37 +22,7 @@ export function account(req: NextRequest) { const isProfileRoute = req.nextUrl.pathname.includes('/auth/profile'); if ((isAccountRoute || isProfileRoute)) { - const authUrl = feature.authUrl + route({ pathname: '/auth/auth0', query: { path: req.nextUrl.pathname } }); - return NextResponse.redirect(authUrl); - } - } - - // if user hasn't confirmed email yet - if (req.cookies.get(cookies.NAMES.INVALID_SESSION)) { - // if user has both cookies, make redirect to logout - if (apiTokenCookie) { - // yes, we could have checked that the current URL is not the logout URL, but we hadn't - // logout URL is always external URL in auth0.com sub-domain - // at least we hope so - - const res = NextResponse.redirect(feature.logoutUrl); - res.cookies.delete(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED); // reset cookie to show email verification page again - - return res; - } - - // if user hasn't seen email verification page, make redirect to it - if (!req.cookies.get(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED)) { - if (!req.nextUrl.pathname.includes('/auth/unverified-email')) { - const url = config.app.baseUrl + route({ pathname: '/auth/unverified-email' }); - const res = NextResponse.redirect(url); - res.cookies.set({ - name: cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED, - value: 'true', - expires: Date.now() + 7 * DAY, - }); - return res; - } + return NextResponse.redirect(config.app.baseUrl); } } } diff --git a/nextjs/middlewares/addressFormat.ts b/nextjs/middlewares/addressFormat.ts new file mode 100644 index 0000000000..bfd8bf14e0 --- /dev/null +++ b/nextjs/middlewares/addressFormat.ts @@ -0,0 +1,20 @@ +import type { NextRequest, NextResponse } from 'next/server'; + +import type { AddressFormat } from 'types/views/address'; + +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); + const defaultFormat = config.UI.views.address.hashFormat.availableFormats[0]; + + if (addressFormatCookie) { + const isValidCookie = config.UI.views.address.hashFormat.availableFormats.includes(addressFormatCookie.value as AddressFormat); + if (!isValidCookie) { + res.cookies.set(cookiesLib.NAMES.ADDRESS_FORMAT, defaultFormat, { path: '/' }); + } + } else { + res.cookies.set(cookiesLib.NAMES.ADDRESS_FORMAT, defaultFormat, { path: '/' }); + } +} diff --git a/nextjs/middlewares/index.ts b/nextjs/middlewares/index.ts index b9466373a3..4e4fdbef4e 100644 --- a/nextjs/middlewares/index.ts +++ b/nextjs/middlewares/index.ts @@ -1,2 +1,3 @@ export { account } from './account'; export { default as colorTheme } from './colorTheme'; +export { default as addressFormat } from './addressFormat'; diff --git a/nextjs/nextjs-routes.d.ts b/nextjs/nextjs-routes.d.ts index ed2eaa19c2..d6a7cb9764 100644 --- a/nextjs/nextjs-routes.d.ts +++ b/nextjs/nextjs-routes.d.ts @@ -9,10 +9,12 @@ declare module "nextjs-routes" { | StaticRoute<"/404"> | StaticRoute<"/account/api-key"> | StaticRoute<"/account/custom-abi"> + | StaticRoute<"/account/rewards"> | StaticRoute<"/account/tag-address"> | StaticRoute<"/account/verified-addresses"> | StaticRoute<"/account/watchlist"> | StaticRoute<"/accounts"> + | DynamicRoute<"/accounts/label/[slug]", { "slug": string }> | DynamicRoute<"/address/[hash]/contract-verification", { "hash": string }> | DynamicRoute<"/address/[hash]", { "hash": string }> | StaticRoute<"/api/config"> @@ -21,18 +23,19 @@ declare module "nextjs-routes" { | StaticRoute<"/api/log"> | StaticRoute<"/api/media-type"> | StaticRoute<"/api/metrics"> + | StaticRoute<"/api/monitoring/invalid-api-schema"> | StaticRoute<"/api/proxy"> | StaticRoute<"/api/sprite"> | StaticRoute<"/api-docs"> | DynamicRoute<"/apps/[id]", { "id": string }> | StaticRoute<"/apps"> - | StaticRoute<"/auth/auth0"> | StaticRoute<"/auth/profile"> - | StaticRoute<"/auth/unverified-email"> | DynamicRoute<"/batches/[number]", { "number": 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"> | StaticRoute<"/blocks"> | StaticRoute<"/contract-verification"> | StaticRoute<"/csv-export"> @@ -42,6 +45,7 @@ declare module "nextjs-routes" { | StaticRoute<"/graphiql"> | StaticRoute<"/"> | StaticRoute<"/login"> + | StaticRoute<"/mud-worlds"> | DynamicRoute<"/name-domains/[name]", { "name": string }> | StaticRoute<"/name-domains"> | DynamicRoute<"/op/[hash]", { "hash": string }> @@ -50,9 +54,11 @@ declare module "nextjs-routes" { | StaticRoute<"/public-tags/submit"> | StaticRoute<"/search-results"> | StaticRoute<"/sprite"> + | DynamicRoute<"/stats/[id]", { "id": string }> | StaticRoute<"/stats"> | 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<"/txs"> diff --git a/nextjs/types.ts b/nextjs/types.ts index c0366ae090..a6f2a38a4e 100644 --- a/nextjs/types.ts +++ b/nextjs/types.ts @@ -1,11 +1,11 @@ import type { NextPage } from 'next'; +import type React from 'react'; import type { Route } from 'nextjs-routes'; -// eslint-disable-next-line @typescript-eslint/ban-types export type NextPageWithLayout

= NextPage & { getLayout?: (page: React.ReactElement) => React.ReactNode; -} +}; export interface RouteParams { pathname: Pathname; diff --git a/nextjs/utils/detectBotRequest.ts b/nextjs/utils/detectBotRequest.ts index 1eecf6333a..d87a3972c0 100644 --- a/nextjs/utils/detectBotRequest.ts +++ b/nextjs/utils/detectBotRequest.ts @@ -9,7 +9,7 @@ type ReturnType = { } | { type: 'search_engine'; bot: SearchEngineBot; -} | undefined +} | undefined; export default function detectBotRequest(req: IncomingMessage): ReturnType { const userAgent = req.headers['user-agent']; diff --git a/nextjs/utils/fetchApi.ts b/nextjs/utils/fetchApi.ts index 63eff42384..eed5c613b4 100644 --- a/nextjs/utils/fetchApi.ts +++ b/nextjs/utils/fetchApi.ts @@ -12,22 +12,22 @@ type Params = ( { resource: R; pathParams?: ResourcePathParams; + queryParams?: Record; } | { url: string; route: string; } ) & { timeout?: number; -} +}; export default async function fetchApi>(params: Params): Promise { const controller = new AbortController(); - const timeout = setTimeout(() => { controller.abort(); }, params.timeout || SECOND); - const url = 'url' in params ? params.url : buildUrl(params.resource, params.pathParams); + const url = 'url' in params ? params.url : buildUrl(params.resource, params.pathParams, params.queryParams); const route = 'route' in params ? params.route : RESOURCES[params.resource]['path']; const end = metrics?.apiRequestDuration.startTimer(); diff --git a/nextjs/utils/fetchProxy.ts b/nextjs/utils/fetchProxy.ts index 0081781215..a8f0c032f2 100644 --- a/nextjs/utils/fetchProxy.ts +++ b/nextjs/utils/fetchProxy.ts @@ -23,7 +23,8 @@ export default function fetchFactory( cookie: apiToken ? `${ cookies.NAMES.API_TOKEN }=${ apiToken }` : '', ..._pick(_req.headers, [ 'x-csrf-token', - 'Authorization', + 'Authorization', // the old value, just in case + 'authorization', // Node.js automatically lowercases headers // feature flags 'updated-gas-oracle', ]) as Record, @@ -33,6 +34,7 @@ export default function fetchFactory( message: 'API fetch via Next.js proxy', url, // headers, + // init, }); const body = (() => { diff --git a/package.json b/package.json index f127afd7e5..d57d2e84a4 100644 --- a/package.json +++ b/package.json @@ -4,20 +4,21 @@ "private": false, "homepage": "https://github.com/blockscout/frontend#readme", "engines": { - "node": "20.11.0", - "npm": "10.2.4" + "node": "22.11.0", + "npm": "10.9.0" }, "scripts": { "dev": "./tools/scripts/dev.sh", "dev:preset": "./tools/scripts/dev.preset.sh", "dev:preset:sync": "tsc -p ./tools/preset-sync/tsconfig.json && node ./tools/preset-sync/index.js", "build": "next build", + "build:next": "./deploy/scripts/download_assets.sh ./public/assets/configs && yarn svg:build-sprite && ./deploy/scripts/make_envs_script.sh && next build", "build:docker": "docker build --build-arg GIT_COMMIT_SHA=$(git rev-parse --short HEAD) --build-arg GIT_TAG=$(git describe --tags --abbrev=0) -t blockscout-frontend:local ./", "start": "next start", "start:docker:local": "docker run -p 3000:3000 --env-file .env.local blockscout-frontend:local", "start:docker:preset": "./tools/scripts/docker.preset.sh", - "lint:eslint": "eslint . --ext .js,.jsx,.ts,.tsx", - "lint:eslint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix", + "lint:eslint": "eslint .", + "lint:eslint:fix": "eslint . --fix", "lint:tsc": "tsc -p ./tsconfig.json", "lint:envs-validator:test": "cd ./deploy/tools/envs-validator && ./test.sh", "prepare": "husky install", @@ -25,8 +26,8 @@ "svg:build-sprite": "icons build -i ./icons -o ./public/icons --optimize", "test:pw": "./tools/scripts/pw.sh", "test:pw:local": "export NODE_PATH=$(pwd)/node_modules && yarn test:pw", - "test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-focal ./tools/scripts/pw.docker.sh", - "test:pw:docker:deps": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-focal ./tools/scripts/pw.docker.deps.sh", + "test:pw:docker": "docker run --rm --ipc=host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.49.0-noble ./tools/scripts/pw.docker.sh", + "test:pw:docker:deps": "docker run --rm --ipc=host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.49.0-noble ./tools/scripts/pw.docker.deps.sh", "test:pw:ci": "yarn test:pw --project=$PW_PROJECT", "test:pw:detect-affected": "node ./deploy/tools/affected-tests/index.js", "test:jest": "jest", @@ -36,41 +37,45 @@ "monitoring:grafana:local": "docker run -d -p 4000:3000 --name=blockscout_grafana --user $(id -u) --volume $(pwd)/grafana:/var/lib/grafana grafana/grafana-enterprise" }, "dependencies": { - "@blockscout/bens-types": "1.3.4", - "@blockscout/stats-types": "1.6.0", + "@blockscout/bens-types": "1.4.1", + "@blockscout/stats-types": "2.0.0", "@blockscout/visualizer-types": "0.2.0", "@chakra-ui/react": "2.7.1", "@chakra-ui/theme-tools": "^2.0.18", "@emotion/react": "^11.10.4", "@emotion/styled": "^11.10.4", "@growthbook/growthbook-react": "0.21.0", + "@helia/verified-fetch": "2.0.1", "@hypelab/sdk-react": "^1.0.0", "@metamask/post-message-stream": "^7.0.0", "@metamask/providers": "^10.2.1", "@monaco-editor/react": "^4.4.6", - "@next/bundle-analyzer": "14.2.3", - "@opentelemetry/auto-instrumentations-node": "^0.39.4", - "@opentelemetry/exporter-metrics-otlp-proto": "^0.45.1", - "@opentelemetry/exporter-trace-otlp-http": "^0.45.0", - "@opentelemetry/resources": "^1.18.0", - "@opentelemetry/sdk-node": "^0.45.0", - "@opentelemetry/sdk-trace-node": "^1.18.0", - "@opentelemetry/semantic-conventions": "^1.18.0", + "@next/bundle-analyzer": "15.0.3", + "@opentelemetry/auto-instrumentations-node": "0.43.0", + "@opentelemetry/exporter-jaeger": "1.27.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.49.1", + "@opentelemetry/exporter-trace-otlp-http": "0.49.1", + "@opentelemetry/resources": "1.22.0", + "@opentelemetry/sdk-node": "0.49.1", + "@opentelemetry/sdk-trace-node": "1.22.0", + "@opentelemetry/semantic-conventions": "1.22.0", + "@scure/base": "1.1.9", "@sentry/cli": "^2.21.2", "@sentry/react": "7.24.0", "@sentry/tracing": "7.24.0", "@slise/embed-react": "^2.2.0", - "@tanstack/react-query": "^5.4.3", - "@tanstack/react-query-devtools": "^5.4.3", + "@tanstack/react-query": "5.55.4", + "@tanstack/react-query-devtools": "5.55.4", "@types/papaparse": "^5.3.5", "@types/react-scroll": "^1.8.4", - "@web3modal/wagmi": "4.2.1", + "@web3modal/wagmi": "5.1.7", + "airtable": "^0.12.2", "bignumber.js": "^9.1.0", "blo": "^1.1.1", "chakra-react-select": "^4.4.3", "crypto-js": "^4.2.0", "d3": "^7.6.1", - "dappscout-iframe": "0.2.2", + "dappscout-iframe": "0.2.5", "dayjs": "^1.11.5", "dom-to-image": "^2.6.0", "focus-visible": "^5.2.0", @@ -85,21 +90,21 @@ "magic-bytes.js": "1.8.0", "mixpanel-browser": "^2.47.0", "monaco-editor": "^0.34.1", - "next": "14.2.3", + "next": "15.0.3", "nextjs-routes": "^1.0.8", "node-fetch": "^3.2.9", "papaparse": "^5.3.2", - "path-to-regexp": "^6.2.1", + "path-to-regexp": "8.1.0", "phoenix": "^1.6.15", "pino-http": "^8.2.1", "pino-pretty": "^9.1.1", "prom-client": "15.1.1", "qrcode": "^1.5.1", - "react": "18.2.0", + "react": "18.3.1", "react-device-detect": "^2.2.3", - "react-dom": "18.2.0", - "react-google-recaptcha": "^3.1.0", - "react-hook-form": "^7.33.1", + "react-dom": "18.3.1", + "react-google-recaptcha-v3": "1.10.1", + "react-hook-form": "7.52.1", "react-identicons": "^1.2.5", "react-intersection-observer": "^9.5.2", "react-jazzicon": "^1.0.4", @@ -107,15 +112,20 @@ "react-scroll": "^1.8.7", "swagger-ui-react": "^5.9.0", "use-font-face-observer": "^1.2.1", - "viem": "2.10.9", - "wagmi": "2.9.2", + "valibot": "0.38.0", + "viem": "2.21.5", + "wagmi": "2.12.10", "xss": "^1.0.14" }, "devDependencies": { - "@playwright/experimental-ct-react": "1.41.1", - "@playwright/test": "1.41.1", + "@eslint/compat": "1.2.2", + "@eslint/js": "9.14.0", + "@next/eslint-plugin-next": "15.0.3", + "@playwright/experimental-ct-react": "1.49.0", + "@playwright/test": "1.49.0", + "@stylistic/eslint-plugin": "2.10.1", "@svgr/webpack": "^6.5.1", - "@tanstack/eslint-plugin-query": "^5.0.5", + "@tanstack/eslint-plugin-query": "5.60.1", "@testing-library/react": "^14.0.0", "@total-typescript/ts-reset": "^0.4.0", "@types/crypto-js": "^4.1.1", @@ -125,11 +135,11 @@ "@types/jest": "^29.2.0", "@types/js-cookie": "^3.0.2", "@types/mixpanel-browser": "^2.38.1", - "@types/node": "20.11.0", + "@types/node": "20.16.7", "@types/phoenix": "^1.5.4", "@types/qrcode": "^1.5.0", - "@types/react": "18.0.9", - "@types/react-dom": "18.0.5", + "@types/react": "18.3.12", + "@types/react-dom": "18.3.1", "@types/react-google-recaptcha": "^2.1.5", "@types/swagger-ui-react": "^4.11.0", "@types/ws": "^8.5.3", @@ -137,14 +147,18 @@ "@vitejs/plugin-react": "^4.0.0", "css-loader": "^6.7.3", "dotenv-cli": "^6.0.0", - "eslint": "^8.32.0", - "eslint-config-next": "13.3.0", - "eslint-plugin-es5": "^1.5.0", - "eslint-plugin-import-helpers": "^1.2.1", - "eslint-plugin-jest": "^27.1.6", + "eslint": "9.14.0", + "eslint-config-next": "15.0.3", + "eslint-plugin-import": "2.31.0", + "eslint-plugin-import-helpers": "2.0.1", + "eslint-plugin-jest": "28.9.0", + "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-no-cyrillic-string": "^1.0.5", - "eslint-plugin-playwright": "^0.11.2", - "eslint-plugin-regexp": "^1.7.0", + "eslint-plugin-playwright": "2.0.1", + "eslint-plugin-react": "7.37.2", + "eslint-plugin-react-hooks": "5.0.0", + "eslint-plugin-regexp": "2.6.0", + "globals": "15.12.0", "husky": "^8.0.0", "jest": "^29.2.1", "jest-environment-jsdom": "^29.2.1", @@ -157,11 +171,16 @@ "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "typescript": "5.4.2", + "typescript-eslint": "8.14.0", "vite-plugin-svgr": "^2.2.2", - "vite-tsconfig-paths": "^3.5.2", + "vite-tsconfig-paths": "4.3.2", "ws": "^8.17.1" }, "lint-staged": { "*.{js,jsx,ts,tsx}": "eslint --cache --fix" + }, + "resolutions": { + "@types/react": "18.3.12", + "@types/react-dom": "18.3.1" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 28520d76a3..3c412226f3 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,4 +1,4 @@ -import type { ChakraProps } from '@chakra-ui/react'; +import { type ChakraProps } from '@chakra-ui/react'; import { GrowthBookProvider } from '@growthbook/growthbook-react'; import * as Sentry from '@sentry/react'; import { QueryClientProvider } from '@tanstack/react-query'; @@ -13,12 +13,16 @@ import useQueryClientConfig from 'lib/api/useQueryClientConfig'; import { AppContextProvider } from 'lib/contexts/app'; import { ChakraProvider } from 'lib/contexts/chakra'; import { MarketplaceContextProvider } from 'lib/contexts/marketplace'; +import { RewardsContextProvider } from 'lib/contexts/rewards'; import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection'; +import { SettingsContextProvider } from 'lib/contexts/settings'; import { growthBook } from 'lib/growthbook/init'; import useLoadFeatures from 'lib/growthbook/useLoadFeatures'; import useNotifyOnNavigation from 'lib/hooks/useNotifyOnNavigation'; import { SocketProvider } from 'lib/socket/context'; +import RewardsLoginModal from 'ui/rewards/login/RewardsLoginModal'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; +import AppErrorGlobalContainer from 'ui/shared/AppError/AppErrorGlobalContainer'; import GoogleAnalytics from 'ui/shared/GoogleAnalytics'; import Layout from 'ui/shared/layout/Layout'; import Web3ModalProvider from 'ui/shared/Web3ModalProvider'; @@ -28,7 +32,7 @@ import 'lib/setLocale'; type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout; -} +}; const ERROR_SCREEN_STYLES: ChakraProps = { h: '100vh', @@ -38,7 +42,7 @@ const ERROR_SCREEN_STYLES: ChakraProps = { justifyContent: 'center', width: 'fit-content', maxW: '800px', - margin: '0 auto', + margin: { base: '0 auto', lg: '0 auto' }, p: { base: 4, lg: 0 }, }; @@ -60,6 +64,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { @@ -67,9 +72,14 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { - - { getLayout() } - + + + + { getLayout() } + { config.features.rewards.isEnabled && } + + + diff --git a/pages/_document.tsx b/pages/_document.tsx index bfb6c3b107..1706e247ac 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -6,7 +6,8 @@ import React from 'react'; import logRequestFromBot from 'nextjs/utils/logRequestFromBot'; import * as serverTiming from 'nextjs/utils/serverTiming'; -import theme from 'theme'; +import config from 'configs/app'; +import theme from 'theme/theme'; import * as svgSprite from 'ui/shared/IconSvg'; class MyDocument extends Document { @@ -35,11 +36,11 @@ class MyDocument extends Document { { /* FONTS */ } @@ -47,12 +48,12 @@ class MyDocument extends Document { - -

+ + { !isHidden && ( + <> + + +
+ + ) }
); }; diff --git a/ui/shared/ad/CoinzillaBanner.tsx b/ui/shared/ad/CoinzillaBanner.tsx index bb0222eb4b..373c777d53 100644 --- a/ui/shared/ad/CoinzillaBanner.tsx +++ b/ui/shared/ad/CoinzillaBanner.tsx @@ -4,10 +4,20 @@ import React from 'react'; import type { BannerProps } from './types'; +import useIsMobile from 'lib/hooks/useIsMobile'; import isBrowser from 'lib/isBrowser'; const CoinzillaBanner = ({ className, platform }: BannerProps) => { const isInBrowser = isBrowser(); + const isMobileViewport = useIsMobile(); + + // On the home page there are two ad banners + // - one in the stats section with prop "platform === mobile", should be hidden on mobile devices + // - another - a regular ad banner, should be hidden on desktop devices + // The Coinzilla provider doesn't work properly with 2 banners with the same id on the page + // So we use this flag to skip ad initialization for the first home page banner on mobile devices + // For all other pages this is not the case + const isHidden = (isMobileViewport && platform === 'mobile'); const { width, height } = (() => { switch (platform) { @@ -22,7 +32,7 @@ const CoinzillaBanner = ({ className, platform }: BannerProps) => { })(); React.useEffect(() => { - if (isInBrowser) { + if (isInBrowser && !isHidden) { window.coinzilla_display = window.coinzilla_display || []; const cDisplayPreferences = { zone: '26660bf627543e46851', @@ -31,7 +41,7 @@ const CoinzillaBanner = ({ className, platform }: BannerProps) => { }; window.coinzilla_display.push(cDisplayPreferences); } - }, [ height, isInBrowser, width ]); + }, [ height, isInBrowser, isHidden, width ]); return ( { h={ height ? `${ height }px` : { base: '100px', lg: '90px' } } w={ width ? `${ width }px` : undefined } > -