diff --git a/.env b/.env new file mode 100644 index 00000000..09bff4ca --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +NEXT_PUBLIC_PREFIX = bridge +NEXT_PUBLIC_APP_NAME = bridge +NEXT_PUBLIC_APP_ENV = testnet +NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID = 5c5740c548328b21cf14472d580f3e2a \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..4edf267c --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +NEXT_PUBLIC_PREFIX = bridge +NEXT_PUBLIC_APP_NAME = bridge +# NEXT_PUBLIC_SENTRY_DNS = https://example.ingest.sentry.io/0000000 +# NEXT_PUBLIC_ANALYTICS_ID = G-XXXXXXXXX +# NEXT_PUBLIC_APP_ENV = testnet + +NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID = 5c5740c548328b21cf14472d580f3e2a \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100755 index 00000000..f6418f34 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,31 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "next/core-web-vitals", + "plugin:@typescript-eslint/recommended", + "eslint-config-prettier", + "plugin:prettier/recommended" + ], + "parserOptions": { + "ecmaFeatures": { + "jsx": true + } + }, + "rules": { + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-unsafe-assignment": 0, + "@typescript-eslint/ban-ts-comment": 0, + "react-hooks/exhaustive-deps": [ + "warn", + { + "additionalHooks": "^use(LockCallback|Interval|ThrottleCallback|DebounceCallback)$" + } + ] + }, + "env": { + "browser": true, + "node": true + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..ea59817c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: 👾 Bug Report +about: Report a bug or issue with the project. +title: '' +labels: 'bug' +assignees: '' + +--- + +### Description +A clear and concise description of what the bug is. + +### Steps To Reproduce +1. Log in... +2. Ensure that... +3. Allow a long period of inactivity to pass... +4. Observe that... +5. Attempt to log in... + +### Current Behavior +- After the period of inactivity... +- When the user tries to log in using another method... +- This causes a bug due to... + +### Expected Behavior +- After a long period of inactivity... +- When a user logs in successfully... +- This ensures that only... + +### Environment +- Platform: PC +- Node: v18.18.0 +- Browser: Chrome 126.0.6478.56 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..163c1c43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,10 @@ +blank_issues_enabled: false +issue_template: + - name: 👾 Bug Report + description: Report a bug or issue with the project. + labels: ["bug"] + template: bug_report.md + - name: 💡 Feature Request + description: Create a new ticket for a new feature request. + labels: ["enhancement"] + template: feature_request.md \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..a4b81753 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,27 @@ +--- +name: 💡 Feature Request +about: Create a new ticket for a new feature request +title: '' +labels: 'enhancement' +assignees: '' + +--- + +### Expected Behavior +Describe the expected behavior here. + +### Specifications +As a `user`, I would like to `action` so that `reason`. + +**Features:** +- describe feature details here. + +**Development Tasks:** +- [ ] Task 1 +- [ ] Task 2 + +### Dependencies +List any dependencies that are required for this feature by providing links to the issues or repositories. + +### References +List any references that are related to this feature request. \ No newline at end of file diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 00000000..696893f4 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,21 @@ +name: Setup +runs: + using: composite + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org + cache: yarn + + - uses: actions/cache@v3 + id: install-cache + with: + path: node_modules/ + key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }} + + - if: steps.install-cache.outputs.cache-hit != 'true' + run: yarn install --frozen-lockfile --ignore-scripts + shell: bash diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 00000000..965b466b --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,41 @@ +name: Run tests and upload coverage + +on: + push: + branches: + - dev + - master + # - release/* + - feature/ut + pull_request: + branches: + - master + +jobs: + test: + name: Run tests and collect coverage + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: '20.18.2' + + - name: Install dependencies + run: yarn + + - name: Run tests + run: yarn vitest run --coverage + + - name: Upload results to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage/lcov.info + flags: unittests + name: codecov-umbrella diff --git a/.github/workflows/mainnet-package.yaml b/.github/workflows/mainnet-package.yaml new file mode 100644 index 00000000..f4fdbba8 --- /dev/null +++ b/.github/workflows/mainnet-package.yaml @@ -0,0 +1,43 @@ +name: Build and Push Image +on: + push: + tags: + - 'v*.*.*' +jobs: + build-and-push-image: + runs-on: eBridge-runner + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Create image tag + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.REPOSITORY_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.REPOSITORY }}/ebridge-web # change this to your project frontend name + tags: | + type=ref,event=tag + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v5 + env: + NEXT_PUBLIC_SENTRY_DNS: ${{ secrets.MAINNET_NEXT_PUBLIC_SENTRY_DNS }} # testnet mainnet + NEXT_PUBLIC_ANALYTICS_ID: ${{ secrets.MAINNET_NEXT_NEXT_PUBLIC_ANALYTICS_ID }} + NEXT_PUBLIC_APP_ENV: mainnet + NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ${{ secrets.MAINNET_NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID }} + NEXT_PUBLIC_TON_API_KEY: ${{ secrets.MAINNET_NEXT_PUBLIC_TON_API_KEY }} + with: + context: . + push: true + platforms: linux/amd64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + NEXT_PUBLIC_SENTRY_DNS=${{ secrets.MAINNET_NEXT_PUBLIC_SENTRY_DNS }} + NEXT_PUBLIC_ANALYTICS_ID=${{ secrets.MAINNET_NEXT_NEXT_PUBLIC_ANALYTICS_ID }} + NEXT_PUBLIC_APP_ENV=mainnet + NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${{ secrets.MAINNET_NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID }} + NEXT_PUBLIC_TON_API_KEY=${{ secrets.MAINNET_NEXT_PUBLIC_TON_API_KEY }} + ENV_NAME=mainnet \ No newline at end of file diff --git a/.github/workflows/nextLint.yml b/.github/workflows/nextLint.yml new file mode 100644 index 00000000..276c550d --- /dev/null +++ b/.github/workflows/nextLint.yml @@ -0,0 +1,33 @@ +name: Next Lint Check +# + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Node.js + uses: actions/setup-node@v1 + with: + node-version: '20' # Your node version, default 20 in 2024 + + - name: Cache node modules + uses: actions/cache@v3 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Install Dependencies + run: yarn + + - name: Run ESLint + run: yarn lint \ No newline at end of file diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml new file mode 100644 index 00000000..fdc395ff --- /dev/null +++ b/.github/workflows/security.yaml @@ -0,0 +1,23 @@ +name: Security Review + +permissions: + pull-requests: write + contents: read + +on: + pull_request: + +jobs: + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 2 + + - uses: Portkey-Wallet/claude-code-security-review@main + with: + comment-pr: true + claude-api-key: ${{ secrets.CLAUDE_API_KEY }} + run-every-commit: true \ No newline at end of file diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml new file mode 100644 index 00000000..2a257c10 --- /dev/null +++ b/.github/workflows/sonarqube.yaml @@ -0,0 +1,21 @@ +on: + pull_request: + types: [opened, synchronize, reopened] + +name: PR Static Code Analysis +jobs: + static-code-analysis: + runs-on: ubuntu-latest + steps: + - name: Code Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: SonarQube Scan + uses: sonarsource/sonarqube-scan-action@v2.1.0 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + with: + args: > + -Dsonar.projectKey=ebridge-web \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..fafa759d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +name: Test + +on: + push: + branches: ['dev', 'master'] + pull_request: + branches: ['dev', 'master'] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + - run: yarn lint \ No newline at end of file diff --git a/.github/workflows/testnet-package.yaml b/.github/workflows/testnet-package.yaml new file mode 100644 index 00000000..34631e6c --- /dev/null +++ b/.github/workflows/testnet-package.yaml @@ -0,0 +1,67 @@ + +name: Build and Push Image +on: + push: + branches: ['release/*'] +jobs: + build-and-push-image: + runs-on: eBridge-runner + permissions: + contents: read + outputs: + short_sha: ${{ steps.vars.outputs.short_sha }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set short git commit SHA + id: vars + run: | + calculatedSha=$(git rev-parse --short ${{ github.sha }}) + echo "short_sha=$calculatedSha" >> "$GITHUB_OUTPUT" + - name: Create image tag + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.REPOSITORY_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.REPOSITORY }}/ebridge-web # change this to your project frontend name + tags: | + type=sha + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v5 + env: + NEXT_PUBLIC_SENTRY_DNS: ${{ secrets.TESTNET_NEXT_PUBLIC_SENTRY_DNS }} # testnet mainnet + NEXT_PUBLIC_ANALYTICS_ID: ${{ secrets.TESTNET_NEXT_NEXT_PUBLIC_ANALYTICS_ID }} + NEXT_PUBLIC_APP_ENV: testnet + NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ${{ secrets.TESTNET_NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID }} + with: + context: . + push: true + platforms: linux/amd64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + NEXT_PUBLIC_SENTRY_DNS=${{ secrets.TESTNET_NEXT_PUBLIC_SENTRY_DNS }} + NEXT_PUBLIC_ANALYTICS_ID=${{ secrets.TESTNET_NEXT_NEXT_PUBLIC_ANALYTICS_ID }} + NEXT_PUBLIC_APP_ENV=testnet + NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${{ secrets.TESTNET_NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID }} + ENV_NAME=testnet + dispatch: + runs-on: eBridge-runner + needs: build-and-push-image + steps: + - name: Deploy + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.TOK }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'eBridgeCrosschain', + repo: 'devops', + workflow_id: 'deployment_testnet.yaml', + ref: 'main', + inputs: { + appName: 'ebridge-web-server', + commit_sha: 'sha-${{ needs.build-and-push-image.outputs.short_sha }}', + } + }) diff --git a/.github/workflows/unit-test.yaml b/.github/workflows/unit-test.yaml new file mode 100644 index 00000000..ae8dbcd3 --- /dev/null +++ b/.github/workflows/unit-test.yaml @@ -0,0 +1,27 @@ +name: Test Badge + +permissions: + contents: write + +on: + push: + branches: + - dev + - master + # - release/* + - feature/ut + pull_request: + branches: + - master + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Run frontend ci action + uses: eBridgeCrosschain/frontend-ci-action@v0.0.1 + with: + node-version: '20.18.2' + commit-token: ${{ secrets.COMMIT_TOKEN }} + branch-name: 'feature/ut-badge' diff --git a/.gitignore b/.gitignore new file mode 100755 index 00000000..eb05bd51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage +**/jest-report.xml + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo + +# shell +*.sh +!postinstall.sh +.eslintcache \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 00000000..4c49ae6a --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +yarn commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..36af2198 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..3cc7493a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,21 @@ +{ + "jsxBracketSameLine": true, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 120, + "endOfLine": "lf", + "overrides": [ + { + "files": "*.css", + "options": { + "parser": "css" + } + }, + { + "files": "*.less", + "options": { + "parser": "less" + } + } + ] +} diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 00000000..bf80ce92 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,10 @@ +{ + "extends": ["stylelint-config-standard", "stylelint-config-prettier"], + "customSyntax": "postcss-less", + "rules": { + "font-family-name-quotes": null, + "rule-empty-line-before": null, + "selector-pseudo-class-no-unknown": null, + "no-descending-specificity": null + } +} diff --git a/.vscode/codeSegment.code-snippets b/.vscode/codeSegment.code-snippets new file mode 100644 index 00000000..54addfaa --- /dev/null +++ b/.vscode/codeSegment.code-snippets @@ -0,0 +1,62 @@ +{ + "App prefix": { + "scope": "less", + "prefix": "v", + "body": ["~'var(--@{app-prefix}-$1)'"], + "description": "less prefix" + }, + "Class prefix": { + "scope": "less", + "prefix": "c", + "body": [".@{app-prefix}-$1"], + "description": "less class prefix" + }, + "Css Module Global": { + "scope": "less", + "prefix": "g", + "body": [":global {$1}"], + "description": "css module global" + }, + "import styles": { + "scope": "javascriptreact, typescriptreact", + "prefix": "styles", + "body": ["import styles from './styles.module.less';"], + "description": "import styles" + }, + "xs media": { + "scope": "less", + "prefix": "xs", + "body": ["@media screen and (max-width: @xs-width) {$1}"], + "description": "xs media" + }, + "md media": { + "scope": "less", + "prefix": "md", + "body": ["@media screen and (max-width: @md-width) {$1}"], + "description": "md media" + }, + "xl media": { + "scope": "less", + "prefix": "xl", + "body": ["@media screen and (max-width: @xl-width) {$1}"], + "description": "xl media" + }, + "xxl media": { + "scope": "less", + "prefix": "xl", + "body": ["@media screen and (max-width: @xxl-width) {$1}"], + "description": "xl media" + }, + "font medium": { + "scope": "less", + "prefix": "medium", + "body": [".font-family-medium();"], + "description": "font medium" + }, + "font regular": { + "scope": "less", + "prefix": "regular", + "body": [".font-family-regular();"], + "description": "font regular" + } +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..9c46fb72 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: debug server-side", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev" + }, + { + "name": "Next.js: debug client-side", + "type": "pwa-chrome", + "request": "launch", + "url": "http://localhost:3000" + }, + { + "name": "Next.js: debug full stack", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "console": "integratedTerminal", + "serverReadyAction": { + "pattern": "started server on .+, url: (https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..c6630b52 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[less]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "cSpell.words": ["stylelint", "antd", "Cascader", "clsx", "fundpools", "Httpclient", "rehype", "Sider", "ewell"], + "cSpell.enableFiletypes": ["typescriptreact"], + + "px-to-rem.px-per-rem": 14 +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..9bfa9d3b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +FROM node:18 AS base + +ARG web=/opt/workspace/aelf-bridge-frontend + +# Rebuild the source code only when needed +FROM base AS builder +ARG NEXT_PUBLIC_SENTRY_DNS +ARG NEXT_PUBLIC_ANALYTICS_ID +ARG NEXT_PUBLIC_APP_ENV +ARG NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID +ARG NEXT_PUBLIC_TON_API_KEY +ARG ENV_NAME +ENV NEXT_PUBLIC_SENTRY_DNS=${NEXT_PUBLIC_SENTRY_DNS} +ENV NEXT_PUBLIC_ANALYTICS_ID=${NEXT_PUBLIC_ANALYTICS_ID} +ENV NEXT_PUBLIC_APP_ENV=${NEXT_PUBLIC_APP_ENV} +ENV NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID} +ENV NEXT_PUBLIC_TON_API_KEY=${NEXT_PUBLIC_TON_API_KEY} +ENV ENV_NAME=${ENV_NAME} + +# Production image, copy all the files and run next +FROM base AS runner +ARG NEXT_PUBLIC_SENTRY_DNS +ARG NEXT_PUBLIC_ANALYTICS_ID +ARG NEXT_PUBLIC_APP_ENV +ARG NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID +ARG NEXT_PUBLIC_TON_API_KEY +ARG ENV_NAME +ENV NEXT_PUBLIC_SENTRY_DNS=${NEXT_PUBLIC_SENTRY_DNS} +ENV NEXT_PUBLIC_ANALYTICS_ID=${NEXT_PUBLIC_ANALYTICS_ID} +ENV NEXT_PUBLIC_APP_ENV=${NEXT_PUBLIC_APP_ENV} +ENV NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID} +ENV NEXT_PUBLIC_TON_API_KEY=${NEXT_PUBLIC_TON_API_KEY} +ENV ENV_NAME=${ENV_NAME} +WORKDIR ${web} + +COPY . ${web} + +# # add git +# RUN apk add git + +# # add python +# RUN apk add --no-cache python3 make g++ + +RUN yarn \ + && NEXT_PUBLIC_SENTRY_DNS=${NEXT_PUBLIC_SENTRY_DNS} NEXT_PUBLIC_ANALYTICS_ID=${NEXT_PUBLIC_ANALYTICS_ID} NEXT_PUBLIC_APP_ENV=${NEXT_PUBLIC_APP_ENV} NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=${NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID} ENV_NAME=${ENV_NAME} yarn build + +ENTRYPOINT yarn start + +EXPOSE 3000 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index dc8b916f..28db76be --- a/README.md +++ b/README.md @@ -1 +1,46 @@ -# ebridge-web +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +

+ UNIT TESTS +    + UNIT TESTS COVERAGE +

+ +## Getting Started + +First, setup env files refer to `.env.example`: + +- `.env.local` for all local environment +- `.env.mainnet.local` for mainnet +- `.env.testnet.local` for testnet + +Run the development server: + +```bash +npm run dev +# or +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..422b1944 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1 @@ +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/docs/PROJECT_STRUCTURE.md b/docs/PROJECT_STRUCTURE.md new file mode 100644 index 00000000..abc2699a --- /dev/null +++ b/docs/PROJECT_STRUCTURE.md @@ -0,0 +1,266 @@ +# eBridge-web Project Architecture Documentation + +## Project Overview + +eBridge-web is a frontend application built with Next.js framework, designed to implement cross-chain bridge functionality. The project uses TypeScript as the development language and employs a modern frontend technology stack and toolchain. + +## Technology Stack + +- **Framework**: Next.js +- **Language**: TypeScript +- **Package Manager**: Yarn +- **UI Library**: Ant Design +- **State Management**: React Query +- **Web3 Libraries**: + - aelf-sdk + - aelf-sdk-cross-chain + - wagmi + - viem +- **Code Standards**: ESLint, Prettier +- **Testing**: Vitest +- **CI/CD**: GitHub Actions + +## Testing + +The project uses Vitest for unit testing. Test files are located alongside the source files with the `.test.ts` or `.test.tsx` extension. + +### Test Structure + +- Test files follow the naming convention: `[filename].test.ts` or `[filename].test.tsx` +- Tests are organized using the `describe` and `it` blocks +- Each test file should be placed in the same directory as the component/function it tests + +### Test Coverage + +The project maintains a minimum test coverage requirement: +- Statement coverage: 80% +- Branch coverage: 70% +- Function coverage: 80% +- Line coverage: 80% + +### Running Tests + +```bash +# Run all tests +yarn test + +# Run tests without watch mode +yarn test:not-watch + +# Update test snapshots +yarn test:update + +# Run tests with coverage report +yarn test:coverage + +# Run tests for CI +yarn test:ci +``` + +### Test Utilities + +The project provides several test utilities: +- `@testing-library/react` for component testing +- `@testing-library/react-hooks` for hook testing +- `@testing-library/jest-dom` for DOM testing +- `happy-dom` for browser environment simulation + +### Testing Best Practices + +1. **Component Testing** + - Test component rendering + - Test user interactions + - Test component props + - Test component state changes + +2. **Hook Testing** + - Test hook initialization + - Test hook state changes + - Test hook side effects + - Test hook cleanup + +3. **Utility Function Testing** + - Test input/output + - Test edge cases + - Test error handling + +4. **Integration Testing** + - Test component interactions + - Test API calls + - Test state management + +### Mocking + +The project uses Vitest's mocking capabilities for: +- API calls +- Web3 interactions +- Contract calls +- External dependencies + +Example of a test file: + +```typescript +import { render, screen } from '@testing-library/react'; +import { MyComponent } from './MyComponent'; +import { describe, it, expect } from 'vitest'; + +describe('MyComponent', () => { + it('renders correctly', () => { + render(); + expect(screen.getByText('Hello World')).toBeInTheDocument(); + }); + + it('handles user interaction', () => { + render(); + const button = screen.getByRole('button'); + fireEvent.click(button); + expect(screen.getByText('Clicked!')).toBeInTheDocument(); + }); +}); +``` + +## Package Management + +The project uses Yarn as its package manager. Key Yarn-related files and configurations: + +- `yarn.lock`: Lock file that ensures consistent dependency versions across all installations +- `.yarnrc.yml`: Yarn configuration file for managing package registry and other settings +- `.yarn/`: Directory containing Yarn's cache and other internal files + +### Yarn Commands + +Common Yarn commands used in the project: + +```bash +# Install dependencies +yarn install + +# Run development server +yarn dev + +# Run development server for mainnet +yarn dev:mainnet + +# Run development server for test1 +yarn dev:test1 + +# Build for production +yarn build + +# Build for mainnet +yarn build:mainnet + +# Start production server +yarn start + +# Start production server for mainnet +yarn start:mainnet + +# Run linting +yarn lint + +# Run bundle analysis +yarn dev:analyze +``` + +## Directory Structure + +``` +ebridge-web/ +├── src/ # Source code directory +│ ├── api/ # API interface definitions and request handling +│ ├── assets/ # Static resources (images, fonts, etc.) +│ ├── components/ # Reusable React components +│ ├── constants/ # Constant definitions +│ ├── contexts/ # React Context related code +│ ├── contracts/ # Smart contract related code +│ ├── hooks/ # Custom React Hooks +│ ├── i18n/ # Internationalization config and translation files +│ ├── modals/ # Modal components +│ ├── page-components/ # Page-level components +│ ├── pages/ # Next.js page components +│ ├── styles/ # Global styles and theme configuration +│ ├── types/ # TypeScript type definitions +│ ├── utils/ # Utility functions +│ └── wagmiConnectors/ # Web3 wallet connection related code +├── public/ # Static resources directory +├── docs/ # Project documentation +└── ... # Other configuration files + +``` + +## Main Directory Descriptions + +### src/api +Contains all API interface definitions and network request handling logic. + +### src/components +Contains reusable React components that are used across multiple pages. + +### src/contexts +React Context related code for global state management. + +### src/contracts +Smart contract related code, including contract ABI and contract interaction logic. + +### src/hooks +Custom React Hooks that provide reusable state logic. + +### src/i18n +Internationalization configuration and translation files, supporting multiple languages. + +### src/modals +Modal components for displaying popups, dialogs, etc. + +### src/page-components +Page-level components that are typically associated with specific pages. + +### src/pages +Next.js page components that define the application's routing structure. + +### src/styles +Global style definitions and theme configuration. + +### src/types +TypeScript type definition files. + +### src/utils +General utility functions and helper methods. + +### src/wagmiConnectors +Web3 wallet connection related code for handling blockchain wallet interactions. + +## Configuration Files + +- `next.config.js`: Next.js configuration +- `tsconfig.json`: TypeScript configuration +- `.eslintrc.json`: ESLint configuration +- `.prettierrc`: Prettier code formatting configuration +- `vite.config.mts`: Vite configuration +- `vitest.setup.ts`: Vitest test configuration + +## Development Tools and Scripts + +The project uses multiple development tools and scripts to ensure code quality: + +- ESLint for code quality checking +- Prettier for code formatting +- Husky for Git hooks +- Vitest for unit testing +- Next.js bundle analyzer for performance optimization + +## Deployment + +The project includes a Dockerfile for containerized deployment. Additionally, automated build and deployment processes are implemented through GitHub Actions. + +## Environment Configuration + +The project supports multiple environments: +- Mainnet +- Test1 +- Development + +Each environment has corresponding rewrite configuration files: +- `rewrites.mainnet.js` +- `rewrites.testnet.js` +- `rewrites.test1.js` \ No newline at end of file diff --git a/docs/TEST_ARCHITECTURE.md b/docs/TEST_ARCHITECTURE.md new file mode 100644 index 00000000..9d30abce --- /dev/null +++ b/docs/TEST_ARCHITECTURE.md @@ -0,0 +1,122 @@ +# eBridge-web Test Architecture + +## Overview + +The project uses Vitest as the primary testing framework, along with React Testing Library for component testing. The test setup includes various configurations to handle browser-specific APIs and DOM manipulation. + +## Test Framework + +- **Vitest**: Main testing framework +- **React Testing Library**: For component testing +- **Jest DOM**: For DOM testing utilities +- **Happy DOM**: For browser environment simulation + +## Test Configuration + +The test configuration is primarily set up in `vitest.setup.ts`, which includes: + +1. DOM environment setup +2. Mock implementations for browser APIs: + - TextEncoder + - ResizeObserver + - matchMedia + - Canvas context +3. Global cleanup after each test +4. React global setup + +## Running Tests + +The project provides several npm scripts for running tests: + +```bash +# Run tests in watch mode (default) +yarn test + +# Run tests without watch mode +yarn test:not-watch + +# Update snapshots +yarn test:update + +# Run tests with coverage +yarn test:coverage + +# Run tests in CI environment +yarn test:ci +``` + +## Test Structure + +Tests should be placed in the same directory as the component or module they're testing, with the following naming convention: +- `*.test.ts` or `*.test.tsx` for component tests +- `*.spec.ts` or `*.spec.tsx` for utility/function tests + +## Test Dependencies + +Key testing dependencies in `package.json`: + +```json +{ + "devDependencies": { + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", + "@testing-library/react-hooks": "^8.0.1", + "@vitest/coverage-v8": "^3.0.5", + "happy-dom": "^17.1.0", + "vitest": "^3.0.5" + } +} +``` + +## Best Practices + +1. **Component Testing**: + - Use React Testing Library for component tests + - Focus on testing component behavior rather than implementation details + - Use meaningful test names that describe the test's purpose + +2. **Utility Testing**: + - Test pure functions with clear inputs and outputs + - Use appropriate test cases to cover edge cases + - Mock external dependencies when necessary + +3. **Test Coverage**: + - Aim for meaningful test coverage + - Focus on testing critical business logic + - Use coverage reports to identify untested areas + +4. **Mocking**: + - Use Vitest's built-in mocking capabilities + - Mock external services and APIs + - Keep mocks simple and focused + +## CI Integration + +Tests are automatically run in the CI environment using the `test:ci` script, which includes coverage reporting. The CI configuration ensures that: + +1. All tests pass before merging +2. Coverage thresholds are met +3. Test results are properly reported + +## Debugging Tests + +To debug tests: + +1. Use the `debug()` function from React Testing Library +2. Add `console.log` statements in test files +3. Use the `--inspect` flag with Vitest for Node.js debugging + +## Common Issues and Solutions + +1. **DOM Environment Issues**: + - Use the provided mock implementations in `vitest.setup.ts` + - Ensure proper cleanup after each test + +2. **Async Testing**: + - Use `waitFor` from React Testing Library + - Handle promises and async operations properly + +3. **Mocking Issues**: + - Ensure mocks are properly reset between tests + - Use appropriate mock implementations for different scenarios \ No newline at end of file diff --git a/feature-ut-badge-utils-test-results.json b/feature-ut-badge-utils-test-results.json new file mode 100644 index 00000000..783f58f0 --- /dev/null +++ b/feature-ut-badge-utils-test-results.json @@ -0,0 +1,6 @@ +{ + "schemaVersion": 1, + "label": "tests", + "message": "89 tests, 0 failures, 0 errors", + "color": "brightgreen" +} diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100755 index 00000000..4f11a03d --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next.config.js b/next.config.js new file mode 100755 index 00000000..5ce2d89c --- /dev/null +++ b/next.config.js @@ -0,0 +1,82 @@ +/* eslint-disable */ +/** @type {import('next').NextConfig} */ +const { NEXT_PUBLIC_PREFIX, ANALYZE, NODE_ENV } = process.env; +const withLess = require('next-with-less'); +const withPlugins = require('next-compose-plugins'); +const withBundleAnalyzer = require('@next/bundle-analyzer')({ + enabled: ANALYZE === 'true', +}); +const { rewriteConstants, getRewrites, rewriteEnv } = require('./rewriteENV'); +rewriteEnv(); +rewriteConstants(); +const plugins = [ + [withBundleAnalyzer], + [ + withLess, + { + lessLoaderOptions: { + lessOptions: { + modifyVars: { + '@app-prefix': NEXT_PUBLIC_PREFIX, + '@ant-prefix': NEXT_PUBLIC_PREFIX, + }, + }, + }, + }, + ], +]; + +const nextConfig = { + reactStrictMode: false, + swcMinify: false, + // webpack(config) { + // config.resolve.alias['bn.js'] = path.resolve(process.cwd(), 'node_modules', 'bn.js'); + // return config; + // }, + compiler: { + removeConsole: false, + }, + async rewrites() { + return getRewrites(); + }, + images: { + domains: [ + 'raw.githubusercontent.com', + 'forest-mainnet.s3.ap-northeast-1.amazonaws.com', + 'forest-testnet.s3.ap-northeast-1.amazonaws.com', + 'portkey-did.s3.ap-northeast-1.amazonaws.com', + ], + }, +}; + +const productionConfig = { + ...nextConfig, + swcMinify: false, + compiler: { + removeConsole: { + exclude: ['error'], + }, + }, + experimental: { + 'react-use': { + transform: 'react-use/lib/{{member}}', + }, + lodash: { + transform: 'lodash/{{member}}', + }, + }, + resolve: {}, +}; + +const withTM = require('next-transpile-modules')([ + 'antd-mobile', + '@aelf-web-login/wallet-adapter-base', + '@aelf-web-login/wallet-adapter-react', + '@aelf-web-login/wallet-adapter-bridge', + '@portkey/did-ui-react', +]); + +module.exports = withPlugins( + plugins, + ANALYZE === 'true' || process.env.NEXT_PUBLIC_APP_ENV === 'mainnet' ? withTM(productionConfig) : withTM(nextConfig), +); diff --git a/package.json b/package.json new file mode 100644 index 00000000..ad1cf995 --- /dev/null +++ b/package.json @@ -0,0 +1,129 @@ +{ + "name": "ebridge-web", + "version": "2.13.3", + "private": true, + "scripts": { + "dev": "PORT=3000 next dev", + "dev:mainnet": "PORT=4200 ENV_NAME=mainnet next dev", + "dev:test1": "ENV_NAME=test1 next dev", + "build": "next build", + "build:mainnet": "ENV_NAME=mainnet next build", + "start": "next start", + "start:mainnet": "ENV_NAME=mainnet next start", + "lint": "next lint", + "dev:analyze": "PORT=4000 ANALYZE=true next dev", + "postinstall": "bash scripts/postinstall.sh", + "test": "vitest", + "test:not-watch": "vitest run", + "test:update": "vitest -u", + "test:coverage": "vitest --coverage", + "test:ci": "vitest --coverage" + }, + "dependencies": { + "@aelf-react/core": "^0.1.19", + "@aelf-react/types": "^0.1.10", + "@aelf-web-login/utils": "^0.4.0-alpha.10", + "@aelf-web-login/wallet-adapter-base": "^0.4.0-alpha.10", + "@aelf-web-login/wallet-adapter-bridge": "^0.4.0-alpha.10", + "@aelf-web-login/wallet-adapter-fairy-vault-discover": "^0.4.0-alpha.10", + "@aelf-web-login/wallet-adapter-night-elf": "^0.4.0-alpha.10", + "@aelf-web-login/wallet-adapter-portkey-discover": "^0.4.0-alpha.10", + "@aelf-web-login/wallet-adapter-portkey-web": "^0.4.0-alpha.10", + "@aelf-web-login/wallet-adapter-react": "^0.4.0-alpha.10", + "@ant-design/icons": "^5.3.0", + "@apollo/client": "^3.7.3", + "@portkey/contracts": "^2.19.0-alpha.10", + "@portkey/did": "^2.19.0-alpha.24", + "@portkey/did-ui-react": "^2.19.0-alpha.10", + "@portkey/types": "^2.19.0-alpha.10", + "@portkey/utils": "^2.19.0-alpha.10", + "@sentry/nextjs": "^7.48.0", + "@tanstack/query-core": "^5.62.7", + "@tanstack/react-query": "^5.62.7", + "@ton/core": "^0.59.0", + "@ton/crypto": "^3.3.0", + "@tonconnect/ui-react": "^2.0.9", + "@walletconnect/ethereum-provider": "^2.9.2", + "@walletconnect/modal-ui": "git+https://github.com/kled-bridge/modal-ui#37b7aedc0aa659d44f8abb72e56f75108940732f", + "aelf-bridge": "git+https://github.com/mason-hz/aelf-bridge", + "aelf-sdk": "^3.4.15", + "aelf-sdk-cross-chain": "^1.0.14", + "antd": "^4.21.7", + "axios": "^1.5.0", + "bignumber.js": "^9.1.0", + "clsx": "^1.2.1", + "graphql": "^16.8.1", + "i18next": "^21.8.13", + "lodash": "^4.17.21", + "next": "12.2.0", + "next-compose-plugins": "^2.2.1", + "next-plugin-antd-less": "^1.8.0", + "next-transpile-modules": "^10.0.1", + "next-with-less": "^2.0.5", + "query-string": "^7.1.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-ga": "^3.3.1", + "react-i18next": "^11.18.0", + "react-infinite-scroll-component": "^6.1.0", + "react-use": "^17.4.0", + "tonweb": "^0.0.66", + "viem": "2.29.x", + "wagmi": "2.11.2", + "web3": "^1.7.4", + "web3-core": "^1.7.4" + }, + "devDependencies": { + "@coinbase/wallet-sdk": "^3.3.0", + "@commitlint/cli": "^17.6.1", + "@commitlint/config-conventional": "^17.6.1", + "@next/bundle-analyzer": "^12.2.4", + "@next/eslint-plugin-next": "^13.4.1", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", + "@testing-library/react-hooks": "^8.0.1", + "@types/jest": "^29.5.14", + "@types/lodash": "^4.14.201", + "@types/node": "18.0.1", + "@types/react": "18.2.51", + "@types/react-dom": "^18.2.18", + "@types/testing-library__jest-dom": "^6.0.0", + "@typescript-eslint/eslint-plugin": "^5.30.4", + "@typescript-eslint/parser": "^5.30.4", + "@vitejs/plugin-react": "^4.3.4", + "@vitest/coverage-v8": "^3.0.5", + "eslint": "8.19.0", + "eslint-config-next": "12.2.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.2.1", + "happy-dom": "^17.1.0", + "husky": "^8.0.3", + "less": "^4.1.3", + "less-loader": "^11.0.0", + "lint-staged": "^13.2.1", + "postcss-less": "^6.0.0", + "prettier": "^2.7.1", + "stylelint": "^14.9.1", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-standard": "^26.0.0", + "typescript": "5.0.4", + "vconsole": "^3.14.6", + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^3.0.5" + }, + "engines": { + "node": ">=18.0.0" + }, + "lint-staged": { + "*.{js,jsx,ts,tsx}": "eslint --cache --fix", + "*.{css,less}": "stylelint --fix" + }, + "resolutions": { + "@types/react": "18.2.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "viem": "2.29.x", + "@walletconnect/modal-ui": "git+https://github.com/kled-bridge/modal-ui#37b7aedc0aa659d44f8abb72e56f75108940732f" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..f5d998c9 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/font/Poppins-Bold.ttf b/public/font/Poppins-Bold.ttf new file mode 100644 index 00000000..00559eeb Binary files /dev/null and b/public/font/Poppins-Bold.ttf differ diff --git a/public/font/Poppins-Medium.ttf b/public/font/Poppins-Medium.ttf new file mode 100644 index 00000000..6bcdcc27 Binary files /dev/null and b/public/font/Poppins-Medium.ttf differ diff --git a/public/font/Poppins-Regular.ttf b/public/font/Poppins-Regular.ttf new file mode 100644 index 00000000..9f0c71b7 Binary files /dev/null and b/public/font/Poppins-Regular.ttf differ diff --git a/public/tonconnect-manifest.json b/public/tonconnect-manifest.json new file mode 100644 index 00000000..ae53d7da --- /dev/null +++ b/public/tonconnect-manifest.json @@ -0,0 +1,7 @@ +{ + "url": "https://ebridge.exchange", + "name": "eBridge", + "iconUrl": "https://ebridge.exchange/favicon.ico", + "termsOfUseUrl": "https://ebridge.exchange/teamOfService", + "privacyPolicyUrl": "https://ebridge.exchange/privacyPolicy" + } \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100755 index 00000000..fbf0e25a --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/rewriteENV.js b/rewriteENV.js new file mode 100644 index 00000000..256db2e1 --- /dev/null +++ b/rewriteENV.js @@ -0,0 +1,36 @@ +/* eslint-disable */ +const path = require('path'); +const fs = require('fs'); + +function rewriteConstants() { + const appEnv = process.env.NEXT_PUBLIC_APP_ENV || 'testnet'; + const IS_MAINNET = appEnv === 'mainnet'; + const constantsPath = path.resolve(__dirname, `./src/constants/index.ts`); + fs.writeFileSync( + constantsPath, + `export const IS_MAINNET = ${IS_MAINNET};\n +export * from './${appEnv}';\n`, + ); +} + +function getRewrites() { + const appEnv = process.env.NEXT_PUBLIC_APP_ENV || 'testnet'; + return require(`./rewrites.${appEnv}`); +} + +function rewriteEnv() { + const name = process.env.ENV_NAME; + try { + const data = fs.readFileSync(`.env.${name}.local`, 'utf8'); + data.split('\n').forEach(function (item) { + const [key, value] = item.split('='); + process.env[key.trim()] = value.trim(); + }); + } catch (error) { + console.log(`Failed to load environment variables from file ${name}`); + } +} + +exports.rewriteConstants = rewriteConstants; +exports.getRewrites = getRewrites; +exports.rewriteEnv = rewriteEnv; diff --git a/rewrites.mainnet.js b/rewrites.mainnet.js new file mode 100644 index 00000000..9e6e8048 --- /dev/null +++ b/rewrites.mainnet.js @@ -0,0 +1,9 @@ +const host = 'https://ebridge.exchange'; +module.exports = [ + { source: '/api/:path*', destination: `${host}/api/:path*` }, + { source: '/cms/:path*', destination: `https://cms.ebridge.exchange/:path*` }, + { + source: '/AElfIndexer_eBridge/:path*', + destination: `https://dapp-indexer.ebridge.exchange/AElfIndexer_eBridge/:path*`, + }, +]; diff --git a/rewrites.test1.js b/rewrites.test1.js new file mode 100644 index 00000000..b7f3223b --- /dev/null +++ b/rewrites.test1.js @@ -0,0 +1,6 @@ +const host = 'http://192.168.66.78:7030'; +module.exports = [ + { source: '/api/:path*', destination: `${host}/api/:path*` }, + { source: '/cms/:path*', destination: `https://cms.ebridge.exchange/:path*` }, + { source: '/AElfIndexer_eBridge/:path*', destination: `http://192.168.66.78:7022/AElfIndexer_eBridge/:path*` }, +]; diff --git a/rewrites.testnet.js b/rewrites.testnet.js new file mode 100644 index 00000000..82d05ce2 --- /dev/null +++ b/rewrites.testnet.js @@ -0,0 +1,9 @@ +const host = 'https://test.ebridge.exchange'; +module.exports = [ + { source: '/api/:path*', destination: `${host}/api/:path*` }, + { source: '/cms/:path*', destination: `https://test-cms.ebridge.exchange/:path*` }, + { + source: '/AElfIndexer_eBridge/:path*', + destination: `https://test-indexer.ebridge.exchange/AElfIndexer_eBridge/:path*`, + }, +]; diff --git a/scripts/fix-web-login.js b/scripts/fix-web-login.js new file mode 100644 index 00000000..550d0db6 --- /dev/null +++ b/scripts/fix-web-login.js @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const fs = require('fs'); +const path = require('path'); +const filePath = path.resolve( + __dirname, + `../node_modules/@aelf-web-login/wallet-adapter-portkey-web/dist/esm/index.js`, +); + +fs.readFile(filePath, 'utf-8', (err, data) => { + if (err) throw err; + let newData = data.replace(`import '@portkey/connect-web-wallet/dist/assets/index.css';`, ''); + fs.writeFile(filePath, newData, (err) => { + if (err) throw err; + console.log('The file has been updated'); + }); +}); diff --git a/scripts/postinstall.sh b/scripts/postinstall.sh new file mode 100644 index 00000000..791dff1c --- /dev/null +++ b/scripts/postinstall.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +husky install +echo "husky install success" + +node scripts/fix-web-login.js +echo "fix fix-web-login success" + diff --git a/src/api/__tests__/index.test.ts b/src/api/__tests__/index.test.ts new file mode 100644 index 00000000..fe3c04be --- /dev/null +++ b/src/api/__tests__/index.test.ts @@ -0,0 +1,109 @@ +import { describe, expect, test, vi } from 'vitest'; +import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'; +import { baseRequest, request, requestGql } from 'api/index'; +import { service, spliceUrl } from 'api/utils'; +import myServer from 'api/server'; +import { IBaseRequest } from 'api/types'; + +// Mock dependencies +vi.mock('api/utils', () => ({ + service: vi.fn(), + spliceUrl: vi.fn(), +})); + +vi.mock('api/server', () => { + return { + default: { + parseRouter: vi.fn(), + base: {}, + }, + }; +}); + +vi.mock('api/list', () => ({ + BASE_APIS: { test: 'test' }, + BASE_REQ_TYPES: {}, + DEFAULT_METHOD: 'GET', + EXPAND_APIS: { expand: 'expand' }, + EXPAND_REQ_TYPES: {}, +})); + +// Mock @apollo/client +vi.mock('@apollo/client', () => ({ + ApolloClient: vi.fn(), + HttpLink: vi.fn(), + InMemoryCache: vi.fn(), +})); + +describe('baseRequest', () => { + test('should call service with correct parameters', async () => { + const mockResponse = { data: 'success' }; + (service as any).mockResolvedValue(mockResponse); + + const config: IBaseRequest = { + url: 'https://example.com', + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }; + + const result = await baseRequest(config); + + expect(spliceUrl).toHaveBeenCalledWith(config.url, config.query); + expect(service).toHaveBeenCalled(); + expect(result).toEqual(mockResponse); + }); + + test('should use default method if method is not provided', async () => { + const mockResponse = { data: 'success' }; + (service as any).mockResolvedValue(mockResponse); + + const config: IBaseRequest = { + url: 'https://example.com', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }; + + const result = await baseRequest(config); + + expect(spliceUrl).toHaveBeenCalledWith(config.url, config.query); + expect(service).toHaveBeenCalled(); + expect(result).toEqual(mockResponse); + }); +}); + +describe('request', () => { + test('should assign base and expand requests correctly', () => { + const baseRequestMock = { test: vi.fn() }; + const expandRequestMock = { expand: vi.fn() }; + + vi.mocked(myServer).base = baseRequestMock; + vi.mocked(myServer).expand = expandRequestMock; + + expect(request).toHaveProperty('parseRouter'); + expect(request).toHaveProperty('base'); + }); +}); + +describe('requestGql', () => { + test('should create ApolloClient with correct configuration', () => { + const uri = 'https://example.com/graphql'; + const client = requestGql({ uri }); + + expect(ApolloClient).toHaveBeenCalledWith({ + cache: new (InMemoryCache as any)(), + queryDeduplication: false, + defaultOptions: { + watchQuery: { + fetchPolicy: 'cache-and-network', + }, + query: { + fetchPolicy: 'network-only', + }, + }, + link: new (HttpLink as any)({ uri }), + }); + + expect(client).toBeInstanceOf(ApolloClient); + }); +}); diff --git a/src/api/__tests__/server/index.test.ts b/src/api/__tests__/server/index.test.ts new file mode 100644 index 00000000..0a4d2cfb --- /dev/null +++ b/src/api/__tests__/server/index.test.ts @@ -0,0 +1,216 @@ +import { describe, expect, test, vi } from 'vitest'; +import myServer from 'api/server'; +import { DEFAULT_METHOD } from 'api/list'; +import { BaseConfig, UrlObj, RequestConfig } from 'api/types'; +import { service, getRequestConfig } from 'api/utils'; + +// Mock dependencies +vi.mock('api/utils', () => ({ + service: vi.fn(), + spliceUrl: vi.fn(), + getRequestConfig: vi.fn(), +})); + +vi.mock('api/list', () => ({ + DEFAULT_METHOD: 'GET', +})); + +describe('myServer', () => { + let serverInstance: any; + + beforeEach(() => { + serverInstance = Object.create(myServer); + vi.clearAllMocks(); + }); + + describe('parseRouter', () => { + test('should create methods on the server instance', () => { + const name = 'test'; + const urlObj: UrlObj = { + method1: { target: 'https://example.com/method1', baseConfig: {} }, + method2: { target: 'https://example.com/method2', baseConfig: {} }, + }; + + serverInstance.parseRouter(name, urlObj); + + expect(serverInstance[name]).toBeDefined(); + expect(serverInstance[name].method1).toBeInstanceOf(Function); + expect(serverInstance[name].method2).toBeInstanceOf(Function); + }); + + test('should bind send method with correct base config', () => { + const name = 'test'; + const urlObj: UrlObj = { + method1: { target: 'https://example.com/method1', baseConfig: {} }, + }; + + serverInstance.parseRouter(name, urlObj); + + expect(serverInstance[name].method1).toBeInstanceOf(Function); + }); + }); + + describe('send', () => { + test('should call service with correct parameters', async () => { + const base: BaseConfig = { target: 'https://example.com', baseConfig: {} }; + const config: RequestConfig = { + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }; + const mockResponse = { data: 'success' }; + + (getRequestConfig as any).mockReturnValue({ + method: 'POST', + query: 'test=1', + url: 'https://example.com', + headers: { 'Content-Type': 'application/json' }, + }); + + (service as any).mockResolvedValue(mockResponse); + + const result = await serverInstance.send(base, config); + + expect(getRequestConfig).toHaveBeenCalledWith(base, config); + expect(service).toHaveBeenCalledWith({ + method: 'POST', + url: 'https://example.com', + headers: { 'Content-Type': 'application/json' }, + }); + expect(result).toEqual(mockResponse); + }); + + test('should use default method if method is not provided', async () => { + const base: BaseConfig = { target: 'https://example.com', baseConfig: {} }; + const config: RequestConfig = { query: 'test=1', headers: { 'Content-Type': 'application/json' } }; + const mockResponse = { data: 'success' }; + + (getRequestConfig as any).mockReturnValue({ + method: DEFAULT_METHOD, + query: 'test=1', + url: 'https://example.com', + headers: { 'Content-Type': 'application/json' }, + }); + + (service as any).mockResolvedValue(mockResponse); + + const result = await serverInstance.send(base, config); + + expect(getRequestConfig).toHaveBeenCalledWith(base, config); + expect(service).toHaveBeenCalledWith({ + method: DEFAULT_METHOD, + url: 'https://example.com', + headers: { 'Content-Type': 'application/json' }, + }); + expect(result).toEqual(mockResponse); + }); + + test('should handle string base correctly', async () => { + const base: BaseConfig = 'https://example.com'; + const config: RequestConfig = { + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }; + const mockResponse = { data: 'success' }; + + (getRequestConfig as any).mockReturnValue({ + method: 'POST', + query: 'test=1', + url: 'https://example.com', + headers: { 'Content-Type': 'application/json' }, + }); + + (service as any).mockResolvedValue(mockResponse); + + const result = await serverInstance.send(base, config); + + expect(getRequestConfig).toHaveBeenCalledWith(base, config); + expect(service).toHaveBeenCalledWith({ + method: 'POST', + url: 'https://example.com', + headers: { 'Content-Type': 'application/json' }, + }); + expect(result).toEqual(mockResponse); + }); + + test('should executes successfully when the config not have url and base is string', async () => { + const base: BaseConfig = 'https://example.com'; + const config: RequestConfig = { + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }; + const mockResponse = { data: 'success' }; + + (getRequestConfig as any).mockReturnValue({ + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }); + + (service as any).mockResolvedValue(mockResponse); + + const result = await serverInstance.send(base, config); + + expect(getRequestConfig).toHaveBeenCalledWith(base, config); + expect(service).toHaveBeenCalledWith({ + method: 'POST', + url: undefined, + headers: { 'Content-Type': 'application/json' }, + }); + expect(result).toEqual(mockResponse); + }); + + test('should executes successfully when the config not have url and base have target', async () => { + const base: BaseConfig = { target: 'https://example.com', baseConfig: {} }; + const config: RequestConfig = { + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }; + const mockResponse = { data: 'success' }; + + (getRequestConfig as any).mockReturnValue({ + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }); + + (service as any).mockResolvedValue(mockResponse); + + const result = await serverInstance.send(base, config); + + expect(getRequestConfig).toHaveBeenCalledWith(base, config); + expect(service).toHaveBeenCalledWith({ + method: 'POST', + url: undefined, + headers: { 'Content-Type': 'application/json' }, + }); + expect(result).toEqual(mockResponse); + }); + + test('should executes successfully when the getRequestConfig method returns undefined', async () => { + const base: BaseConfig = { target: 'https://example.com', baseConfig: {} }; + const config: RequestConfig = { + method: 'POST', + query: 'test=1', + headers: { 'Content-Type': 'application/json' }, + }; + const mockResponse = { data: 'success' }; + + (getRequestConfig as any).mockReturnValue(undefined); + + (service as any).mockResolvedValue(mockResponse); + + const result = await serverInstance.send(base, config); + + expect(getRequestConfig).toHaveBeenCalledWith(base, config); + expect(service).toHaveBeenCalledWith({ + method: 'GET', + url: undefined, + }); + expect(result).toEqual(mockResponse); + }); + }); +}); diff --git a/src/api/__tests__/utils.test.ts b/src/api/__tests__/utils.test.ts new file mode 100644 index 00000000..d1507f1b --- /dev/null +++ b/src/api/__tests__/utils.test.ts @@ -0,0 +1,204 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { describe, expect, test, vi } from 'vitest'; +import { service, spliceUrl, getRequestConfig, isDeniedRequest } from 'api/utils'; + +// Mock eBridgeEventBus and axios +vi.mock('utils/eBridgeEventBus'); + +vi.mock('axios', () => { + const BASE_URL = 'https://ebridge.exchange'; + return { + default: { + create: vi.fn().mockReturnValue({ + interceptors: { + request: { + use: vi.fn().mockImplementation((config: any) => { + config(); + }), + handlers: [ + { + fulfilled: vi.fn().mockImplementation((config: any) => { + return config; + }), + rejected: vi.fn(), + }, + ], + }, + response: { + use: vi.fn().mockImplementation((response: any) => { + response({ data: { code: '200' } }); + }), + }, + }, + post: vi.fn().mockImplementation((config: any) => { + return config; + }), + get: vi.fn().mockImplementation((config: any) => { + return config; + }), + defaults: { + baseURL: BASE_URL, + timeout: 50000, + }, + }), + }, + }; +}); + +const BASE_URL = 'https://ebridge.exchange'; + +describe('service', () => { + test('should create axios instance with correct baseURL and timeout', () => { + expect(service.defaults.baseURL).toBe(BASE_URL); + expect(service.defaults.timeout).toBe(50000); + }); + + test('should pass configuration through the GET request', async () => { + const config = { + url: '/test', + method: 'get', + headers: { + 'Content-Type': 'application/json', + }, + }; + + const response = await service.get(config.url); + expect(response).toEqual('/test'); + }); + + test('should pass configuration through the POST request', async () => { + const config = { + url: '/test', + method: 'post', + headers: { + 'Content-Type': 'application/json', + }, + }; + + const response = await service.post(config.url); + expect(response).toEqual('/test'); + }); +}); + +describe('isDeniedRequest', () => { + test('returns true if error message contains "401"', () => { + const error = { message: 'Request failed with status code 401' }; + const result = isDeniedRequest(error); + expect(result).toBe(true); + }); + + test('returns false if error message does not contain "401"', () => { + const error = { message: 'Request failed with status code 404' }; + const result = isDeniedRequest(error); + expect(result).toBe(false); + }); + + test('handles errors without message property', () => { + // @ts-ignore + const error = {}; + const result = isDeniedRequest(error as any); + expect(result).toBe(false); + }); + + test('logs warnings if an error occurs within isDeniedRequest', () => { + // Mock console.log + vi.spyOn(console, 'log').mockImplementation(() => {}); + + // @ts-ignore + isDeniedRequest(null); + expect(console.log).toHaveBeenCalled(); + }); +}); + +describe('spliceUrl', () => { + test('should return baseUrl when extendArg is not provided', () => { + const baseUrl = 'https://example.com'; + const result = spliceUrl(baseUrl); + + expect(result).toBe(baseUrl); + }); + + test('should return baseUrl with extendArg appended', () => { + const baseUrl = 'https://example.com'; + const extendArg = 'test'; + const result = spliceUrl(baseUrl, extendArg); + + expect(result).toBe(`${baseUrl}/${extendArg}`); + }); +}); + +describe('getRequestConfig', () => { + test('should return config when base is a string', () => { + const base = 'https://example.com'; + const config = { method: 'GET', params: { id: 1 } }; + const result = getRequestConfig(base, config); + + expect(result).toEqual(config); + }); + + test('should merge baseConfig and config correctly', () => { + const base = { + target: '', + baseConfig: { + method: 'POST', + params: { page: 1 }, + data: { key: 'value' }, + query: '?sort=asc', + }, + }; + const config = { + method: 'GET', + params: { id: 1 }, + data: { newKey: 'newValue' }, + query: '&filter=true', + }; + const result = getRequestConfig(base, config); + + expect(result).toEqual({ + ...config, + ...base.baseConfig, + query: `${base.baseConfig.query}${config.query}`, + method: config.method, + params: { ...base.baseConfig.params, ...config.params }, + data: { ...base.baseConfig.data, ...config.data }, + }); + }); + + test('should handle undefined base and config', () => { + const result = getRequestConfig(undefined as any, undefined); + + expect(result).toEqual({ + data: {}, + method: undefined, + params: {}, + query: '', + }); + }); + + test('should handle undefined baseConfig', () => { + const base = { target: '/test', baseConfig: {} }; + const config = { method: 'GET', params: { id: 1 } }; + const result = getRequestConfig(base, config); + + expect(result).toEqual({ + data: {}, + ...config, + query: '', + }); + }); + + test('should handle undefined config', () => { + const base = { + target: '/test', + baseConfig: { + method: 'POST', + params: { page: 1 }, + data: { key: 'value' }, + query: '?sort=asc', + }, + }; + const result = getRequestConfig(base); + + expect(result).toEqual(base.baseConfig); + }); +}); diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 00000000..23b25584 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,42 @@ +import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'; +import { BASE_APIS, BASE_REQ_TYPES, DEFAULT_METHOD, EXPAND_APIS, EXPAND_REQ_TYPES } from './list'; +import myServer from './server'; +import { IBaseRequest } from './types'; +import { spliceUrl, service } from './utils'; + +function baseRequest({ url, method = DEFAULT_METHOD, query = '', ...c }: IBaseRequest) { + return service({ + ...c, + url: spliceUrl(url, query), + method, + }); +} + +myServer.parseRouter('base', BASE_APIS); + +Object.entries(EXPAND_APIS).forEach(([key, value]) => { + myServer.parseRouter(key, value); +}); + +const request: BASE_REQ_TYPES & EXPAND_REQ_TYPES = Object.assign({}, myServer.base, myServer); + +const requestGql = ({ uri }: { uri: string }) => { + return new ApolloClient({ + cache: new InMemoryCache(), + queryDeduplication: false, + defaultOptions: { + watchQuery: { + fetchPolicy: 'cache-and-network', + }, + query: { + fetchPolicy: 'network-only', + }, + // ...defaultOptions, + }, + link: new HttpLink({ + uri, + }), + }); +}; + +export { baseRequest, request, requestGql }; diff --git a/src/api/list.ts b/src/api/list.ts new file mode 100644 index 00000000..9392cdc0 --- /dev/null +++ b/src/api/list.ts @@ -0,0 +1,90 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { API_REQ_FUNCTION, UrlObj } from './types'; + +export const DEFAULT_METHOD = 'GET'; + +/** + * api request configuration directory + * @example + * upload: { + * target: '/api/file-management/file-descriptor/upload', + * baseConfig: { method: 'POST', }, + * }, + * or: + * upload:'/api/file-management/file-descriptor/upload' + * + * @description api configuration default method is from DEFAULT_METHOD + * @type {UrlObj} // The type of this object from UrlObj. + */ +export const BASE_APIS = { + getCurrentBlockHeight: '', +}; + +const CrossApiList = { + getCrossChainTransfers: '/api/app/cross-chain-transfers', + getCrossChainIndexing: '/api/app/cross-chain-indexing/progress', + getDailyLimits: 'api/app/limiter/dailyLimits', + getRateLimits: 'api/app/limiter/rateLimits', +}; + +const ApplicationApiList = { + getTokenList: '/api/ebridge/application/tokens', + getTokenDetail: '/api/ebridge/application/token-detail', + commitTokenInfo: { + target: '/api/ebridge/application/commit-basic-info', + baseConfig: { method: 'POST' }, + }, + getTokenInfo: '/api/ebridge/application/user-token-access-info', + getChainStatus: '/api/ebridge/application/check-chain-access-status', + addChain: { + target: '/api/ebridge/application/add-chain', + baseConfig: { method: 'POST' }, + }, + prepareBindIssue: { + target: '/api/ebridge/application/prepare-binding-issue', + baseConfig: { method: 'POST' }, + }, + getIssue: { + target: '/api/ebridge/application/issue-binding', + baseConfig: { method: 'POST' }, + }, + getMyApplicationList: '/api/ebridge/application/list', + getApplicationDetail: '/api/ebridge/application/detail', + getTokenConfig: '/api/ebridge/application/token/config', +}; + +const CommonApiList = { + getTokenWhiteList: '/api/ebridge/application/token-white-list', + getTokenPrice: '/api/ebridge/application/token/price', +}; + +const CMSApiList = { + getToggleResultOfMask: '/cms/items/home', +}; + +const PoolApiList = { + overview: '/api/ebridge/application/pool-overview', + detail: '/api/ebridge/application/pool-detail', + list: '/api/ebridge/application/pool-list', +}; +/** + * api request extension configuration directory + * @description object.key // The type of this object key comes from from @type {UrlObj} + */ +export const EXPAND_APIS = { + cross: CrossApiList, + cms: CMSApiList, + application: ApplicationApiList, + common: CommonApiList, + pool: PoolApiList, +}; + +export type BASE_REQ_TYPES = { + [x in keyof typeof BASE_APIS]: API_REQ_FUNCTION; +}; + +export type EXPAND_REQ_TYPES = { + [X in keyof typeof EXPAND_APIS]: { + [K in keyof (typeof EXPAND_APIS)[X]]: API_REQ_FUNCTION; + }; +}; diff --git a/src/api/server/index.ts b/src/api/server/index.ts new file mode 100644 index 00000000..b5aab143 --- /dev/null +++ b/src/api/server/index.ts @@ -0,0 +1,34 @@ +import { DEFAULT_METHOD } from 'api/list'; +import { BaseConfig, UrlObj, RequestConfig } from 'api/types'; +import { spliceUrl, service, getRequestConfig } from 'api/utils'; + +const myServer = new Function(); + +/** + * @method parseRouter + * @param {string} name + * @param {UrlObj} urlObj + */ +myServer.prototype.parseRouter = function (name: string, urlObj: UrlObj) { + const obj: any = (this[name] = {}); + Object.keys(urlObj).forEach((key) => { + obj[key] = this.send.bind(this, urlObj[key]); + }); +}; + +/** + * @method send + * @param {BaseConfig} base + * @param {object} config + * @return {Promise} + */ +myServer.prototype.send = function (base: BaseConfig, config: RequestConfig) { + const { method = DEFAULT_METHOD, query = '', url, ...axiosConfig } = getRequestConfig(base, config) || {}; + return service({ + ...axiosConfig, + url: url || spliceUrl(typeof base === 'string' ? base : base.target, query), + method, + }); +}; + +export default myServer.prototype; diff --git a/src/api/types.ts b/src/api/types.ts new file mode 100644 index 00000000..db941843 --- /dev/null +++ b/src/api/types.ts @@ -0,0 +1,15 @@ +import { AxiosRequestConfig, AxiosResponse } from 'axios'; + +export type RequestConfig = { + query?: string; //this for url parameter; example: test/:id +} & AxiosRequestConfig; + +export type IBaseRequest = { + url: string; +} & RequestConfig; + +export type BaseConfig = string | { target: string; baseConfig: RequestConfig }; + +export type UrlObj = { [key: string]: BaseConfig }; + +export type API_REQ_FUNCTION = (config?: RequestConfig) => Promise>; diff --git a/src/api/utils.ts b/src/api/utils.ts new file mode 100644 index 00000000..d0aee870 --- /dev/null +++ b/src/api/utils.ts @@ -0,0 +1,65 @@ +import axios from 'axios'; +import { BaseConfig, RequestConfig } from './types'; +import { BASE_URL } from 'constants/index'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; + +export const isDeniedRequest = (error: { message: string }) => { + try { + const message: string = error.message; + if (message?.includes('401')) return true; + } catch (error) { + console.log(error); + } + return false; +}; + +const axiosInstance = axios.create({ + baseURL: BASE_URL, + timeout: 50000, +}); + +axiosInstance.interceptors.request.use( + (config) => { + return config; + }, + (error) => { + Promise.reject(error); + }, +); + +axiosInstance.interceptors.response.use( + (response) => { + const res = response.data; + return res; + }, + (error) => { + console.log('api error', error, isDeniedRequest(error)); + if (isDeniedRequest(error)) { + eBridgeEventBus.Unauthorized.emit(); + } + return Promise.reject(error); + }, +); + +export const service = axiosInstance; + +export function spliceUrl(baseUrl: string, extendArg?: string) { + return extendArg ? baseUrl + '/' + extendArg : baseUrl; +} + +export function getRequestConfig(base: BaseConfig, config?: RequestConfig) { + if (typeof base === 'string') { + return config; + } else { + const { baseConfig } = base || {}; + const { query, method, params, data } = config || {}; + return { + ...config, + ...baseConfig, + query: (baseConfig?.query || '') + (query || ''), + method: method ? method : baseConfig?.method, + params: Object.assign({}, baseConfig?.params, params), + data: Object.assign({}, baseConfig?.data, data), + }; + } +} diff --git a/src/assets/images/arrow-blue.svg b/src/assets/images/arrow-blue.svg new file mode 100644 index 00000000..3ee8488e --- /dev/null +++ b/src/assets/images/arrow-blue.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/arrow-gray.svg b/src/assets/images/arrow-gray.svg new file mode 100644 index 00000000..e5627d17 --- /dev/null +++ b/src/assets/images/arrow-gray.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/arrow.svg b/src/assets/images/arrow.svg new file mode 100644 index 00000000..0c7a1eb4 --- /dev/null +++ b/src/assets/images/arrow.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/arrow_right_gray.svg b/src/assets/images/arrow_right_gray.svg new file mode 100644 index 00000000..ce11d7c7 --- /dev/null +++ b/src/assets/images/arrow_right_gray.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/arrow_right_white.svg b/src/assets/images/arrow_right_white.svg new file mode 100644 index 00000000..d648d433 --- /dev/null +++ b/src/assets/images/arrow_right_white.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/back.svg b/src/assets/images/back.svg new file mode 100644 index 00000000..497c53b8 --- /dev/null +++ b/src/assets/images/back.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/background.png b/src/assets/images/background.png new file mode 100644 index 00000000..dd21266d Binary files /dev/null and b/src/assets/images/background.png differ diff --git a/src/assets/images/bg-mobile.png b/src/assets/images/bg-mobile.png new file mode 100644 index 00000000..3fe90014 Binary files /dev/null and b/src/assets/images/bg-mobile.png differ diff --git a/src/assets/images/bg-pc.png b/src/assets/images/bg-pc.png new file mode 100644 index 00000000..99ed9502 Binary files /dev/null and b/src/assets/images/bg-pc.png differ diff --git a/src/assets/images/bridge-blue.svg b/src/assets/images/bridge-blue.svg new file mode 100644 index 00000000..34a711b6 --- /dev/null +++ b/src/assets/images/bridge-blue.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/bridge.svg b/src/assets/images/bridge.svg new file mode 100644 index 00000000..4fdb9d31 --- /dev/null +++ b/src/assets/images/bridge.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/change.svg b/src/assets/images/change.svg new file mode 100644 index 00000000..9c0dc45f --- /dev/null +++ b/src/assets/images/change.svg @@ -0,0 +1,70 @@ + + + change + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/check-blue.svg b/src/assets/images/check-blue.svg new file mode 100644 index 00000000..16c01790 --- /dev/null +++ b/src/assets/images/check-blue.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/check-filled.svg b/src/assets/images/check-filled.svg new file mode 100644 index 00000000..cd119b89 --- /dev/null +++ b/src/assets/images/check-filled.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/checkFilled16.svg b/src/assets/images/checkFilled16.svg new file mode 100644 index 00000000..2ba1e5d5 --- /dev/null +++ b/src/assets/images/checkFilled16.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/clear-icon.svg b/src/assets/images/clear-icon.svg new file mode 100644 index 00000000..37f83710 --- /dev/null +++ b/src/assets/images/clear-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/close.svg b/src/assets/images/close.svg new file mode 100644 index 00000000..7afc1600 --- /dev/null +++ b/src/assets/images/close.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/closeFilled16.svg b/src/assets/images/closeFilled16.svg new file mode 100644 index 00000000..f67a2691 --- /dev/null +++ b/src/assets/images/closeFilled16.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/coinbaseWalletIcon.svg b/src/assets/images/coinbaseWalletIcon.svg new file mode 100644 index 00000000..04e9f49b --- /dev/null +++ b/src/assets/images/coinbaseWalletIcon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/community.svg b/src/assets/images/community.svg new file mode 100644 index 00000000..cfdba9d9 --- /dev/null +++ b/src/assets/images/community.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/complete.svg b/src/assets/images/complete.svg new file mode 100644 index 00000000..1612a12d --- /dev/null +++ b/src/assets/images/complete.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/discord-dark.svg b/src/assets/images/discord-dark.svg new file mode 100644 index 00000000..449bb817 --- /dev/null +++ b/src/assets/images/discord-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/discord.svg b/src/assets/images/discord.svg new file mode 100644 index 00000000..9c5021d7 --- /dev/null +++ b/src/assets/images/discord.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/down.svg b/src/assets/images/down.svg new file mode 100644 index 00000000..b8b626fa --- /dev/null +++ b/src/assets/images/down.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/downBig.svg b/src/assets/images/downBig.svg new file mode 100644 index 00000000..7d35097f --- /dev/null +++ b/src/assets/images/downBig.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/downSmall.svg b/src/assets/images/downSmall.svg new file mode 100644 index 00000000..c078e705 --- /dev/null +++ b/src/assets/images/downSmall.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/empty-wallet.svg b/src/assets/images/empty-wallet.svg new file mode 100644 index 00000000..2c8ad29f --- /dev/null +++ b/src/assets/images/empty-wallet.svg @@ -0,0 +1,24 @@ + + + illus + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/empty.svg b/src/assets/images/empty.svg new file mode 100644 index 00000000..e8285395 --- /dev/null +++ b/src/assets/images/empty.svg @@ -0,0 +1,27 @@ + + + illus + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/error-filled.svg b/src/assets/images/error-filled.svg new file mode 100644 index 00000000..e0f66cd3 --- /dev/null +++ b/src/assets/images/error-filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/ethereum.svg b/src/assets/images/ethereum.svg new file mode 100644 index 00000000..88ee23a3 --- /dev/null +++ b/src/assets/images/ethereum.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/assets/images/group.svg b/src/assets/images/group.svg new file mode 100644 index 00000000..9070fb87 --- /dev/null +++ b/src/assets/images/group.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/index.ts b/src/assets/images/index.ts new file mode 100644 index 00000000..bb1f2c00 --- /dev/null +++ b/src/assets/images/index.ts @@ -0,0 +1,107 @@ +import metamask from './metamask.png'; +import walletConnectIcon from './walletConnectIcon.svg'; +import coinbaseWalletIcon from './coinbaseWalletIcon.svg'; +import nightElfIcon from './nightElf.png'; +import ebridgeLog from './logo-eBridge.svg'; +import aelfLogo from './logo-aelf.svg'; +import aelfChainLogo from './logo-aelf-chain.svg'; +import ethereumLogo from './ethereum.svg'; +import groupIcon from './group.svg'; +import tonLogo from './ton.svg'; +import clearIcon from './clear-icon.svg'; +import discordIcon from './discord.svg'; +import discordDarkIcon from './discord-dark.svg'; +import telegramIcon from './telegram.svg'; +import telegramDarkIcon from './telegram-dark.svg'; +import twitterIcon from './twitter.svg'; +import twitterDarkIcon from './twitter-dark.svg'; +import translateIcon from './translate.svg'; +import translateBlueIcon from './translate-blue.svg'; +import communityIcon from './community.svg'; +import legalIcon from './legal.svg'; +import arrowIcon from './arrow.svg'; +import arrowGrayIcon from './arrow-gray.svg'; +import downIcon from './down.svg'; +import downBigIcon from './downBig.svg'; +import downSmallIcon from './downSmall.svg'; +import arrowBlueIcon from './arrow-blue.svg'; +import arrowRightWhiteIcon from './arrow_right_white.svg'; +import arrowRightGrayIcon from './arrow_right_gray.svg'; +import backIcon from './back.svg'; +import checkBlueIcon from './check-blue.svg'; +import checkFilledIcon from './check-filled.svg'; +import checkFilled16Icon from './checkFilled16.svg'; +import closeFilled16Icon from './closeFilled16.svg'; +import errorFilledIcon from './error-filled.svg'; +import menuIcon from './menu.svg'; +import bridgeIcon from './bridge.svg'; +import bridgeBlueIcon from './bridge-blue.svg'; +import transactionsIcon from './transactions.svg'; +import transactionsBlueIcon from './transactions-blue.svg'; +import closeIcon from './close.svg'; +import infoCircleIcon from './info-circle.svg'; +import questionFilledIcon from './question-filled.svg'; +import loadingIcon from './loading.png'; +import completeIcon from './complete.svg'; +import infoCircleFillIcon from './info-circle-fill.svg'; +import infoCircleFillGrayIcon from './info-circle-fill-gray.svg'; +import viewProgressIcon from './view-progress.svg'; +import poolsIcon from './pools.svg'; +import poolsBlueIcon from './pools-blue.svg'; +import listingIcon from './listing.svg'; +import searchIcon from './search.svg'; + +export { + metamask, + walletConnectIcon, + coinbaseWalletIcon, + nightElfIcon, + ebridgeLog, + aelfLogo, + aelfChainLogo, + ethereumLogo, + groupIcon, + clearIcon, + discordIcon, + discordDarkIcon, + telegramIcon, + telegramDarkIcon, + twitterIcon, + twitterDarkIcon, + translateIcon, + translateBlueIcon, + communityIcon, + legalIcon, + arrowIcon, + arrowGrayIcon, + downIcon, + downBigIcon, + downSmallIcon, + arrowBlueIcon, + arrowRightWhiteIcon, + arrowRightGrayIcon, + backIcon, + checkBlueIcon, + checkFilledIcon, + checkFilled16Icon, + closeFilled16Icon, + errorFilledIcon, + menuIcon, + bridgeIcon, + bridgeBlueIcon, + transactionsIcon, + transactionsBlueIcon, + closeIcon, + infoCircleIcon, + infoCircleFillIcon, + infoCircleFillGrayIcon, + questionFilledIcon, + loadingIcon, + tonLogo, + completeIcon, + viewProgressIcon, + poolsIcon, + poolsBlueIcon, + listingIcon, + searchIcon, +}; diff --git a/src/assets/images/info-circle-fill-gray.svg b/src/assets/images/info-circle-fill-gray.svg new file mode 100644 index 00000000..bff68e73 --- /dev/null +++ b/src/assets/images/info-circle-fill-gray.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/info-circle-fill.svg b/src/assets/images/info-circle-fill.svg new file mode 100644 index 00000000..9752fadf --- /dev/null +++ b/src/assets/images/info-circle-fill.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/info-circle.svg b/src/assets/images/info-circle.svg new file mode 100644 index 00000000..ea13d4f0 --- /dev/null +++ b/src/assets/images/info-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/legal.svg b/src/assets/images/legal.svg new file mode 100644 index 00000000..0288bc4a --- /dev/null +++ b/src/assets/images/legal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/listing.svg b/src/assets/images/listing.svg new file mode 100644 index 00000000..f1f24aa5 --- /dev/null +++ b/src/assets/images/listing.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/loading.png b/src/assets/images/loading.png new file mode 100644 index 00000000..1861216d Binary files /dev/null and b/src/assets/images/loading.png differ diff --git a/src/assets/images/logo-aelf-chain.svg b/src/assets/images/logo-aelf-chain.svg new file mode 100644 index 00000000..6b085a9a --- /dev/null +++ b/src/assets/images/logo-aelf-chain.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/logo-aelf.svg b/src/assets/images/logo-aelf.svg new file mode 100644 index 00000000..99b5656b --- /dev/null +++ b/src/assets/images/logo-aelf.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/assets/images/logo-eBridge.svg b/src/assets/images/logo-eBridge.svg new file mode 100644 index 00000000..799a3d1b --- /dev/null +++ b/src/assets/images/logo-eBridge.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/menu.svg b/src/assets/images/menu.svg new file mode 100644 index 00000000..d5ffac71 --- /dev/null +++ b/src/assets/images/menu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/metamask.png b/src/assets/images/metamask.png new file mode 100644 index 00000000..85714ea2 Binary files /dev/null and b/src/assets/images/metamask.png differ diff --git a/src/assets/images/nightElf.png b/src/assets/images/nightElf.png new file mode 100644 index 00000000..0f764541 Binary files /dev/null and b/src/assets/images/nightElf.png differ diff --git a/src/assets/images/pools-blue.svg b/src/assets/images/pools-blue.svg new file mode 100644 index 00000000..c32d6e2e --- /dev/null +++ b/src/assets/images/pools-blue.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/pools.svg b/src/assets/images/pools.svg new file mode 100644 index 00000000..004edf8b --- /dev/null +++ b/src/assets/images/pools.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/question-filled.svg b/src/assets/images/question-filled.svg new file mode 100644 index 00000000..8a3d67de --- /dev/null +++ b/src/assets/images/question-filled.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/search.svg b/src/assets/images/search.svg new file mode 100644 index 00000000..e5f39512 --- /dev/null +++ b/src/assets/images/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/telegram-dark.svg b/src/assets/images/telegram-dark.svg new file mode 100644 index 00000000..b8ec1abd --- /dev/null +++ b/src/assets/images/telegram-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/telegram.svg b/src/assets/images/telegram.svg new file mode 100644 index 00000000..1a6c12bc --- /dev/null +++ b/src/assets/images/telegram.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/ton.svg b/src/assets/images/ton.svg new file mode 100644 index 00000000..819a1b04 --- /dev/null +++ b/src/assets/images/ton.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/assets/images/transactions-blue.svg b/src/assets/images/transactions-blue.svg new file mode 100644 index 00000000..4cb7baf0 --- /dev/null +++ b/src/assets/images/transactions-blue.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/transactions.svg b/src/assets/images/transactions.svg new file mode 100644 index 00000000..f87de16e --- /dev/null +++ b/src/assets/images/transactions.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/translate-blue.svg b/src/assets/images/translate-blue.svg new file mode 100644 index 00000000..632a3f38 --- /dev/null +++ b/src/assets/images/translate-blue.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/translate.svg b/src/assets/images/translate.svg new file mode 100644 index 00000000..d2883c9e --- /dev/null +++ b/src/assets/images/translate.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/twitter-dark.svg b/src/assets/images/twitter-dark.svg new file mode 100644 index 00000000..48d47e43 --- /dev/null +++ b/src/assets/images/twitter-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/twitter.svg b/src/assets/images/twitter.svg new file mode 100644 index 00000000..0df88b21 --- /dev/null +++ b/src/assets/images/twitter.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/view-progress.svg b/src/assets/images/view-progress.svg new file mode 100644 index 00000000..acb0cd94 --- /dev/null +++ b/src/assets/images/view-progress.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/images/walletConnectIcon.svg b/src/assets/images/walletConnectIcon.svg new file mode 100644 index 00000000..f78a7b85 --- /dev/null +++ b/src/assets/images/walletConnectIcon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/theme/color.less b/src/assets/theme/color.less new file mode 100644 index 00000000..48333e51 --- /dev/null +++ b/src/assets/theme/color.less @@ -0,0 +1,54 @@ +@primary-color: ~'var(--@{app-prefix}-primary-color)'; +@primary-color-hover: ~'var(--@{app-prefix}-primary-color-hover)'; + +@font-1: @primary-color; +@font-2: #262626; +@font-3: #8f97a6; +@font-4: #000000; +@font-5: #364153; +@font-6: #e13d3d; +@font-7: #faad14; +@font-8: #f28736; +@font-9: #52c41a; +@font-10: #d8dce2; +@font-11: #a2b0f2; +@font-12: #4d6aec; +@font-13: #808080; +@font-14: #127fff; +@font-15: #1a1a1a; +@font-16: #285eff; +@font-17: #252525; +@font-18: #d6d6d6; + +@bg-1: #f7f7f9; +@bg-2: #ffffff; +@bg-3: #f3f4f7; +@bg-4: #eceef2; +@bg-5: #dee2e8; +@bg-6: @primary-color; +@bg-7: #f6f7fa; +@bg-8: #edf0fd; +@bg-9: #d27974; +@bg-10: #fef6e7; +@bg-11: #d8e0ff; +@bg-12: #4f77ff; +@bg-13: #edf0fd; +@bg-14: #f9fcff; +@bg-15: #f8f8f8; +@bg-16: #f5f5f5; +@bg-17: #127fff; +@bg-18: #00b73e; +@bg-19: #f0f0f0; +@bg-20: #ff9900; +@bg-21: #f53f3f; +@bg-22: #fee8e8; + +@border-1: #c5cbd5; +@border-2: @primary-color; +@border-3: #dee2e8; +@border-4: #faad14; +@border-5: #e0e0e0; +@border-6: #f0f0f0; + +@shadow-1: #6a83d1; +@shadow-2: #0000000d; diff --git a/src/assets/theme/common-class.less b/src/assets/theme/common-class.less new file mode 100644 index 00000000..39395b2f --- /dev/null +++ b/src/assets/theme/common-class.less @@ -0,0 +1,117 @@ +.flex { + display: flex; +} +.flex-1 { + flex: 1; +} +.flex-shrink-0 { + flex-shrink: 0; +} +.flex-row { + display: flex; + flex-direction: row; +} +.flex-row-reverse { + display: flex; + flex-direction: row-reverse; +} +.flex-column { + display: flex; + flex-direction: column; +} +.align-end { + align-items: flex-end; +} +.flex-column-center { + align-items: center; + .flex-column(); +} +.flex-row-wrap { + flex-wrap: wrap; + .flex-row(); +} +.flex-row-center { + align-items: center; + .flex-row(); +} +.flex-row-center-between { + justify-content: space-between; + .flex-row-center(); +} +.flex-row-content-center { + justify-content: center; + .flex-row(); +} +.flex-row-content-end { + justify-content: flex-end; + .flex-row(); +} +.row-center { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} +.flex-center { + display: flex; + justify-content: center; + align-items: center; +} +.flex-row-between { + justify-content: space-between; + .flex-row(); +} +.cursor-pointer { + cursor: pointer; +} +.margin-auto { + margin: auto; +} +.margin-top-10 { + margin-top: 10px; +} + +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} +.gap-8 { + gap: 8px; +} +.gap-4 { + gap: 4px; +} + + +/* set list-style */ +ul, +ol { + list-style: none; +} + +/* set text-decoration */ +a, +a:hover { + text-decoration: none; +} + +/* clear margin & padding */ +body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements */ + dl, dt, dd, ul, ol, li, /* list elements */ + pre, /* text formatting elements */ + fieldset, button, input, textarea, /* form elements */ + th, td { + /* table elements */ + margin: 0; + padding: 0; +} +.pointer-events-none { + pointer-events: none !important; +} + +.list-style-decimal { + list-style: decimal; + padding-left: 16px; +} diff --git a/src/assets/theme/constants.less b/src/assets/theme/constants.less new file mode 100644 index 00000000..c22aab69 --- /dev/null +++ b/src/assets/theme/constants.less @@ -0,0 +1,17 @@ + +@xl-width: 1179px; +@md-width: 799px; + +@max-width: ~'var(--@{app-prefix}-max-width)'; +@footer-height: 120px; +@header-height: ~'var(--@{app-prefix}-header-height)'; + +.text-overflow(@n) { + text-overflow: -o-ellipsis-lastline; + overflow: hidden; + /* stylelint-disable-next-line declaration-block-no-duplicate-properties */ + text-overflow: ellipsis; + -webkit-box-orient: vertical; + line-clamp: @n; + -webkit-line-clamp: @n; +} diff --git a/src/assets/theme/customize.less b/src/assets/theme/customize.less new file mode 100644 index 00000000..ad5b0a7b --- /dev/null +++ b/src/assets/theme/customize.less @@ -0,0 +1,33 @@ +@font-face { + font-family: Poppins-Bold; + src: url('/font/Poppins-Bold.ttf'); +} +@font-face { + font-family: Poppins-Medium; + src: url('/font/Poppins-Medium.ttf'); +} +@font-face { + font-family: Poppins-Regular; + src: url('/font/Poppins-Regular.ttf'); +} +html { + --@{app-prefix}-primary-color: #127fff; + --@{app-prefix}-primary-color-hover: #127fff; + // border-hover + --@{app-prefix}-primary-5: #127fff; + // border-hover-shadow + --@{app-prefix}-primary-color-outline: #506ef514; + --@{app-prefix}-primary-1: #edf0fd; + --@{app-prefix}-error-color: #ce342a; + // constants + --@{app-prefix}-max-width: 1000px; + --@{app-prefix}-header-height: 86px; + + @media screen and (max-width: 1160px) { + --@{app-prefix}-max-width: calc(100vw - 120px); + } +} + +html[data-theme='dark'] { + --@{app-prefix}-primary-color: red; +} diff --git a/src/assets/theme/font.less b/src/assets/theme/font.less new file mode 100644 index 00000000..d2223a0c --- /dev/null +++ b/src/assets/theme/font.less @@ -0,0 +1,28 @@ +@import '/src/assets/theme/color.less'; +.font-family-semibold { + font-weight: bold; + font-family: Poppins-Bold, sans-serif; +} +.font-family-medium { + font-weight: 500; + font-family: Poppins-Medium, sans-serif; +} +.font-family-regular { + font-weight: 400; + font-family: Poppins-Regular, sans-serif; +} +.font-2 { + color: @font-2; +} +.font-15 { + color: @font-15; +} +// .font-family-ultralight { +// font-family: PingFangSC-Ultralight, PingFang SC, sans-serif; +// } +// .font-family-light { +// font-family: PingFangSC-Light, PingFang SC, sans-serif; +// } +// .font-family-thin { +// font-family: PingFangSC-Thin, PingFang SC, sans-serif; +// } diff --git a/src/components/CommonAmountRow/AmountInput/index.tsx b/src/components/CommonAmountRow/AmountInput/index.tsx new file mode 100644 index 00000000..70e35ad5 --- /dev/null +++ b/src/components/CommonAmountRow/AmountInput/index.tsx @@ -0,0 +1,31 @@ +import { Input, InputProps, Row } from 'antd'; +import { useState } from 'react'; +import { parseInputChange } from 'utils/input'; +import { isValidPositiveNumber } from 'utils/reg'; +import styles from './styles.module.less'; +export default function AmountInput({ + value: propsValue, + onChange, + className, + maxLength, + placeholder = '0', +}: InputProps) { + const [value, setValue] = useState(''); + return ( + + { + const { value } = e.target; + if (value && !isValidPositiveNumber(value)) return; + propsValue === undefined && setValue(parseInputChange(value)); + onChange?.(e); + }} + /> + + ); +} diff --git a/src/components/CommonAmountRow/AmountInput/styles.module.less b/src/components/CommonAmountRow/AmountInput/styles.module.less new file mode 100644 index 00000000..2ca235bd --- /dev/null +++ b/src/components/CommonAmountRow/AmountInput/styles.module.less @@ -0,0 +1,22 @@ +@import '/src/assets/theme/color.less'; + +.amount-input { + flex: 1; + :global { + .@{app-prefix}-input, + .@{app-prefix}-input-affix-wrapper { + padding: 0; + border: 0; + color: @font-17; + font-size: 20px; + font-weight: 500; + line-height: 24px; + &:focus, + &:not(.input-affix-wrapper-disabled), + &:not(.input-affix-wrapper-disabled):hover { + border: 0; + box-shadow: none; + } + } + } +} diff --git a/src/components/CommonAmountRow/index.tsx b/src/components/CommonAmountRow/index.tsx new file mode 100644 index 00000000..62647634 --- /dev/null +++ b/src/components/CommonAmountRow/index.tsx @@ -0,0 +1,73 @@ +import { useLanguage } from 'i18n'; +import clsx from 'clsx'; +import { Button, InputProps } from 'antd'; +import styles from './styles.module.less'; +import AmountInput from './AmountInput'; +import { ReactNode } from 'react'; + +export type TCommonAmountRowProps = { + onClickMAX?: () => void; + showBalance?: boolean; + leftHeaderTitle?: string; + rightHeaderTitle?: string; + leftHeaderEle?: ReactNode; + rightInputEle?: ReactNode; + rightHeaderEle?: ReactNode; + value?: InputProps['value']; + onAmountInputChange?: InputProps['onChange']; + showError?: boolean; + maxLength?: number; + className?: string; + inputWrapClassName?: string; + placeholder?: InputProps['placeholder']; +}; + +export default function CommonAmountRow({ + onClickMAX, + showBalance, + leftHeaderTitle, + rightHeaderTitle, + leftHeaderEle, + rightHeaderEle, + rightInputEle, + value, + showError, + maxLength = 25, + onAmountInputChange, + className, + inputWrapClassName, + placeholder, +}: TCommonAmountRowProps) { + const { t } = useLanguage(); + + return ( +
+
+ {leftHeaderEle ? leftHeaderEle : {leftHeaderTitle}} + {showBalance && ( +
+ {rightHeaderEle ? rightHeaderEle : {rightHeaderTitle}} + +
+ )} +
+
+ + {rightInputEle ? ( + <> +
+ {rightInputEle} + + ) : null} +
+
+ ); +} diff --git a/src/components/CommonAmountRow/styles.module.less b/src/components/CommonAmountRow/styles.module.less new file mode 100644 index 00000000..d40ea7bb --- /dev/null +++ b/src/components/CommonAmountRow/styles.module.less @@ -0,0 +1,43 @@ +@import '/src/assets/theme/color.less'; + +.amount-row { + gap: 8px; +} + +.amount-label-wrap { + gap: 16px; + .amount-label { + color: @font-13; + font-size: 16px; + line-height: 22px; + } + .balance-wrap { + gap: 16px; + } + .balance { + color: @font-15; + font-size: 16px; + line-height: 22px; + } + .max-button { + padding: 0; + font-size: 16px; + font-weight: 500; + line-height: 22px; + } +} + +.amount-input-wrap { + gap: 12px; + padding: 16px 12px; + border-radius: 6px; + border: 1px solid @border-5; + background: @bg-2; + .amount-input-red input { + color: @font-6; + } + .divider { + height: 24px; + border-left: 1px solid @border-6; + } +} diff --git a/src/components/CommonButton/index.tsx b/src/components/CommonButton/index.tsx new file mode 100644 index 00000000..aa0d8391 --- /dev/null +++ b/src/components/CommonButton/index.tsx @@ -0,0 +1,35 @@ +import { Button } from 'antd'; +import type { ButtonProps } from 'antd/lib/button/button'; +import clsx from 'clsx'; +import React from 'react'; +import styles from './styles.module.less'; +declare const ButtonTypes: ['default', 'primary', 'ghost', 'dashed', 'link', 'text', 'select']; + +export interface CommonButtonProps { + type?: (typeof ButtonTypes)[number]; + children?: React.ReactNode; + icon?: React.ReactNode; + shape?: ButtonProps['shape']; + size?: ButtonProps['size']; + disabled?: boolean; + loading?: + | boolean + | { + delay?: number; + }; + prefixCls?: string; + className?: string; + ghost?: boolean; + danger?: boolean; + block?: boolean; + onClick?: ButtonProps['onClick']; +} +export default function CommonButton({ className, type, ...props }: CommonButtonProps) { + let c = ''; + let btnType = type; + if (type === 'select') { + c = styles['select-btn']; + btnType = undefined; + } + return + ); +} diff --git a/src/components/CommonMessage/index.tsx b/src/components/CommonMessage/index.tsx new file mode 100644 index 00000000..548e201b --- /dev/null +++ b/src/components/CommonMessage/index.tsx @@ -0,0 +1,18 @@ +import { message } from 'antd'; +import i18n from 'i18next'; + +class CommonMessage { + error(text: any) { + if ( + /((You closed the prompt with out any action)|(Operation canceled)|(You closed the prompt without any action))/.test( + JSON.stringify(text), + ) + ) { + const txt = i18n.t('Request rejected. eBridge needs your permission to continue'); + return message.error(txt); + } + message.error(text); + } +} +// eslint-disable-next-line import/no-anonymous-default-export +export default new CommonMessage(); diff --git a/src/components/CommonModal/index.tsx b/src/components/CommonModal/index.tsx new file mode 100644 index 00000000..04df112c --- /dev/null +++ b/src/components/CommonModal/index.tsx @@ -0,0 +1,58 @@ +import { Col, Modal, ModalProps, Row } from 'antd'; +import clsx from 'clsx'; +import LeftOutlined from '@ant-design/icons/LeftOutlined'; +import { ReactNode } from 'react'; +import useMediaQueries from 'hooks/useMediaQueries'; // Cannot import useMobile +import { prefixCls } from 'constants/misc'; +import CommonImage from 'components/CommonImage'; +import { closeIcon } from 'assets/images'; +import { TelegramPlatform } from 'utils/telegram/telegram'; + +export interface ICommonModalProps extends ModalProps { + children?: any; + className?: string; + leftCallBack?: () => void; + leftElement?: ReactNode; + transitionName?: string; + type?: 'pop-bottom' | 'default'; +} + +export default function CommonModal(props: ICommonModalProps) { + const { leftCallBack, width, title, leftElement, transitionName, type } = props; + const isMd = useMediaQueries('md'); + return ( + } + maskClosable={false} + centered={props.centered ? props.centered : !isMd} + destroyOnClose + footer={null} + {...props} + width={width ? width : '800px'} + className={clsx( + 'common-modals', + { + 'common-modal-center': isMd && props.centered, + 'common-bottom-modals': type === 'pop-bottom', + 'tg-common-modals': TelegramPlatform.isTelegramPlatform(), + 'tg-common-bottom-modals': type === 'pop-bottom' && TelegramPlatform.isTelegramPlatform(), + }, + props.className, + )} + transitionName={transitionName ?? isMd ? `${prefixCls}-move-down` : undefined} + title={ + + {leftCallBack || leftElement ? ( + + {leftElement || } + + ) : null} + + {title} + + {leftCallBack || leftElement ? : null} + + } + /> + ); +} diff --git a/src/components/CommonPopover/index.tsx b/src/components/CommonPopover/index.tsx new file mode 100644 index 00000000..8bb3d32e --- /dev/null +++ b/src/components/CommonPopover/index.tsx @@ -0,0 +1,6 @@ +import { Popover, PopoverProps } from 'antd'; +import clsx from 'clsx'; +import styles from './styles.module.less'; +export default function CommonPopover(props: PopoverProps) { + return ; +} diff --git a/src/components/CommonPopover/styles.module.less b/src/components/CommonPopover/styles.module.less new file mode 100644 index 00000000..0c0ec2f8 --- /dev/null +++ b/src/components/CommonPopover/styles.module.less @@ -0,0 +1,19 @@ +@import '/src/assets/theme/color.less'; +.common-popover { + padding: 5px; + :global { + .@{app-prefix}-popover-inner { + border-radius: 12px; + } + .@{app-prefix}-popover-inner-content { + max-width: 280px; + padding: 20px; + box-shadow: 0 5px 12px 4px @shadow-2; + border-radius: 12px; + } + .@{app-prefix}-popover-arrow { + width: 16px; + height: 16px; + } + } +} diff --git a/src/components/CommonSelect/index.tsx b/src/components/CommonSelect/index.tsx new file mode 100644 index 00000000..abb31fb1 --- /dev/null +++ b/src/components/CommonSelect/index.tsx @@ -0,0 +1,18 @@ +import { Select, SelectProps } from 'antd'; +import clsx from 'clsx'; +import React from 'react'; +import CommonImage from 'components/CommonImage'; +import { closeIcon, arrowIcon } from 'assets/images'; +import styles from './styles.module.less'; +export default function CommonSelect({ className, dropdownClassName, ...props }: SelectProps) { + return ( + { + setValue(e.target.value); + }} + onPressEnter={onSearch} + className={styles['input-search']} + placeholder={t('Search token')} + suffix={} + /> + {remindContent && ( + + {remindContent} + + )} +
+ {(searchList || tokenList).map((item, k) => { + return ( + onSelect(item)} + key={k} + className={clsx('flex-row-center-between', { + [styles['token-item']]: true, + [styles['token-item-selected']]: item.symbol === selectSymbol, + [styles['token-item-disable']]: item?.disable, + })}> +
+ + {formatSymbol(item.displaySymbol || item.symbol)} +
+ {item?.isShowSuffix &&
{item?.status}
} +
+ ); + })} +
+ {!hideAddToken && ( +
+
+ +
+
+ )} + + ); +} + +export default function CommonSelectTokenModal({ open, onClose, ...selectTokenProps }: ISelectTokenModalProps) { + const { t } = useLanguage(); + + return ( + + + + ); +} diff --git a/src/components/CommonSelectTokenModal/styles.module.less b/src/components/CommonSelectTokenModal/styles.module.less new file mode 100644 index 00000000..b7b2c8bc --- /dev/null +++ b/src/components/CommonSelectTokenModal/styles.module.less @@ -0,0 +1,134 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + +@margin-left-right-web: 24px; +@margin-left-right-mobile: 16px; + +.select-modal { + height: 430px; + :global { + .@{app-prefix}-modal-content { + display: flex; + flex-direction: column; + height: 100%; + } + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-body { + overflow: hidden; + min-width: 448px; + max-width: 448px; + padding-left: 0 !important; + padding-right: 0 !important; + padding-top: 8px; + } + } + .input-search { + height: 40px; + border-radius: 6px; + width: calc(100% - @margin-left-right-web * 2); + margin-bottom: 8px; + align-self: center; + :global { + .@{app-prefix}-input-suffix { + font-size: 16px; + } + } + } + .input-search-suffix{ + width: 16px; + height: 16px; + } + .token-tip { + margin-left: @margin-left-right-web; + margin-right: @margin-left-right-web; + margin-bottom: 8px; + } + .token-list { + overflow: auto; + .token-item { + flex-wrap: nowrap; + padding: 13px @margin-left-right-web; + line-height: 24px; + cursor: pointer; + .font-family-medium(); + .token-logo { + flex: none; + width: 24px; + height: 24px; + margin-right: 8px; + border-radius: 50%; + } + .token-name { + overflow: hidden; + flex-shrink: 1; + text-overflow: ellipsis; + white-space: nowrap; + } + .token-suffix { + font-size: 14px; + color: @font-13; + .font-family-regular(); + } + .token-info-disable { + opacity: 0.5; + } + &:hover { + background: @bg-15; + } + } + .token-item-selected { + background: @bg-15; + } + .token-item-disable { + cursor: not-allowed; + } + } + .token-list-add-token { + margin-bottom: 56px; + } + .bottom-row { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 8px 0 24px; + .row-body { + height: 48px; + a { + color: @font-1; + } + } + } +} +@media screen and (max-width: @md-width) { + .select-modal { + height: auto; + :global { + .@{app-prefix}-modal-header { + border-width: 0; + } + .@{app-prefix}-modal-content { + padding-left: 0 !important; + padding-right: 0 !important; + } + } + .input-search { + width: calc(100% - @margin-left-right-mobile * 2); + } + .token-tip { + margin-left: @margin-left-right-mobile; + margin-right: @margin-left-right-mobile; + } + .token-list { + .token-item { + padding-left: @margin-left-right-mobile; + padding-right: @margin-left-right-mobile; + } + } + } +} diff --git a/src/components/CommonSteps/index.tsx b/src/components/CommonSteps/index.tsx new file mode 100644 index 00000000..07dc2b68 --- /dev/null +++ b/src/components/CommonSteps/index.tsx @@ -0,0 +1,54 @@ +import { useMemo } from 'react'; +import clsx from 'clsx'; +import { Steps, StepsProps } from 'antd'; +import PartialLoading from 'components/PartialLoading'; +import { useMobile } from 'contexts/useStore/hooks'; +import styles from './styles.module.less'; + +export interface ICommonStepsProps extends Omit { + stepItems: (Required['items'][number] & { isLoading?: boolean })[]; + hideLine?: boolean; +} + +export default function CommonSteps({ + className, + stepItems, + current, + direction, + hideLine = false, + ...props +}: ICommonStepsProps) { + const isMobile = useMobile(); + + const stepDirection = useMemo(() => { + const _default = isMobile ? 'vertical' : 'horizontal'; + return direction || _default; + }, [direction, isMobile]); + + const stepDirectionClassName = useMemo(() => { + return stepDirection === 'vertical' ? styles['common-steps-vertical'] : styles['common-steps-horizontal']; + }, [stepDirection]); + + const stepItemsWithIcon = stepItems?.map(({ isLoading, ...item }) => ({ + ...item, + icon: isLoading ? : undefined, + })); + + return ( + + ); +} diff --git a/src/components/CommonSteps/styles.module.less b/src/components/CommonSteps/styles.module.less new file mode 100644 index 00000000..dfc00a59 --- /dev/null +++ b/src/components/CommonSteps/styles.module.less @@ -0,0 +1,196 @@ +@import 'src/assets/theme/color.less'; +@import 'src/assets/theme/constants.less'; +@import 'src/assets/theme/font.less'; + +.common-steps.common-steps-weight { + :global { + .@{app-prefix}-steps-item { + .@{app-prefix}-steps-item-content { + min-width: 72px; + + .@{app-prefix}-steps-item-title { + .font-family-medium(); + + font-size: 12px; + height: 20px; + line-height: 20px; + } + .@{app-prefix}-steps-item-description { + .font-family-medium(); + + color: @font-13; + font-size: 12px; + line-height: 20px; + } + } + + &:not(.@{app-prefix}-steps-item-active) > .@{app-prefix}-steps-item-container[role='button']:hover { + .@{app-prefix}-steps-item-icon { + border-color: @bg-17; + .@{app-prefix}-steps-icon { + color: @bg-17; + } + } + .@{app-prefix}-steps-item-title { + color: @bg-17; + } + } + + &.@{app-prefix}-steps-item-process { + .@{app-prefix}-steps-item-icon { + border: none; + border-radius: 50%; + background-color: @bg-17; + .@{app-prefix}-steps-icon { + display: flex; + align-items: center; + justify-content: center; + color: @bg-2; + path { + stroke: @bg-2; + } + } + } + .@{app-prefix}-steps-item-tail { + &::after { + background-color: @border-5; + } + } + } + + &.@{app-prefix}-steps-item-wait { + .@{app-prefix}-steps-item-icon { + border-color: @font-13; + .@{app-prefix}-steps-icon { + color: @font-13; + } + } + .@{app-prefix}-steps-item-tail { + &::after { + background-color: @border-5; + } + } + } + + &.@{app-prefix}-steps-item-finish { + .@{app-prefix}-steps-item-icon { + border-color: @bg-17; + .@{app-prefix}-steps-finish-icon { + color: @bg-17; + } + } + .@{app-prefix}-steps-item-tail { + &::after { + background-color: @bg-17; + } + } + } + + &.@{app-prefix}-steps-item-error { + .@{app-prefix}-steps-item-title { + color: @font-15; + } + } + + &:has(+ .@{app-prefix}-steps-item-error) { + .@{app-prefix}-steps-item-tail { + &::after { + background-color: @font-6; + } + } + } + + &:has(+ .@{app-prefix}-steps-item-finish), + &:has(+ .@{app-prefix}-steps-item-process) { + .@{app-prefix}-steps-item-tail { + &::after { + background-color: @bg-17; + } + } + } + } + } + + &.common-steps-hide-line { + :global { + .@{app-prefix}-steps-item { + .@{app-prefix}-steps-item-tail { + &::after { + background-color: transparent !important; + } + } + } + } + } + + &.common-steps-horizontal { + :global { + .@{app-prefix}-steps-item { + .@{app-prefix}-steps-item-tail { + margin-left: 12px; + padding: 3.5px 24px; + } + .@{app-prefix}-steps-item-icon { + margin-left: 0; + margin-right: 12px; + } + + .@{app-prefix}-steps-item-content { + width: 100%; + // margin-right: 24px; + text-align: left; + } + &.@{app-prefix}-steps-item-process { + .@{app-prefix}-steps-item-icon { + .@{app-prefix}-steps-icon { + width: 24px; + height: 24px; + } + } + } + } + } + } + + &.common-steps-vertical { + .common-steps-loading { + width: 10px; + height: 10px; + } + + :global { + .@{app-prefix}-steps-item { + &:last-child { + flex: none; + .@{app-prefix}-steps-item-content { + min-height: auto; + } + } + &:not(:last-child) { + .@{app-prefix}-steps-item-content { + min-height: 52px; + } + } + .@{app-prefix}-steps-item-tail { + left: 10px; + padding-top: 28px; + } + .@{app-prefix}-steps-item-icon { + width: 20px; + height: 20px; + margin-right: 12px; + display: flex; + align-items: center; + justify-content: center; + .@{app-prefix}-steps-icon { + font-size: 10px; + } + } + } + } + + @media screen and (max-width: @md-width) { + width: auto; + } + } +} diff --git a/src/components/CommonTable/index.tsx b/src/components/CommonTable/index.tsx new file mode 100644 index 00000000..9d511bbe --- /dev/null +++ b/src/components/CommonTable/index.tsx @@ -0,0 +1,36 @@ +import { Table, TableProps } from 'antd'; +import React, { useMemo } from 'react'; +import EmptyWallet from 'assets/images/empty-wallet.svg'; +import Empty from 'assets/images/empty.svg'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { Trans } from 'react-i18next'; +import EmptyDataBox from 'components/EmptyDataBox'; + +export default function CommonTable(props: TableProps & { emptyText?: React.ReactNode }) { + const { fromWallet, toWallet } = useWallet(); + const isConnect = !(fromWallet?.account || toWallet?.account); + const defaultEmptyText = useMemo(() => { + return !isConnect ? ( + No cross-chain records found. + ) : ( + <> + Connect2 + wallet + to view transaction history + + ); + }, [isConnect]); + + return ( + { + return ( + + ); + }, + }} + /> + ); +} diff --git a/src/components/CommonTooltip/index.tsx b/src/components/CommonTooltip/index.tsx new file mode 100644 index 00000000..c1a5bbf3 --- /dev/null +++ b/src/components/CommonTooltip/index.tsx @@ -0,0 +1,7 @@ +import { Tooltip, TooltipProps } from 'antd'; +import clsx from 'clsx'; +import styles from './styles.module.less'; + +export default function CommonTooltip({ overlayClassName, ...props }: TooltipProps) { + return ; +} diff --git a/src/components/CommonTooltip/styles.module.less b/src/components/CommonTooltip/styles.module.less new file mode 100644 index 00000000..4a7f94dc --- /dev/null +++ b/src/components/CommonTooltip/styles.module.less @@ -0,0 +1,24 @@ +@import 'src/assets/theme/font.less'; +@import 'src/assets/theme/constants.less'; + +.common-tooltip-overlay { + font-size: 12px; + line-height: 20px; + + :global { + .@{app-prefix}-tooltip-inner { + padding: 8px; + border-radius: 8px; + } + } +} + +@media screen and (max-width: @md-width) { + .common-tooltip-overlay { + :global { + .@{app-prefix}-tooltip-inner { + padding: 6px 8px; + } + } + } +} diff --git a/src/components/CommonTooltipSwitchModal/index.tsx b/src/components/CommonTooltipSwitchModal/index.tsx new file mode 100644 index 00000000..571f6ce1 --- /dev/null +++ b/src/components/CommonTooltipSwitchModal/index.tsx @@ -0,0 +1,101 @@ +import { useMemo, useState, useImperativeHandle, forwardRef, useCallback, useEffect } from 'react'; +import clsx from 'clsx'; +import { TooltipProps } from 'antd'; +import CommonTooltip from 'components/CommonTooltip'; +import CommonModal, { ICommonModalProps } from 'components/CommonModal'; +import { GOT_IT } from 'constants/misc'; +import styles from './styles.module.less'; +import { closeIcon } from 'assets/images'; +import CommonImage from 'components/CommonImage'; +import useMediaQueries from 'hooks/useMediaQueries'; +import CommonButton from 'components/CommonButton'; + +export interface ICommonTooltipSwitchModalRef { + open: () => void; +} + +interface ICommonTooltipSwitchModalProps { + tooltipProps?: Pick; + modalProps?: Pick; + modalWidth?: number; + tip: React.ReactNode; + children: React.ReactNode; + isShowConfirmButton?: boolean; + confirmButtonText?: string; + confirmButtonClassName?: string; + onConfirm?: () => void; + autoOpen?: boolean; +} + +const CommonTooltipSwitchModal = forwardRef( + ( + { + tooltipProps, + modalProps, + modalWidth = 335, + tip, + children, + isShowConfirmButton = true, + confirmButtonText = GOT_IT, + confirmButtonClassName, + onConfirm, + autoOpen, + }, + ref, + ) => { + const isMd = useMediaQueries('md'); + const isTooltip = useMemo(() => !isMd, [isMd]); + const [isModalOpen, setIsModalOpen] = useState(false); + + const handleModalOpen = useCallback(() => { + if (!isTooltip) { + setIsModalOpen(true); + } + }, [isTooltip]); + + const handleModalClose = useCallback(() => { + setIsModalOpen(false); + }, []); + + useImperativeHandle(ref, () => ({ + open: handleModalOpen, + })); + + useEffect(() => { + if (!isMd) { + handleModalClose(); + } + }, [handleModalClose, isMd]); + + return ( + <> + + {autoOpen ?
handleModalOpen()}>{children}
: children} +
+ } + okText={confirmButtonText} + open={isModalOpen} + onOk={handleModalClose} + onCancel={handleModalClose}> +
{tip}
+ {isShowConfirmButton && ( + + {confirmButtonText} + + )} +
+ + ); + }, +); + +CommonTooltipSwitchModal.displayName = 'CommonTooltipSwitchModal'; + +export default CommonTooltipSwitchModal; diff --git a/src/components/CommonTooltipSwitchModal/styles.module.less b/src/components/CommonTooltipSwitchModal/styles.module.less new file mode 100644 index 00000000..28c8788d --- /dev/null +++ b/src/components/CommonTooltipSwitchModal/styles.module.less @@ -0,0 +1,46 @@ +@import '/src/assets/theme/color.less'; + +.common-tooltip-switch-modal { + :global { + .@{app-prefix}-modal-body { + color: @font-13; + padding: 0 24px 24px !important; + } + .@{app-prefix}-modal-content { + .@{app-prefix}-modal-close { + .@{app-prefix}-modal-close-x { + width: 40px; + height: 16px; + padding-top: 36px; + padding-right: 24px; + div { + width: 16px; + height: 16px; + } + svg path { + fill: @font-15; + } + } + } + + .@{app-prefix}-modal-header { + min-height: 68px; + padding: 24px 24px 20px; + height: auto; + .@{app-prefix}-modal-title { + width: calc(100% - 40px); + font-size: 16px; + line-height: 24px; + word-wrap: break-word; + } + } + } + } + + .confirm-button { + height: 48px; + font-size: 16px; + line-height: 24px; + margin-top: 16px; + } +} diff --git a/src/components/ConnectWallet/index.tsx b/src/components/ConnectWallet/index.tsx new file mode 100644 index 00000000..39903ebf --- /dev/null +++ b/src/components/ConnectWallet/index.tsx @@ -0,0 +1,46 @@ +import clsx from 'clsx'; +import { Button } from 'antd'; +import { useLanguage } from 'i18n'; +import { ChainType, Web3Type } from 'types'; +import { shortenString } from 'utils'; +import { isELFChain } from 'utils/aelfUtils'; +import { formatAddress } from 'utils/chain'; +import styles from './styles.module.less'; +import { useConnect } from 'hooks/useConnect'; +import { TBridgeChainId } from 'constants/chain'; + +interface IConnectWalletProps { + wallet?: Web3Type; + chainType?: ChainType; + chainId?: TBridgeChainId; + account?: string; + buttonText?: string; +} + +export default function ConnectWallet({ wallet, chainType, chainId, account, buttonText }: IConnectWalletProps) { + const { t } = useLanguage(); + const connect = useConnect(); + const { chainId: walletChainId, account: walletAccount } = wallet || {}; + const currentChainId = chainId || walletChainId; + const currentAccount = account || walletAccount; + + return currentAccount ? ( +
+
+
+ {isELFChain(currentChainId) + ? shortenString(formatAddress(currentChainId, currentAccount), 7, 8) + : shortenString(currentAccount, 5, 3)} +
+
+ ) : ( + + ); +} diff --git a/src/components/ConnectWallet/styles.module.less b/src/components/ConnectWallet/styles.module.less new file mode 100644 index 00000000..fa73bf63 --- /dev/null +++ b/src/components/ConnectWallet/styles.module.less @@ -0,0 +1,24 @@ +@import '/src/assets/theme/color.less'; + +.wallet-address-wrap { + gap: 4px; + color: @font-13; + font-size: 14px; + font-weight: 400; + line-height: 22px; + .dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: @bg-18; + } +} + +.connect-button { + height: auto; + padding: 0; + border: none; + font-size: 16px; + line-height: 22px; + color: @font-14; +} diff --git a/src/components/Copy/index.tsx b/src/components/Copy/index.tsx new file mode 100644 index 00000000..6ad74183 --- /dev/null +++ b/src/components/Copy/index.tsx @@ -0,0 +1,25 @@ +import { useCopyToClipboard } from 'react-use'; +import IconFont from 'components/IconFont'; +export default function Copy({ + toCopy, + children, + className, +}: { + toCopy: string; + children?: React.ReactNode; + className?: string; +}) { + const [isCopied, setCopied] = useCopyToClipboard(); + return ( + setCopied(toCopy)} className={className}> + {isCopied.value ? ( + 'Copied' + ) : ( + <> + + {children} + + )} + + ); +} diff --git a/src/components/DisplayImage/index.tsx b/src/components/DisplayImage/index.tsx new file mode 100644 index 00000000..4dbdb89e --- /dev/null +++ b/src/components/DisplayImage/index.tsx @@ -0,0 +1,52 @@ +import styles from './styles.module.less'; +import clsx from 'clsx'; +import { useState } from 'react'; +import CommonImage from 'components/CommonImage'; + +interface DisplayImageProps { + name: string; + src?: string; + className?: string; + defaultIconClassName?: string; + width?: number; + height?: number; + alt?: string; + isCircle?: boolean; +} + +export default function DisplayImage({ + name, + src, + className, + defaultIconClassName, + width = 24, + height = 24, + alt, + isCircle = true, +}: DisplayImageProps) { + const [showIcon, setShowIcon] = useState(true); + + return ( +
+
+ {name?.charAt(0)} +
+ {showIcon && src && ( + { + setShowIcon(false); + }} + /> + )} +
+ ); +} diff --git a/src/components/DisplayImage/styles.module.less b/src/components/DisplayImage/styles.module.less new file mode 100644 index 00000000..e1a3b05d --- /dev/null +++ b/src/components/DisplayImage/styles.module.less @@ -0,0 +1,29 @@ +@import '/src/assets/theme/color.less'; + +.display-image { + position: relative; + flex-shrink: 0; +} + +.display-image-circle { + border-radius: 50%; + overflow: hidden; + + .common-image { + border-radius: 50%; + overflow: hidden; + } +} + +.default-text { + position: absolute; + top: 0; + left: 0; + right: 0; + border-radius: 50%; + overflow: hidden; + width: 16px; + height: 16px; + border: 1px solid @border-1; + background: @bg-15; +} diff --git a/src/components/DynamicArrow/index.tsx b/src/components/DynamicArrow/index.tsx new file mode 100644 index 00000000..7dd562d7 --- /dev/null +++ b/src/components/DynamicArrow/index.tsx @@ -0,0 +1,44 @@ +import { useMemo } from 'react'; +import clsx from 'clsx'; +import CommonImage from 'components/CommonImage'; +import { downBigIcon, downIcon, downSmallIcon } from 'assets/images'; +import styles from './styles.module.less'; + +export type TDynamicArrowSize = 'Big' | 'Normal' | 'Small'; + +type TDynamicArrow = { + className?: string; + iconClassName?: string; + isExpand?: boolean; + size?: TDynamicArrowSize; +}; + +export default function DynamicArrow({ className, iconClassName, isExpand = false, size = 'Normal' }: TDynamicArrow) { + const arrowIcon = useMemo(() => { + if (size === 'Big') { + return downBigIcon; + } + if (size === 'Small') { + return downSmallIcon; + } + return downIcon; + }, [size]); + + return ( + + + + ); +} diff --git a/src/components/DynamicArrow/styles.module.less b/src/components/DynamicArrow/styles.module.less new file mode 100644 index 00000000..ada88b6b --- /dev/null +++ b/src/components/DynamicArrow/styles.module.less @@ -0,0 +1,24 @@ +.dynamic-arrow { + box-sizing: border-box; + width: 16px; + height: 16px; +} + +.dynamic-arrow-big { + width: 20px; + height: 20px; +} + +.dynamic-arrow-small { + width: 12px; + height: 12px; +} + +.dynamic-arrow-icon { + width: 100%; + height: 100%; +} + +.dynamic-arrow-icon-rotate { + transform: rotate(180deg); +} diff --git a/src/components/EmptyDataBox/index.tsx b/src/components/EmptyDataBox/index.tsx new file mode 100644 index 00000000..fc55c08b --- /dev/null +++ b/src/components/EmptyDataBox/index.tsx @@ -0,0 +1,18 @@ +import styles from './styles.module.less'; +import clsx from 'clsx'; +import Empty from 'assets/images/empty.svg'; +import CommonImage from 'components/CommonImage'; + +export type TEmptyDataBoxProps = { + imageSrc?: string; + text?: React.ReactNode; +}; + +export default function EmptyDataBox({ imageSrc = Empty, text = 'No found' }: TEmptyDataBoxProps) { + return ( +
+ +
{text}
+
+ ); +} diff --git a/src/components/EmptyDataBox/styles.module.less b/src/components/EmptyDataBox/styles.module.less new file mode 100644 index 00000000..581b80ca --- /dev/null +++ b/src/components/EmptyDataBox/styles.module.less @@ -0,0 +1,27 @@ +@import '/src/assets/theme/color.less'; +@import 'src/assets/theme/constants.less'; + +.empty-data-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 72px 0; + color: @font-13; + .empty-data-box-icon { + width: 140px; + height: 140px; + } + .empty-data-box-text { + color: @font-13; + } +} + +@media screen and (max-width: @md-width) { + .empty-data-box { + .empty-data-box-icon { + width: 30vw; + height: 30vw; + } + } +} diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx new file mode 100644 index 00000000..4f198e97 --- /dev/null +++ b/src/components/Footer/index.tsx @@ -0,0 +1,55 @@ +import { memo } from 'react'; +import { useLanguage } from 'i18n'; +import Link from 'next/link'; +import CommonImage from 'components/CommonImage'; +import { aelfLogo } from 'assets/images'; +import styles from './styles.module.less'; +import { FOOTER_COMMUNITY_CONFIG, FOOTER_MENU_LIST_CONFIG } from 'constants/link'; +import useMediaQueries from 'hooks/useMediaQueries'; + +function Footer() { + const { t } = useLanguage(); + const isMd = useMediaQueries('md'); + + if (isMd) { + return null; + } + + return ( +
+
+
+ Powered by + +
+
+ {FOOTER_COMMUNITY_CONFIG.map((item) => { + return ( + + + + + + ); + })} +
+
+
+ {FOOTER_MENU_LIST_CONFIG.map((group, index) => ( +
+ {t(group.title)} + {group.menu.map((item) => { + return ( + + {t(item.label)} + + ); + })} +
+ ))} +
+
+ ); +} + +export default memo(Footer); diff --git a/src/components/Footer/styles.module.less b/src/components/Footer/styles.module.less new file mode 100644 index 00000000..e02aed71 --- /dev/null +++ b/src/components/Footer/styles.module.less @@ -0,0 +1,69 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.page-footer { + display: flex; + justify-content: space-between; + align-items: flex-start; + height: @footer-height; + padding: 24px 48px; + border-top: 1px solid @border-5; + background: rgb(255 255 255 / 95%); + color: @font-13; + font-size: 16px; + + .page-footer-left { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + gap: 8px; + height: 100%; + } + + .page-footer-powered { + display: flex; + gap: 8px; + align-items: center; + } + + .aelf-logo { + width: 53px; + height: 16px; + } + + .page-footer-community-group { + display: flex; + gap: 24px; + align-items: center; + } + + .page-footer-community { + width: 20px; + height: 20px; + cursor: pointer; + } + + .page-footer-right { + display: flex; + gap: 80px; + align-items: flex-start; + } + + .page-footer-menu { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + font-size: 14px; + line-height: 16px; + .font-family-medium(); + & > a { + color: @font-15; + } + .page-footer-menu-title { + color: @font-13; + } + } +} diff --git a/src/components/GoogleAnalytics/index.tsx b/src/components/GoogleAnalytics/index.tsx new file mode 100644 index 00000000..a6675cff --- /dev/null +++ b/src/components/GoogleAnalytics/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import Script from 'next/script'; +export default function GoogleAnalytics({ id }: { id?: string }) { + return ( + <> + + + ); +} diff --git a/src/components/Header/images/logo.svg b/src/components/Header/images/logo.svg new file mode 100644 index 00000000..77c37181 --- /dev/null +++ b/src/components/Header/images/logo.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Header/images/testLogo.svg b/src/components/Header/images/testLogo.svg new file mode 100644 index 00000000..001c7eee --- /dev/null +++ b/src/components/Header/images/testLogo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx new file mode 100644 index 00000000..52a4acd5 --- /dev/null +++ b/src/components/Header/index.tsx @@ -0,0 +1,375 @@ +import styles from './styles.module.less'; +import logo from './images/logo.svg'; +import testLogo from './images/testLogo.svg'; +import { + translateIcon, + translateBlueIcon, + arrowGrayIcon, + arrowBlueIcon, + checkBlueIcon, + menuIcon, + closeIcon, + communityIcon, + legalIcon, + aelfLogo, + listingIcon, +} from 'assets/images'; +import clsx from 'clsx'; +import { Button, Drawer, Dropdown, Menu } from 'antd'; +import { LOCAL_LANGUAGE } from 'i18n/config'; +import { useLanguage } from 'i18n'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { useEffect, useMemo, useState } from 'react'; +import CommonImage from 'components/CommonImage'; +import { useRouter } from 'next/router'; +import Link from 'next/link'; +import { IS_MAINNET } from 'constants/index'; +import { NAV_LIST, HEADER_COMMUNITY_CONFIG, LEGAL_MENU_CONFIG, ROUTE_PATHS, LISTING_MENU_CONFIG } from 'constants/link'; +import { setAccountModal, setWalletsModal } from 'contexts/useModal/actions'; +import { useModalDispatch } from 'contexts/useModal/hooks'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { AElfConnectorId, ChainType, EVMConnectorId, TONConnectorId } from 'types'; +import { useAelfLogin } from 'hooks/wallet'; +import WalletIcon from 'components/WalletIcon'; +import { shortenString } from 'utils'; +import { isELFChain } from 'utils/aelfUtils'; +import { formatAddress } from 'utils/chain'; +import { isPortkey } from 'utils/portkey'; +import { useConnect } from 'hooks/useConnect'; +import { isMobileDevices } from 'utils/isMobile'; + +function SelectLanguage() { + const { language, changeLanguage } = useLanguage(); + const menu = ( + { + return { + key: item.language, + label: item.title, + onClick: () => changeLanguage(item.language), + }; + })} + /> + ); + const showText = LOCAL_LANGUAGE.find((item) => item.language === language)?.title || LOCAL_LANGUAGE[0].title; + return ( + triggerNode}> +
+ + {showText} + +
+
+ ); +} + +function Logo({ clickable = false }: { clickable?: boolean }) { + const { push } = useRouter(); + return ( + { + if (clickable) { + push(ROUTE_PATHS.BRIDGE); + } + }} + /> + ); +} + +function ConnectWalletsButton() { + const { t } = useLanguage(); + const dispatch = useModalDispatch(); + const login = useAelfLogin(); + const { isHomogeneous } = useWallet(); + + return ( + + ); +} + +function WalletButton({ chainType }: { chainType?: ChainType }) { + const isMd = useMediaQueries('md'); + const { t } = useLanguage(); + const dispatch = useModalDispatch(); + const connect = useConnect(); + + const { fromWallet, toWallet, fromOptions } = useWallet(); + const wallet = fromOptions?.chainType === chainType ? fromWallet : toWallet; + const { walletType, chainId, account, connectorId } = wallet || {}; + const isELF = chainType === 'ELF'; + return account ? ( + + ) : ( + + ); +} + +function WalletButtonList() { + const { fromWallet, toWallet, fromOptions, toOptions } = useWallet(); + const { account: fromAccount } = fromWallet || {}; + const { account: toAccount } = toWallet || {}; + if (!fromAccount && !toAccount) { + return ; + } else { + return ( +
+ + +
+ ); + } +} + +interface IMobileDrawerMenuItem { + icon?: any; + label: string; + link?: string; + checked?: boolean; + onClick?: () => void; +} + +interface IMobileDrawerMenuConfigItem { + icon: any; + label: string; + children: IMobileDrawerMenuItem[]; +} + +function MobileDrawerMenu({ isDrawerVisible, onCloseDrawer }: { isDrawerVisible: boolean; onCloseDrawer(): void }) { + const { t } = useLanguage(); + const [openKeys, setOpenKeys] = useState([]); + const { language, changeLanguage } = useLanguage(); + + useEffect(() => { + if (!isDrawerVisible) { + setOpenKeys([]); + } + }, [isDrawerVisible]); + + const mobileDrawerMenuConfig: IMobileDrawerMenuConfigItem[] = useMemo( + () => [ + { + icon: translateIcon, + label: 'Language', + children: LOCAL_LANGUAGE.map((item) => ({ + label: item.title, + checked: item.language === language, + onClick: () => changeLanguage(item.language), + })), + }, + { + icon: listingIcon, + label: 'Listing', + children: LISTING_MENU_CONFIG, + }, + { + icon: communityIcon, + label: 'Community', + children: HEADER_COMMUNITY_CONFIG, + }, + { + icon: legalIcon, + label: 'Legal', + children: LEGAL_MENU_CONFIG, + }, + ], + [changeLanguage, language], + ); + + const items = useMemo( + () => + mobileDrawerMenuConfig.map(({ icon, label, children }) => ({ + key: label, + label: ( +
+ {icon && } + {t(label)} + +
+ ), + children: children?.map((child) => { + const isExternalLink = child.link?.startsWith('https:'); + return { + key: child.label, + label: ( + + onCloseDrawer()}> + {child.icon && } + {t(child.label)} + {child.checked && } + + + ), + onClick: child.onClick, + }; + }), + })), + [mobileDrawerMenuConfig, onCloseDrawer, openKeys, t], + ); + + return ( + } + items={items} + openKeys={openKeys} + onOpenChange={(keys) => setOpenKeys(keys)} + /> + ); +} + +function MobileHeader() { + const [visible, setVisible] = useState(false); + const { pathname, push } = useRouter(); + const { t } = useLanguage(); + return ( + <> +
+ setVisible(true)} + /> +
+ + setVisible(false)} + open={visible}> +
+ + setVisible(false)} + /> +
+ {NAV_LIST.map((item, index) => { + const isSelected = pathname === item.href; + return ( +
{ + push(item.href); + setVisible(false); + }}> + + {t(item.title)} +
+ ); + })} +
+
+ setVisible(false)} /> +
+
+ Powered by + +
+ + + ); +} + +export default function Header() { + const isMd = useMediaQueries('md'); + return ( +
+ {!isMd ? ( + <> + +
+ + +
+ + ) : ( + + )} +
+ ); +} diff --git a/src/components/Header/styles.module.less b/src/components/Header/styles.module.less new file mode 100644 index 00000000..1ceeeae0 --- /dev/null +++ b/src/components/Header/styles.module.less @@ -0,0 +1,194 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.header { + width: 100%; + height: @header-height; + padding: 19px 48px; + background: @bg-2; + border-bottom: 1px solid @border-5; +} +.header-right { + gap: 8px; +} +.wallet-button-list { + gap: 8px; + .account-button { + gap: 8px; + } + .mobile-wallet-button-content { + gap: 8px; + .wallet-icon:not(:first-child) { + margin-left: -4px; + } + } + .wallet-icon { + font-size: 16px; + } + .wallet-arrow-icon { + width: 16px; + height: 16px; + } +} +.language-selector { + gap: 8px; + height: 40px; + padding: 0 20px; + color: @font-14; + font-size: 14px; + font-weight: 500; + line-height: 22px; +} +.language-selector-translate-icon, +.language-selector-arrow-icon { + width: 16px; + height: 16px; +} +.dropdown { + justify-content: space-between; + align-items: center; +} +.dropdown-overlay { + overflow: hidden; + width: auto; + box-shadow: 0 9px 20px 8px @shadow-2; + border-radius: 8px; + :global { + .@{app-prefix}-dropdown-menu-item { + min-height: 40px; + &:hover:not(.@{app-prefix}-dropdown-menu-item-selected) { + background: @bg-3; + } + } + .@{app-prefix}-dropdown-menu-item-selected { + background: @bg-8; + } + } +} + +.mobile-header-left { + gap: 8px; +} +.menu-icon { + width: 24px; + height: 24px; +} +.mobile-drawer { + :global { + .@{app-prefix}-drawer-body { + display: flex; + flex-direction: column; + padding-top: 0; + padding-right: 0; + /* stylelint-disable-next-line function-no-unknown */ + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); + padding-left: 0; + } + } +} +.mobile-drawer-header { + position: sticky; + top: 0; + z-index: 10; + flex-shrink: 0; + width: 100%; + height: 56px; + padding: 12px 16px; + background-color: @bg-2; + border-bottom: 1px solid @border-6; +} +.close-icon { + width: 20px; + height: 20px; +} +.mobile-drawer-nav-item { + flex-shrink: 0; + gap: 12px; + height: 56px; + padding: 0 16px; + font-size: 16px; + font-weight: 500; + line-height: 24px; + color: @font-15; +} +.mobile-drawer-nav-item-selected { + background-color: @bg-15; + color: @font-16; +} +.nav-item-icon { + width: 20px; + height: 20px; +} +.divider { + margin: 16px 0; + border-bottom: 1px solid @border-6; +} +.mobile-drawer-menu-wrap { + flex: 1; +} +.mobile-drawer-menu { + border: none; + :global { + .@{app-prefix}-menu-submenu > .@{app-prefix}-menu-submenu-title, + .@{app-prefix}-menu-submenu > .@{app-prefix}-menu-sub > .@{app-prefix}-menu-item, + .@{app-prefix}-menu-item { + width: 100%; + height: 56px; + margin: 0; + padding: 0 16px !important; + } + .@{app-prefix}-menu-submenu > .@{app-prefix}-menu-sub > .@{app-prefix}-menu-item { + padding-left: 48px !important; + } + .@{app-prefix}-menu-submenu > .@{app-prefix}-menu-sub { + background-color: @bg-2 !important; + } + .@{app-prefix}-menu-submenu > .@{app-prefix}-menu-sub > .@{app-prefix}-menu-item:last-child { + border-bottom: 1px solid @border-6; + } + } +} +.mobile-drawer-menu-label-wrap { + gap: 12px; +} +.menu-item-icon { + width: 20px; + height: 20px; +} +.menu-item-text { + flex: 1; + color: @font-15; + font-size: 16px; + font-weight: 500; + line-height: 24px; +} +.menu-expand-icon, +.menu-checked-icon { + width: 16px; + height: 16px; +} +.menu-expand-icon-rotate { + transform: rotate(180deg); +} +.mobile-drawer-footer { + flex-shrink: 0; + gap: 8px; + margin: 24px 0; + color: @font-13; + font-size: 16px; + line-height: 26px; +} +.aelf-logo { + width: 53px; + height: 16px; + margin-top: 2px; +} + +@media screen and (max-width: @md-width) { + .header { + padding-left: 16px; + padding-right: 16px; + } +} diff --git a/src/components/IconFont/index.tsx b/src/components/IconFont/index.tsx new file mode 100644 index 00000000..6321779b --- /dev/null +++ b/src/components/IconFont/index.tsx @@ -0,0 +1,7 @@ +import createFromIconfontCN from '@ant-design/icons/lib/components/IconFont'; +const iconPark = 'https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/'; + +const IconFont = createFromIconfontCN({ + scriptUrl: `${iconPark}svg_16148_40.3be9d9afb9a72f0dbe477fdee5e5843e.js`, +}); +export default IconFont; diff --git a/src/components/LinkForBlank/index.tsx b/src/components/LinkForBlank/index.tsx new file mode 100644 index 00000000..b3dd6787 --- /dev/null +++ b/src/components/LinkForBlank/index.tsx @@ -0,0 +1,18 @@ +import Link from 'next/link'; +import { ReactNode } from 'react'; + +interface ILinkForBlankProps { + href: string; + className?: string; + ariaLabel?: string; + element?: ReactNode; +} +export default function LinkForBlank({ href, className, ariaLabel, element }: ILinkForBlankProps) { + const isExternalLink = !href.startsWith('/'); + const target = isExternalLink ? '_blank' : '_self'; + return ( + + {element} + + ); +} diff --git a/src/components/Loading/LoadingIcon/index.tsx b/src/components/Loading/LoadingIcon/index.tsx new file mode 100644 index 00000000..4df70f5e --- /dev/null +++ b/src/components/Loading/LoadingIcon/index.tsx @@ -0,0 +1,7 @@ +import CommonImage from 'components/CommonImage'; +import styles from './styles.module.less'; +import { loadingIcon } from 'assets/images'; + +export function LoadingIcon() { + return ; +} diff --git a/src/components/Loading/LoadingIcon/styles.module.less b/src/components/Loading/LoadingIcon/styles.module.less new file mode 100644 index 00000000..e2c36572 --- /dev/null +++ b/src/components/Loading/LoadingIcon/styles.module.less @@ -0,0 +1,15 @@ +@import '/src/assets/theme/color.less'; + +.loading-icon { + width: 48px; + height: 48px; + animation: spin 1.5s linear infinite; +} +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/Loading/LoadingModal/index.tsx b/src/components/Loading/LoadingModal/index.tsx new file mode 100644 index 00000000..342b3fb7 --- /dev/null +++ b/src/components/Loading/LoadingModal/index.tsx @@ -0,0 +1,37 @@ +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import CommonModal from 'components/CommonModal'; +import styles from './styles.module.less'; +import { LoadingIcon } from '../LoadingIcon'; + +export interface ILoadingModalProps { + open?: boolean; + title?: string; + description?: string; + hideTitle?: boolean; + hideDescription?: boolean; +} + +export default function LoadingModal({ open, title, description, hideTitle, hideDescription }: ILoadingModalProps) { + const { t } = useLanguage(); + return ( + +
+ + {!hideDescription && ( +
+ {description || + t( + "The Transaction fee also needs to be approved. Don't refresh or close the page until the operation is complete.", + )} +
+ )} +
+
+ ); +} diff --git a/src/components/Loading/LoadingModal/styles.module.less b/src/components/Loading/LoadingModal/styles.module.less new file mode 100644 index 00000000..d8b03518 --- /dev/null +++ b/src/components/Loading/LoadingModal/styles.module.less @@ -0,0 +1,31 @@ +@import '/src/assets/theme/color.less'; + +.loading-modal { + width: 380px; + + :global { + .@{app-prefix}-modal-body { + width: 100%; + min-width: auto; + } + } + .loading-modal-body { + gap: 10px; + padding: 16px 0; + } + .loading-text { + color: @font-13; + text-align: left; + line-height: 20px; + } + &-hide-title { + :global { + .@{app-prefix}-modal-header { + display: none; + } + .@{app-prefix}-modal-body { + padding-top: 24px; + } + } + } +} diff --git a/src/components/Loading/ResultModal/index.tsx b/src/components/Loading/ResultModal/index.tsx new file mode 100644 index 00000000..3e032f20 --- /dev/null +++ b/src/components/Loading/ResultModal/index.tsx @@ -0,0 +1,68 @@ +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import { Button } from 'antd'; +import CommonModal from 'components/CommonModal'; +import CommonImage from 'components/CommonImage'; +import { checkFilledIcon, errorFilledIcon } from 'assets/images'; +import styles from './styles.module.less'; +import { useCallback, useMemo } from 'react'; + +export enum ResultType { + APPROVED = 'approved', + REJECTED = 'rejected', +} + +export interface IResultModalProps { + open?: boolean; + type?: ResultType; + onClose?: () => void; + onRetry?: () => void; + approvedDescription?: string; +} + +export default function ResultModal({ open, type, onClose, onRetry, approvedDescription }: IResultModalProps) { + const { t } = useLanguage(); + + const handleRetry = useCallback(() => { + onClose?.(); + onRetry?.(); + }, [onClose, onRetry]); + + const resultModalConfig = useMemo( + () => ({ + [ResultType.APPROVED]: { + title: 'Transaction approved', + icon: checkFilledIcon, + description: approvedDescription || 'The transaction has been approved.', + buttonProps: { + children: t('Close'), + onClick: onClose, + }, + }, + [ResultType.REJECTED]: { + title: 'Transaction rejected', + icon: errorFilledIcon, + description: 'The transaction was not signed. It was rejected.', + buttonProps: { + children: t('Retry'), + onClick: handleRetry, + }, + }, + }), + [approvedDescription, handleRetry, onClose, t], + ); + + const config = useMemo(() => resultModalConfig[type || ResultType.APPROVED], [resultModalConfig, type]); + + return ( + +
+
+ +
{t(config.description)}
+
+
+
+ ); +} diff --git a/src/components/Loading/ResultModal/styles.module.less b/src/components/Loading/ResultModal/styles.module.less new file mode 100644 index 00000000..654f544e --- /dev/null +++ b/src/components/Loading/ResultModal/styles.module.less @@ -0,0 +1,29 @@ +@import '/src/assets/theme/color.less'; + +.result-modal { + :global { + .@{app-prefix}-modal-body { + width: 100%; + min-width: auto; + } + } + .result-modal-body { + gap: 16px; + } + .result-description-wrap { + gap: 10px; + padding: 16px 0; + } + .result-icon { + width: 64px; + height: 64px; + } + .result-description { + color: @font-13; + text-align: center; + line-height: 20px; + } + .result-button { + width: 100%; + } +} diff --git a/src/components/Loading/index.tsx b/src/components/Loading/index.tsx new file mode 100644 index 00000000..345d0a51 --- /dev/null +++ b/src/components/Loading/index.tsx @@ -0,0 +1,21 @@ +import { useState, useCallback, useEffect } from 'react'; +import LoadingModal from './LoadingModal'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; + +export default function Loading() { + const [open, setOpen] = useState(false); + + const setLoadingHandler = useCallback((isLoading: boolean) => { + setOpen(isLoading); + }, []); + + useEffect(() => { + const { remove } = eBridgeEventBus.GlobalLoading.addListener(setLoadingHandler); + + return () => { + remove(); + }; + }); + + return ; +} diff --git a/src/components/MainContentHeader/index.tsx b/src/components/MainContentHeader/index.tsx new file mode 100644 index 00000000..82dab331 --- /dev/null +++ b/src/components/MainContentHeader/index.tsx @@ -0,0 +1,73 @@ +import { ReactNode, useState } from 'react'; +import clsx from 'clsx'; +import { Tooltip } from 'antd'; +import CommonImage from 'components/CommonImage'; +import { infoCircleIcon } from 'assets/images'; +import { isMobileDevices } from 'utils/isMobile'; +import styles from './styles.module.less'; + +interface IMainContentHeaderProps { + className?: string; + wrap?: boolean; + title: string; + rightEle?: ReactNode; + tipConfig?: { + label: string; + } & ({ content: React.ReactNode; onClick?: never } | { content?: never; onClick?: () => void }); +} + +export default function MainContentHeader({ className, wrap, title, tipConfig, rightEle }: IMainContentHeaderProps) { + const [isTooltipOpen, setIsTooltipOpen] = useState(false); + + const isMobile = isMobileDevices(); + + const handleTipClick = () => { + if (tipConfig?.onClick) { + tipConfig.onClick(); + } else if (isMobile) { + setIsTooltipOpen(true); + } + }; + + const handleMouseEnter = () => { + if (!tipConfig?.onClick) { + setIsTooltipOpen(true); + } + }; + + const handleMouseLeave = () => { + if (!tipConfig?.onClick) { + setIsTooltipOpen(false); + } + }; + + return ( +
+
{title}
+ {rightEle} + {tipConfig && ( +
+ setIsTooltipOpen(open)}> + + + {tipConfig.label} +
+ )} +
+ ); +} diff --git a/src/components/MainContentHeader/styles.module.less b/src/components/MainContentHeader/styles.module.less new file mode 100644 index 00000000..13c32a98 --- /dev/null +++ b/src/components/MainContentHeader/styles.module.less @@ -0,0 +1,33 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.main-content-header { + display: flex; + gap: 10px; + .header-title { + flex: 1; + color: @font-15; + font-size: 24px; + font-weight: 700; + .font-family-semibold(); + } + .header-tip { + gap: 4px; + color: @font-13; + line-height: 22px; + } + .info-icon { + width: 16px; + height: 16px; + } +} + +.main-content-header-wrap { + flex-direction: column; + align-items: flex-start; +} + +.main-content-header-nowrap { + justify-content: space-between; + align-items: center; +} diff --git a/src/components/Mask/images/upgrade.svg b/src/components/Mask/images/upgrade.svg new file mode 100644 index 00000000..cde89550 --- /dev/null +++ b/src/components/Mask/images/upgrade.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Mask/images/upgrade_phone.svg b/src/components/Mask/images/upgrade_phone.svg new file mode 100644 index 00000000..032aa107 --- /dev/null +++ b/src/components/Mask/images/upgrade_phone.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Mask/images/warning_6.svg b/src/components/Mask/images/warning_6.svg new file mode 100644 index 00000000..b31d0cb7 --- /dev/null +++ b/src/components/Mask/images/warning_6.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/Mask/index.tsx b/src/components/Mask/index.tsx new file mode 100644 index 00000000..61b2cdda --- /dev/null +++ b/src/components/Mask/index.tsx @@ -0,0 +1,33 @@ +import CommonImage from 'components/CommonImage'; +import { useLanguage } from 'i18n'; +import warnIcon from './images/warning_6.svg'; +import upgradeImg from './images/upgrade.svg'; +import styles from './styles.module.less'; +import useMediaQueries from 'hooks/useMediaQueries'; + +export default function Mask() { + const { t } = useLanguage(); + const isMd = useMediaQueries('md'); + return ( +
+
+
+ +

{t('Upgrade Title')}

+
+ {Object.values(t('Upgrade Notice', { returnObjects: true })).map((ele: any, idx: number) => { + return ( +
+ {Array.isArray(ele) + ? ele.map((segment, segIdx) => { + return

{segment}

; + }) + : ele} +
+ ); + })} +
+ {isMd ?
: } +
+ ); +} diff --git a/src/components/Mask/styles.module.less b/src/components/Mask/styles.module.less new file mode 100644 index 00000000..3946533c --- /dev/null +++ b/src/components/Mask/styles.module.less @@ -0,0 +1,86 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + +.unique-mask { + + .font-family-regular(); + // padding-top: calc((100vh - @header-height - 400px ) / 2 ); + padding-top: 64px; + padding-left: 24px; + padding-right: 24px; + color: @font-5; + line-height: 22px; + display: flex; + align-items: center; + justify-content: center; + .content{ + flex: 1; + max-width: 600px; + min-width: 330px; + padding-right: 50px; + z-index: 1; + } + + .notice-title{ + display: flex; + align-items: center; + padding: 14px 0; + .warn-icon{ + width: 24px; + height: 24px; + } + h1{ + .font-family-medium(); + + padding-left: 8px; + color: @font-2; + font-size: 24px; + line-height: 32px; + } + } + + section{ + margin-top: 8px; + } + .link{ + font-weight: 600; + color: rgb(77 106 236 / 100%); + } + .upgrade-img{ + width: 400px; + height: 400px; + } +} + +@media screen and (max-width: @md-width) { + .unique-mask{ + flex-direction: column; + padding-top: 24px; + padding-left: 0; + padding-right: 0; + .content{ + width: 100%; + padding: 0 24px; + } + .upgrade-img{ + width: 100vw; + height: 328px; + background-image: url('./images/upgrade_phone.svg'); + background-repeat: no-repeat; + background-position: center top; + background-size: 480px 480px; + position: absolute; + bottom: 0; + left: 0; + } + } +} + +@media screen and (max-width: 375px) { + .unique-mask{ + .upgrade-img{ + height: 260px; + } + } +} \ No newline at end of file diff --git a/src/components/Nav/index.tsx b/src/components/Nav/index.tsx new file mode 100644 index 00000000..c577d036 --- /dev/null +++ b/src/components/Nav/index.tsx @@ -0,0 +1,38 @@ +import { memo } from 'react'; +import clsx from 'clsx'; +import { useRouter } from 'next/router'; +import { useLanguage } from 'i18n'; +import CommonImage from 'components/CommonImage'; +import { NAV_LIST } from 'constants/link'; +import styles from './styles.module.less'; + +function Nav() { + const { pathname, push } = useRouter(); + const { t } = useLanguage(); + return ( +
+ {NAV_LIST.map((item, index) => { + const isSelected = pathname === item.href; + return ( +
{ + push(item.href); + }}> + + {t(item.title)} +
+ ); + })} +
+ ); +} + +export default memo(Nav); diff --git a/src/components/Nav/styles.module.less b/src/components/Nav/styles.module.less new file mode 100644 index 00000000..561c1f52 --- /dev/null +++ b/src/components/Nav/styles.module.less @@ -0,0 +1,31 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.nav-list { + flex-shrink: 0; + gap: 8px; + width: 200px; + padding: 16px 0; +} + +.nav-item { + gap: 8px; + height: 56px; + padding: 16px 0 16px 48px; + border-radius: 0 12px 12px 0; + font-size: 16px; + font-weight: 500; + line-height: 24px; + color: @font-15; + .font-family-medium(); +} + +.nav-item-selected { + background: rgb(91 142 244 / 12%); + color: @font-14; +} + +.nav-item-icon { + width: 24px; + height: 24px; +} diff --git a/src/components/Network/Network.tsx b/src/components/Network/Network.tsx new file mode 100644 index 00000000..ad84851f --- /dev/null +++ b/src/components/Network/Network.tsx @@ -0,0 +1,62 @@ +import { Dropdown, Menu, Row } from 'antd'; +import { useMemo } from 'react'; +import { switchNetwork } from '../../utils/network'; +import { ChainId, NetworkType } from 'types'; +import { getIconByChainId, getNameByChainId } from 'utils/chain'; +import IconFont from 'components/IconFont'; +import styles from './styles.module.less'; +import clsx from 'clsx'; +import { arrowIcon } from 'assets/images'; +import CommonImage from 'components/CommonImage'; + +export default function Network({ + networkList, + overlayClassName, + onChange, + className, + chainId, +}: { + overlayClassName?: string | undefined; + networkList: NetworkType[]; + onChange?: (i: NetworkType['info']) => void; + className?: string; + chainId?: ChainId; +}) { + const menu = useMemo(() => { + return ( + { + return { + icon: , + key: i.info.chainId, + label: i.title, + onClick: () => (onChange || switchNetwork)(i.info), + }; + })} + /> + ); + }, [chainId, networkList, onChange]); + + const iconProps = useMemo(() => { + if (!chainId) return undefined; + return getIconByChainId(chainId); + }, [chainId]); + + const name = getNameByChainId(chainId); + + return ( + triggerNode}> + + +
{name || 'Wrong Network'}
+ +
+
+ ); +} diff --git a/src/components/Network/index.tsx b/src/components/Network/index.tsx new file mode 100644 index 00000000..b9ce800d --- /dev/null +++ b/src/components/Network/index.tsx @@ -0,0 +1,6 @@ +import dynamic from 'next/dynamic'; + +const Network = dynamic(import('./Network'), { + ssr: false, +}); +export default Network; diff --git a/src/components/Network/styles.module.less b/src/components/Network/styles.module.less new file mode 100644 index 00000000..5e7b2c57 --- /dev/null +++ b/src/components/Network/styles.module.less @@ -0,0 +1,50 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +.dropdown { + gap: 8px; + padding: 12px; + border: 1px solid @border-5; + border-radius: 6px; + .network-icon { + width: 24px; + height: 24px; + } + .network-name { + flex: 1; + color: @font-15; + .text-overflow(1); + + white-space: nowrap; + font-size: 14px; + font-weight: 500; + line-height: 22px; + } + .arrow-icon { + width: 16px; + height: 16px; + } + :global { + .anticon { + font-size: 24px; + } + } +} +.dropdown-overlay { + width: auto; + border: 1px solid @border-5; + border-radius: 6px; + :global { + .@{app-prefix}-dropdown-menu { + border-radius: 6px; + } + .@{app-prefix}-dropdown-menu-item { + min-height: 40px; + &:hover:not(.@{app-prefix}-dropdown-menu-item-selected) { + background: @bg-3; + } + } + .@{app-prefix}-dropdown-menu-item-selected { + background: @bg-8; + } + } +} diff --git a/src/components/Notification/images/close.svg b/src/components/Notification/images/close.svg new file mode 100644 index 00000000..1549b4e6 --- /dev/null +++ b/src/components/Notification/images/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Notification/images/eBridge_logo.svg b/src/components/Notification/images/eBridge_logo.svg new file mode 100644 index 00000000..5480cd18 --- /dev/null +++ b/src/components/Notification/images/eBridge_logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Notification/images/upgrade.svg b/src/components/Notification/images/upgrade.svg new file mode 100644 index 00000000..cde89550 --- /dev/null +++ b/src/components/Notification/images/upgrade.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Notification/images/upgrade_phone.svg b/src/components/Notification/images/upgrade_phone.svg new file mode 100644 index 00000000..032aa107 --- /dev/null +++ b/src/components/Notification/images/upgrade_phone.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Notification/images/warning_6.svg b/src/components/Notification/images/warning_6.svg new file mode 100644 index 00000000..b31d0cb7 --- /dev/null +++ b/src/components/Notification/images/warning_6.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/Notification/index.tsx b/src/components/Notification/index.tsx new file mode 100644 index 00000000..82596a80 --- /dev/null +++ b/src/components/Notification/index.tsx @@ -0,0 +1,72 @@ +import { notification, Drawer } from 'antd'; +import CommonImage from 'components/CommonImage'; +import { useEffect, useMemo, useState } from 'react'; +import { useLanguage } from 'i18n'; +import styles from './styles.module.less'; +import warnIcon from './images/warning_6.svg'; +import closeIcon from './images/close.svg'; + +const URL = 'https://t.me/eBridge_official'; + +function useBoliplate() { + const { t } = useLanguage(); + return useMemo( + () => ({ + title: ( +
+ + {t('Notification Title')} +
+ ), + content: ( +
+
{t('Notification Content Section 1')}
+
{t('Notification Content Section 2')}
+ + {URL} + +
+ ), + closeIcon: , + }), + [t], + ); +} + +export function Notification() { + const { title, content, closeIcon } = useBoliplate(); + useEffect(() => { + notification.open({ + className: styles['notification-wrap'], + message: title, + description: content, + closeIcon: closeIcon, + duration: null, + placement: 'bottomRight', + bottom: 16, + key: 'eBridge', + }); + }, [title, content, closeIcon]); + + return null; +} + +export function NotificationForPhone() { + const [visible, setVisible] = useState(true); + const { title, content, closeIcon } = useBoliplate(); + const onClose = () => { + setVisible(false); + }; + return ( + + {content} + + ); +} diff --git a/src/components/Notification/styles.module.less b/src/components/Notification/styles.module.less new file mode 100644 index 00000000..94728fb9 --- /dev/null +++ b/src/components/Notification/styles.module.less @@ -0,0 +1,105 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.common-title-wrap{ + .title-wrap{ + display: flex; + align-items: center; + .warn-icon{ + width: 18px; + height: 18px; + margin-right: 10px; + } + } + .section2{ + margin-top: 8px; + } + .link{ + font-weight: 600; + color: rgb(77 106 236 / 100%); + } + .close-icon{ + width: 16px; + height: 16px; + } +} +.common-bg{ + background-image: url('./images/eBridge_logo.svg'); + background-repeat: no-repeat; + background-position: 10px center; + background-size: 144px 144px; + background-color: rgb(237 240 253 / 100%); +} + +.notification-wrap { + width: 480px; + height: 198px; + color: @font-5; + border-radius: 8px; + box-shadow: 0 5px 12px 4px rgb(0 0 0 / 15%); + padding: 0 0 0 80px; + line-height: 22px; + .common-bg(); + .font-family-regular(); + .common-title-wrap(); + + :global { + .@{app-prefix}-notification-notice-content{ + background-color: rgb(255 255 255 / 100%); + height: 100%; + } + .@{app-prefix}-notification-notice-message{ + .font-family-medium(); + + color: @font-2; + font-size: 18px; + padding: 16px 24px 14px 16px; + margin-bottom: 0; + } + .@{app-prefix}-notification-notice-description{ + padding-left: 16px; + padding-right: 54px; + } + } +} + +@media screen and (max-width: @md-width) { + + .notification-phone-wrap { + color: @font-5; + margin: 0; + max-width: @md-width; + .common-title-wrap(); + :global{ + .@{app-prefix}-drawer-content-wrapper{ + .common-bg(); + + border-top-left-radius: 8px; + border-top-right-radius: 8px; + padding-left: 80px; + overflow: hidden; + height: 260px; + } + .@{app-prefix}-drawer-header{ + padding: 14px 16px; + padding-right: 12px; + border: none; + } + .@{app-prefix}-drawer-body{ + padding: 0 54px 0 16px; + border: none; + } + .@{app-prefix}-drawer-header-title{ + flex-direction: row-reverse; + } + .@{app-prefix}-drawer-title{ + .font-family-medium(); + + line-height: 28px; + color: @font-2; + font-size: 18px; + } + } + } +} \ No newline at end of file diff --git a/src/components/PageHead/index.tsx b/src/components/PageHead/index.tsx new file mode 100644 index 00000000..b2456909 --- /dev/null +++ b/src/components/PageHead/index.tsx @@ -0,0 +1,31 @@ +import GoogleAnalytics from 'components/GoogleAnalytics'; +import Head from 'next/head'; +import React from 'react'; +export type DefaultHeadProps = { title?: string; description?: string }; + +export default function PageHead(props: DefaultHeadProps) { + return ( + <> + + + + ); +} + +export function DefaultHead({ + title = 'eBridge: Cross-chain Bridge', + description = 'Web3 cross-chain bridge that facilitates seamless token transfer between the aelf blockchain and Ethereum Virtual Machine (EVM) compatible networks.', +}: DefaultHeadProps) { + return ( + + {title} + + + + + + ); +} diff --git a/src/components/PartialLoading/data.json b/src/components/PartialLoading/data.json new file mode 100644 index 00000000..1066caf2 --- /dev/null +++ b/src/components/PartialLoading/data.json @@ -0,0 +1,133 @@ +{ + "v": "5.6.9", + "fr": 25, + "ip": 0, + "op": 47, + "w": 500, + "h": 500, + "nm": "loading", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "loading", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 0, "k": 0, "ix": 10 }, + "p": { "a": 0, "k": [250, 250, 0], "ix": 2 }, + "a": { "a": 0, "k": [-16, -36, 0], "ix": 1 }, + "s": { "a": 0, "k": [92.843, 92.843, 100], "ix": 6 } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "d": 1, + "ty": "el", + "s": { "a": 0, "k": [447.14, 447.14], "ix": 2 }, + "p": { "a": 0, "k": [0, 0], "ix": 3 }, + "nm": "circle", + "mn": "ADBE Vector Shape - Ellipse", + "hd": false + }, + { + "ty": "st", + "c": { "a": 0, "k": [0.156862745098, 0.36862745098, 1, 1], "ix": 3 }, + "o": { "a": 0, "k": 100, "ix": 4 }, + "w": { "a": 0, "k": 76, "ix": 5 }, + "lc": 2, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "stroke", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { "a": 0, "k": [-16, -36], "ix": 2 }, + "a": { "a": 0, "k": [0, 0], "ix": 1 }, + "s": { "a": 0, "k": [100, 100], "ix": 3 }, + "r": { "a": 0, "k": 0, "ix": 6 }, + "o": { "a": 0, "k": 100, "ix": 7 }, + "sk": { "a": 0, "k": 0, "ix": 4 }, + "sa": { "a": 0, "k": 0, "ix": 5 }, + "nm": "Transform" + } + ], + "nm": "circle", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tm", + "s": { "a": 0, "k": 0, "ix": 1 }, + "e": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "t": 0, + "s": [1] + }, + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "t": 15, + "s": [50] + }, + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "t": 27, + "s": [70] + }, + { "t": 46, "s": [1] } + ], + "ix": 2 + }, + "o": { + "a": 1, + "k": [ + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "t": 0, + "s": [0] + }, + { + "i": { "x": [0.833], "y": [0.833] }, + "o": { "x": [0.167], "y": [0.167] }, + "t": 27, + "s": [410] + }, + { "t": 46, "s": [720] } + ], + "ix": 3 + }, + "m": 1, + "ix": 2, + "nm": "road", + "mn": "ADBE Vector Filter - Trim", + "hd": false + } + ], + "ip": 0, + "op": 51, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} diff --git a/src/components/PartialLoading/index.tsx b/src/components/PartialLoading/index.tsx new file mode 100644 index 00000000..34d3b1c9 --- /dev/null +++ b/src/components/PartialLoading/index.tsx @@ -0,0 +1,35 @@ +import { useRef, useEffect } from 'react'; +import clsx from 'clsx'; +import lottie, { AnimationItem } from 'lottie-web'; +import animationData from './data.json'; +import styles from './styles.module.less'; + +interface PartialLoadingProps { + className?: string; +} + +export default function PartialLoading({ className }: PartialLoadingProps) { + const containerRef = useRef(null); + const animation = useRef(null); + + useEffect(() => { + if (!animation.current) { + animation.current = lottie.loadAnimation({ + container: containerRef.current as HTMLDivElement, + renderer: 'svg', + loop: true, + autoplay: true, + animationData: animationData, + }); + } + return () => { + if (animation.current) { + animation.current.stop(); + animation.current.destroy(); + animation.current = null; + } + }; + }, []); + + return
; +} diff --git a/src/components/PartialLoading/styles.module.less b/src/components/PartialLoading/styles.module.less new file mode 100644 index 00000000..86707d23 --- /dev/null +++ b/src/components/PartialLoading/styles.module.less @@ -0,0 +1,4 @@ +.partial-loading { + width: 14px; + height: 14px; +} diff --git a/src/components/Provider/index.tsx b/src/components/Provider/index.tsx new file mode 100644 index 00000000..8f630f09 --- /dev/null +++ b/src/components/Provider/index.tsx @@ -0,0 +1,48 @@ +import { ConfigProvider } from 'antd'; +import { prefixCls } from 'constants/misc'; +import ModalProvider from 'contexts/useModal'; +import StoreProvider from 'contexts/useStore'; +import Web3Provider from 'components/Web3Provider'; +import WalletProvider from 'contexts/useWallet'; +import AElfContractProvider from 'contexts/useAElfContract'; +import ChainProvider from 'contexts/useChain'; +import WhitelistProvider from 'contexts/useWhitelist'; +import PoolsProvider from 'contexts/usePools'; +import TokenProvider from 'contexts/useToken'; +import Modals from 'modals'; +import type { ReactNode } from 'react'; +import { initLanguage, useLanguage } from 'i18n'; +import { ANTD_LOCAL } from 'i18n/config'; +import { TonConnectUIProvider } from '@tonconnect/ui-react'; + +ConfigProvider.config({ prefixCls }); +initLanguage(localStorage); +export default function Provider({ children }: { children: ReactNode }) { + const { language } = useLanguage(); + return ( + + + + + + + + + + + + + {children} + + + + + + + + + + + + ); +} diff --git a/src/components/Remind/index.tsx b/src/components/Remind/index.tsx new file mode 100644 index 00000000..a5405d82 --- /dev/null +++ b/src/components/Remind/index.tsx @@ -0,0 +1,51 @@ +import clsx from 'clsx'; +import styles from './styles.module.less'; +import CommonImage from 'components/CommonImage'; +import { errorFilledIcon, infoCircleFillGrayIcon, infoCircleFillIcon } from 'assets/images'; + +export enum RemindType { + BRAND = 'brand', + INFO = 'info', + WARNING = 'warning', + ERROR = 'error', +} + +const ICON_SRC = { + [RemindType.BRAND]: infoCircleFillIcon, + [RemindType.INFO]: infoCircleFillGrayIcon, + [RemindType.WARNING]: infoCircleFillIcon, + [RemindType.ERROR]: errorFilledIcon, +}; + +export default function Remind({ + className, + iconClassName, + isShowIcon = true, + isCard = true, + isBorder = true, + type = RemindType.BRAND, + children, +}: { + className?: string; + iconClassName?: string; + isShowIcon?: boolean; + isCard?: boolean; + isBorder?: boolean; + type?: RemindType; + children: React.ReactNode; +}) { + return ( +
+ {isShowIcon && ( + + )} +
{children}
+
+ ); +} diff --git a/src/components/Remind/styles.module.less b/src/components/Remind/styles.module.less new file mode 100644 index 00000000..8a8c54a2 --- /dev/null +++ b/src/components/Remind/styles.module.less @@ -0,0 +1,64 @@ +@import '/src/assets/theme/color.less'; + +.remind-card { + display: flex; + flex-direction: row; + align-items: flex-start; + padding: 8px 12px; + border-radius: 8px; + font-size: 14px; + line-height: 22px; + + .icon { + margin: 3px 0; + width: 16px; + height: 16px; + } +} + +.remind-text { + display: flex; + flex-direction: row; + align-items: center; + color: @font-2; +} + +.remind-border { + border: 1px solid @border-6; +} + +.text { + margin-left: 8px; + color: @font-13; +} + +.remind-brand { + background-color: @bg-11; + border-color: @border-3; + + .text { + color: @font-2; + } +} + +.remind-info { + background-color: @bg-15; +} + +.remind-warning { + background-color: @bg-10; + border-color: @font-7; + + .text { + color: @font-2; + } +} + +.remind-error { + background-color: @bg-22; + border: none; + + .text { + color: @font-15; + } +} diff --git a/src/components/ScrollToTop/index.tsx b/src/components/ScrollToTop/index.tsx new file mode 100644 index 00000000..95f03627 --- /dev/null +++ b/src/components/ScrollToTop/index.tsx @@ -0,0 +1,18 @@ +import { useRouter } from 'next/router'; +import { useRef } from 'react'; +import { useUpdateEffect } from 'react-use'; + +export default function ScrollToTop() { + const router = useRouter(); + + const scrollRef = useRef(); + + useUpdateEffect(() => { + if (!scrollRef.current) { + scrollRef.current = document.getElementsByClassName('page-body-wrap')[0]; + } + scrollRef.current?.scrollTo(0, 0); + }, [router]); + + return null; +} diff --git a/src/components/TokenLogo/index.tsx b/src/components/TokenLogo/index.tsx new file mode 100644 index 00000000..bc52c591 --- /dev/null +++ b/src/components/TokenLogo/index.tsx @@ -0,0 +1,65 @@ +import clsx from 'clsx'; +import type { ImageProps } from 'next/image'; +import Image from 'next/image'; +import { useMemo, useState } from 'react'; +import { ChainId } from 'types'; +import { getTokenLogoURL } from 'utils'; +import styles from './styles.module.less'; +import { useGetTokenInfoByWhitelist } from 'hooks/token'; +const BAD_SRCS: { [src: string]: true } = {}; + +export default function TokenLogo({ + chainId, + symbol, + className, + src, + alt = 'logo', + layout = 'fill', + style, + onClick, + ...props +}: { + style?: ImageProps['style']; + layout?: ImageProps['layout']; + alt?: string; + src?: string; + chainId?: ChainId; + symbol?: string; + className?: string; + onClick?: ImageProps['onClick']; +}) { + const getTokenInfoByWhitelist = useGetTokenInfoByWhitelist(); + const memoSrc = useMemo(() => { + if (src) return src; + return getTokenInfoByWhitelist(chainId, symbol)?.icon ?? getTokenLogoURL(symbol, chainId); + }, [chainId, getTokenInfoByWhitelist, src, symbol]); + const [, refresh] = useState(0); + const tmpSrc = BAD_SRCS[memoSrc] ? undefined : memoSrc; + return ( +
+ {!tmpSrc ? ( + {symbol?.[0]} + ) : ( + { + if (memoSrc) BAD_SRCS[memoSrc] = true; + refresh((i) => i + 1); + }} + src={memoSrc} + layout={layout} + alt={alt} + /> + )} +
+ ); +} diff --git a/src/components/TokenLogo/styles.module.less b/src/components/TokenLogo/styles.module.less new file mode 100644 index 00000000..c7df30c2 --- /dev/null +++ b/src/components/TokenLogo/styles.module.less @@ -0,0 +1,18 @@ +@import '/src/assets/theme/color.less'; +.token-logo { + width: 28px; + height: 28px; + border-radius: 14px; + overflow: hidden; + position: relative; +} +.token-bad { + border: 1px solid @border-3; + display: flex; + align-items: center; + justify-content: center; + span { + color: @font-3; + font-size: 14px; + } +} diff --git a/src/components/TonWalletList/index.tsx b/src/components/TonWalletList/index.tsx new file mode 100644 index 00000000..ed4fa629 --- /dev/null +++ b/src/components/TonWalletList/index.tsx @@ -0,0 +1,111 @@ +import { Button } from 'antd'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useModal } from 'contexts/useModal'; +import IconFont from 'components/IconFont'; +import { SUPPORTED_WALLETS } from 'constants/wallets'; +import CommonMessage from 'components/CommonMessage'; +import styles from './styles.module.less'; +import { TelegramPlatform } from 'utils/telegram/telegram'; +import { ChainType, TWalletConnectorId } from 'types'; +import { useConnect } from 'hooks/useConnect'; +import { handleErrorMessage } from 'utils/error'; + +export default function WalletList({ onFinish }: { onFinish?: () => void }) { + const [{ walletWallet }] = useModal(); + const { connectorId, account, isActive } = walletWallet || {}; + const [loading, setLoading] = useState(); + + const onCancel = useCallback(() => { + setLoading(undefined); + onFinish?.(); + }, [onFinish]); + + const timerRef = useRef(); + useEffect(() => { + if (TelegramPlatform.isTelegramPlatform()) { + timerRef.current = setInterval(() => { + const wcmModalNode = document.getElementsByTagName('wcm-modal'); + const idIsWcmModalElement = wcmModalNode?.[0]?.shadowRoot?.querySelector('#wcm-modal'); + const wcmModalRouterNode = idIsWcmModalElement?.getElementsByTagName('wcm-modal-router'); + const wcmModalRouterNodeShadowRoot = wcmModalRouterNode?.[0]?.shadowRoot?.querySelector('.wcm-content'); + if (TelegramPlatform.isTelegramPlatformWeb() || TelegramPlatform.isTelegramPlatformDesktop()) { + wcmModalRouterNodeShadowRoot?.setAttribute('style', 'height: 70vh; min-height: 400px; overflow-y: auto'); + } + + const wcmConnectWalletViewNode = wcmModalRouterNodeShadowRoot?.getElementsByTagName('wcm-connect-wallet-view'); + const wcmConnectWalletViewNodeShadowRoot = wcmConnectWalletViewNode?.[0]?.shadowRoot; + const wcmDesktopWalletSelectionNode = wcmConnectWalletViewNodeShadowRoot?.querySelector( + '#wcm-desktop-wallet-selection', + ); + const wcmDesktopWalletSelectionNodeShadowRoot = wcmDesktopWalletSelectionNode?.shadowRoot; + const wcmModalFooterNode = wcmDesktopWalletSelectionNodeShadowRoot?.querySelector('wcm-modal-footer'); + + wcmModalFooterNode?.setAttribute('style', 'display: none;'); + + // if (wcmModalRouterNodeShadowRoot && wcmModalFooterNode) { + // clearInterval(timerRef.current); + // timerRef.current = undefined; + // } + }, 1000); + } + + return () => { + clearInterval(timerRef.current); + timerRef.current = undefined; + }; + }, []); + + const connect = useConnect(); + const tryActivation = useCallback( + async (chainType: ChainType, key: string) => { + if (loading) return; + setLoading({ [key]: true }); + try { + // TODO ton await + connect(chainType); + + // TODO ton check if connect need await + onCancel(); + } catch (error: any) { + console.debug(`connection error: ${error}`); + CommonMessage.error(`connection error: ${handleErrorMessage(error.message)}`); + } + setLoading(undefined); + }, + [connect, loading, onCancel], + ); + + const walletList = useMemo(() => { + const keys = Object.keys(SUPPORTED_WALLETS) as TWalletConnectorId[]; + + return keys.filter((key: TWalletConnectorId) => { + const option = SUPPORTED_WALLETS[key]; + return option.chainType === 'TON'; + }); + }, []); + + return ( +
+ {walletList.map((key) => { + const option = SUPPORTED_WALLETS[key]; + const disabled = !!(isActive && account && option.connectorId && option.connectorId === connectorId); + + return ( + + ); + })} +
+ ); +} diff --git a/src/components/TonWalletList/styles.module.less b/src/components/TonWalletList/styles.module.less new file mode 100644 index 00000000..2d927244 --- /dev/null +++ b/src/components/TonWalletList/styles.module.less @@ -0,0 +1,33 @@ +@import '/src/assets/theme/color.less'; + +.wallet-list { + display: flex; + flex-direction: column; + gap: 8px; + padding: 16px 0; + + :global { + .@{app-prefix}-btn { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + width: 100%; + height: 48px; + padding: 12px 16px; + border: none; + border-radius: 48px; + background-color: @bg-16; + color: @font-15; + font-size: 14px; + font-weight: 400; + line-height: 22px; + &:disabled { + opacity: 0.3; + } + } + } + .wallet-icon { + font-size: 24px; + } +} diff --git a/src/components/WalletIcon/index.tsx b/src/components/WalletIcon/index.tsx new file mode 100644 index 00000000..6cd5b250 --- /dev/null +++ b/src/components/WalletIcon/index.tsx @@ -0,0 +1,38 @@ +import clsx from 'clsx'; +import IconFont from 'components/IconFont'; +import { SUPPORTED_WALLETS } from 'constants/wallets'; +import React, { useCallback, useMemo } from 'react'; +import { EVMConnectorId, TWalletConnectorId } from 'types'; +import styles from './styles.module.less'; +import { TelegramPlatform } from 'utils/telegram/telegram'; + +export default function WalletIcon({ + connectorId, + className, + type, +}: { + connectorId?: TWalletConnectorId; + className?: string; + type?: string; +}) { + const filter = useCallback( + (k: TWalletConnectorId) => { + const isTelegramPlatformAndNotWeb = TelegramPlatform.isTelegramPlatformAndNotWeb(); + if ( + isTelegramPlatformAndNotWeb && + (SUPPORTED_WALLETS[k].connectorId === EVMConnectorId.COINBASE_WALLET || + SUPPORTED_WALLETS[k].connectorId === EVMConnectorId.METAMASK) + ) { + return false; + } + + return SUPPORTED_WALLETS[k].connectorId === connectorId; + }, + [connectorId], + ); + const iconType = useMemo(() => { + const keys: TWalletConnectorId[] = Object.keys(SUPPORTED_WALLETS) as TWalletConnectorId[]; + return keys.filter((k) => filter(k)).map((k) => SUPPORTED_WALLETS[k].iconType)[0]; + }, [filter]); + return ; +} diff --git a/src/components/WalletIcon/styles.module.less b/src/components/WalletIcon/styles.module.less new file mode 100644 index 00000000..a4f9d0d5 --- /dev/null +++ b/src/components/WalletIcon/styles.module.less @@ -0,0 +1,3 @@ +.icon { + font-size: 26px; +} diff --git a/src/components/WalletList/index.tsx b/src/components/WalletList/index.tsx new file mode 100644 index 00000000..0a7f7b7c --- /dev/null +++ b/src/components/WalletList/index.tsx @@ -0,0 +1,172 @@ +import { Button } from 'antd'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useChain } from 'contexts/useChain'; +import { useModal } from 'contexts/useModal'; +import { setSelectERCWallet } from 'contexts/useChain/actions'; +import IconFont from 'components/IconFont'; +import { SUPPORTED_WALLETS } from 'constants/wallets'; +import { DEFAULT_ERC_CHAIN_INFO } from 'constants/index'; +import { getNetworkInfo, switchChain } from 'utils/network'; +import { sleep } from 'utils'; +import { isPortkey, isPortkeyConnector } from 'utils/portkey'; +import { isMobileDevices } from 'utils/isMobile'; +import CommonMessage from 'components/CommonMessage'; +import styles from './styles.module.less'; +import { TelegramPlatform } from 'utils/telegram/telegram'; +import { ChainType, EVMConnectorId, TWalletConnectorId } from 'types'; +import { useTonConnectModal } from '@tonconnect/ui-react'; +import { useEVMConnectWallet, useTon } from 'hooks/web3'; +import { usePrevious } from 'react-use'; +import { handleErrorMessage } from 'utils/error'; +export default function WalletList({ onFinish, chainType }: { onFinish?: () => void; chainType?: ChainType }) { + const [{ walletWallet, walletChainType }] = useModal(); + const { chainId, connector: connectedConnector, connectorId, account } = walletWallet || {}; + const { open } = useTonConnectModal(); + const { isActive } = useTon(); + const prevIsActive = usePrevious(isActive); + const onCancel = useCallback(() => { + setLoading(undefined); + onFinish?.(); + }, [onFinish]); + useEffect(() => { + if (chainType === 'TON' && prevIsActive === false && prevIsActive !== isActive) { + onCancel(); + } + }, [chainType, isActive, onCancel, prevIsActive]); + const [loading, setLoading] = useState(); + const [{ userERCChainId }, { dispatch: chainDispatch }] = useChain(); + const timerRef = useRef(); + useEffect(() => { + if (TelegramPlatform.isTelegramPlatform()) { + timerRef.current = setInterval(() => { + const wcmModalNode = document.getElementsByTagName('wcm-modal'); + const idIsWcmModalElement = wcmModalNode?.[0]?.shadowRoot?.querySelector('#wcm-modal'); + const wcmModalRouterNode = idIsWcmModalElement?.getElementsByTagName('wcm-modal-router'); + const wcmModalRouterNodeShadowRoot = wcmModalRouterNode?.[0]?.shadowRoot?.querySelector('.wcm-content'); + if (TelegramPlatform.isTelegramPlatformWeb() || TelegramPlatform.isTelegramPlatformDesktop()) { + wcmModalRouterNodeShadowRoot?.setAttribute('style', 'height: 70vh; min-height: 400px; overflow-y: auto'); + } + + const wcmConnectWalletViewNode = wcmModalRouterNodeShadowRoot?.getElementsByTagName('wcm-connect-wallet-view'); + const wcmConnectWalletViewNodeShadowRoot = wcmConnectWalletViewNode?.[0]?.shadowRoot; + const wcmDesktopWalletSelectionNode = wcmConnectWalletViewNodeShadowRoot?.querySelector( + '#wcm-desktop-wallet-selection', + ); + const wcmDesktopWalletSelectionNodeShadowRoot = wcmDesktopWalletSelectionNode?.shadowRoot; + const wcmModalFooterNode = wcmDesktopWalletSelectionNodeShadowRoot?.querySelector('wcm-modal-footer'); + + wcmModalFooterNode?.setAttribute('style', 'display: none;'); + + // if (wcmModalRouterNodeShadowRoot && wcmModalFooterNode) { + // clearInterval(timerRef.current); + // timerRef.current = undefined; + // } + }, 1000); + } + + return () => { + clearInterval(timerRef.current); + timerRef.current = undefined; + }; + }, []); + + const evmConnectWallet = useEVMConnectWallet(); + const tryActivation = useCallback( + async (chainType: ChainType, connectorId: TWalletConnectorId, key: string) => { + if (loading || chainType === 'ELF') return; + + setLoading({ [key]: true }); + try { + if (chainType === 'TON') { + // Case: chainType === 'TON' + open(); + } else { + // Case: chainType === 'ERC' + + // wagmi don not need to remove wcm-modal + // if (connectorId === EVMConnectorId.WALLET_CONNECT) { + // document.getElementsByTagName('wcm-modal')?.[0]?.remove(); + // } + await evmConnectWallet(connectorId as EVMConnectorId, typeof chainId === 'number' ? chainId : undefined); + chainDispatch(setSelectERCWallet(connectorId as EVMConnectorId)); + + if (connectorId === EVMConnectorId.COINBASE_WALLET) { + await sleep(500); + await switchChain(DEFAULT_ERC_CHAIN_INFO as any, connectedConnector, true); + } else if (userERCChainId) { + try { + // Whether the switch is successful or not does not affect the link status + const info = getNetworkInfo(userERCChainId); + if (info) await switchChain(info.info, connectedConnector, true); + } catch (error) { + console.debug(error, '====error'); + } + } + onCancel(); + } + } catch (error: any) { + console.debug(`connection error: ${error}`); + CommonMessage.error(`connection error: ${handleErrorMessage(error)}`); + } + setLoading(undefined); + }, + [chainDispatch, chainId, connectedConnector, evmConnectWallet, loading, onCancel, open, userERCChainId], + ); + + const walletList = useMemo(() => { + const keys = Object.keys(SUPPORTED_WALLETS) as TWalletConnectorId[]; + + return keys.filter((key) => { + if (chainType) { + return SUPPORTED_WALLETS[key].chainType === chainType; + } else { + const option = SUPPORTED_WALLETS[key]; + const isNotERCWallet = option.chainType !== 'ERC'; + const isStringChain = typeof chainId === 'string' || walletChainType === 'ELF'; + + // Undisplayed metamask entrance in mobile and telegram(not browser) + if ((TelegramPlatform.isTelegramPlatformAndNotWeb() || isMobileDevices()) && key === EVMConnectorId.METAMASK) + return false; + + // Undisplayed coinbase entrance in telegram(not browser) + if (TelegramPlatform.isTelegramPlatformAndNotWeb()) { + if (option.connectorId === EVMConnectorId.COINBASE_WALLET) return false; + } + + // Undisplayed coinbase entrance in Portkey App + if (isPortkey()) { + if (option.connectorId === EVMConnectorId.COINBASE_WALLET) return false; + if (isStringChain) return isPortkeyConnector(option.connectorId); + + // EVM + if (!isNotERCWallet) return !(option.connectorId === EVMConnectorId.METAMASK); + } + return isNotERCWallet ? isStringChain : !isStringChain; + } + }); + }, [chainId, chainType, walletChainType]); + + return ( +
+ {walletList.map((key) => { + const option = SUPPORTED_WALLETS[key]; + const disabled = !!(account && option.connectorId && option.connectorId === connectorId); + return ( + + ); + })} +
+ ); +} diff --git a/src/components/WalletList/styles.module.less b/src/components/WalletList/styles.module.less new file mode 100644 index 00000000..2d927244 --- /dev/null +++ b/src/components/WalletList/styles.module.less @@ -0,0 +1,33 @@ +@import '/src/assets/theme/color.less'; + +.wallet-list { + display: flex; + flex-direction: column; + gap: 8px; + padding: 16px 0; + + :global { + .@{app-prefix}-btn { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + width: 100%; + height: 48px; + padding: 12px 16px; + border: none; + border-radius: 48px; + background-color: @bg-16; + color: @font-15; + font-size: 14px; + font-weight: 400; + line-height: 22px; + &:disabled { + opacity: 0.3; + } + } + } + .wallet-icon { + font-size: 24px; + } +} diff --git a/src/components/Web3Provider/evm.tsx b/src/components/Web3Provider/evm.tsx new file mode 100644 index 00000000..389f2a1c --- /dev/null +++ b/src/components/Web3Provider/evm.tsx @@ -0,0 +1,14 @@ +'use client'; + +import { WagmiProvider } from 'wagmi'; +import { EVMProviderConfig } from 'constants/evm'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +export const queryClient = new QueryClient(); +export default function EVMProvider({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/src/components/Web3Provider/index.tsx b/src/components/Web3Provider/index.tsx new file mode 100644 index 00000000..4c671cc4 --- /dev/null +++ b/src/components/Web3Provider/index.tsx @@ -0,0 +1,29 @@ +import dynamic from 'next/dynamic'; +import { useInitWallet } from 'hooks/wallet'; +import EVMProvider from './evm'; + +function Web3Manager({ children }: { children: JSX.Element }) { + useInitWallet(); + + return children; +} + +const WebLoginProviderDynamic = dynamic( + async () => { + const WalletProvider = await import('./webLoginV2Provider').then((module) => module); + return WalletProvider; + }, + { + ssr: false, + }, +); + +export default function Web3Provider({ children }: { children: JSX.Element }) { + return ( + + + {children} + + + ); +} diff --git a/src/components/Web3Provider/webLoginV2Config.ts b/src/components/Web3Provider/webLoginV2Config.ts new file mode 100644 index 00000000..cc4414fa --- /dev/null +++ b/src/components/Web3Provider/webLoginV2Config.ts @@ -0,0 +1,108 @@ +import { PortkeyDiscoverWallet } from '@aelf-web-login/wallet-adapter-portkey-discover'; +import { PortkeyInnerWallet } from '@aelf-web-login/wallet-adapter-portkey-web'; +import { NightElfWallet } from '@aelf-web-login/wallet-adapter-night-elf'; +import { IConfigProps } from '@aelf-web-login/wallet-adapter-bridge'; +import { SignInDesignEnum, TChainId } from '@aelf-web-login/wallet-adapter-base'; +import { APP_NAME, EBRIDGE_PORTKEY_PROJECT_CODE } from 'constants/misc'; +import { AELF_NODES, SHOW_V_CONSOLE, SupportedELFChain, TELEGRAM_BOT_ID, WEB_LOGIN_CONFIG } from 'constants/index'; +import { devices } from '@portkey/utils'; +import { TelegramPlatform } from 'utils/telegram/telegram'; +import { FairyVaultDiscoverWallet } from '@aelf-web-login/wallet-adapter-fairy-vault-discover'; + +const defaultChainId = WEB_LOGIN_CONFIG.chainId as TChainId; + +export const didConfig = { + graphQLUrl: WEB_LOGIN_CONFIG.portkeyV2.graphQLUrl, + connectUrl: WEB_LOGIN_CONFIG.portkeyV2.connectServer, + serviceUrl: WEB_LOGIN_CONFIG.portkeyV2.apiServer, + requestDefaults: { + baseURL: WEB_LOGIN_CONFIG.portkeyV2.apiServer, + timeout: 20 * 1000, + }, + socialLogin: { + Telegram: { + botId: TELEGRAM_BOT_ID, + }, + }, + networkType: WEB_LOGIN_CONFIG.portkeyV2.networkType, + referralInfo: { + referralCode: '', + projectCode: EBRIDGE_PORTKEY_PROJECT_CODE, + }, +}; + +const baseConfig: IConfigProps['baseConfig'] = { + showVconsole: SHOW_V_CONSOLE, + networkType: WEB_LOGIN_CONFIG.portkeyV2.networkType, + chainId: defaultChainId, + sideChainId: defaultChainId, + design: SignInDesignEnum.CryptoDesign, + enableAcceleration: true, + appName: APP_NAME, + theme: 'light', +}; + +export function getConfig() { + const isTelegramPlatform = TelegramPlatform.isTelegramPlatform(); + const portkeyInnerWallet = new PortkeyInnerWallet({ + networkType: WEB_LOGIN_CONFIG.portkeyV2.networkType, + chainId: defaultChainId, + disconnectConfirm: true, + }); + const fairyVaultDiscoverWallet = new FairyVaultDiscoverWallet({ + networkType: WEB_LOGIN_CONFIG.portkeyV2.networkType, + chainId: defaultChainId, + autoRequestAccount: true, // If set to true, please contact Portkey to add whitelist + autoLogoutOnDisconnected: true, + autoLogoutOnNetworkMismatch: true, + autoLogoutOnAccountMismatch: true, + autoLogoutOnChainMismatch: true, + }); + setTimeout(() => { + (fairyVaultDiscoverWallet as any).detect(); + }, 100); + const isMobileDevices = devices.isMobileDevices(); + const config: IConfigProps = { + baseConfig, + wallets: isTelegramPlatform + ? [portkeyInnerWallet] + : isMobileDevices + ? [ + portkeyInnerWallet, + fairyVaultDiscoverWallet, + new PortkeyDiscoverWallet({ + networkType: WEB_LOGIN_CONFIG.portkeyV2.networkType, + chainId: defaultChainId, + autoRequestAccount: true, + autoLogoutOnDisconnected: true, + autoLogoutOnNetworkMismatch: true, + autoLogoutOnAccountMismatch: true, + autoLogoutOnChainMismatch: true, + onPluginNotFound: (openStore) => { + openStore(); + }, + }), + ] + : [ + portkeyInnerWallet, + fairyVaultDiscoverWallet, + new PortkeyDiscoverWallet({ + networkType: WEB_LOGIN_CONFIG.portkeyV2.networkType, + chainId: defaultChainId, + autoRequestAccount: true, + autoLogoutOnDisconnected: true, + autoLogoutOnNetworkMismatch: true, + autoLogoutOnAccountMismatch: true, + autoLogoutOnChainMismatch: true, + }), + new NightElfWallet({ + chainId: defaultChainId, + appName: APP_NAME, + connectEagerly: true, + defaultRpcUrl: SupportedELFChain[defaultChainId].CHAIN_INFO.rpcUrl, + nodes: AELF_NODES, + }), + ], + }; + return config; +} diff --git a/src/components/Web3Provider/webLoginV2Provider.tsx b/src/components/Web3Provider/webLoginV2Provider.tsx new file mode 100644 index 00000000..10048de6 --- /dev/null +++ b/src/components/Web3Provider/webLoginV2Provider.tsx @@ -0,0 +1,38 @@ +'use client'; +// import '../../utils/telegram/telegram-web-app'; +import { useMemo } from 'react'; +import { WebLoginProvider } from '@aelf-web-login/wallet-adapter-react'; +import { getConfig, didConfig } from './webLoginV2Config'; +import { useEffect, useState } from 'react'; +import { did } from '@portkey/did'; +import { checkConnectedWallet } from 'utils/portkey'; + +export default function WebLoginV2Providers({ children }: { children: React.ReactNode }) { + useMemo(() => { + did.setConfig(didConfig); + checkConnectedWallet(); + }, []); + + const [isLoaded, setIsLoaded] = useState(false); + useEffect(() => { + const script = document.createElement('script'); + script.src = 'https://telegram.org/js/telegram-web-app.js'; + document.body.appendChild(script); + + script?.addEventListener('load', (res) => { + console.log('>>>>>> Telegram web app script - load', res); + setIsLoaded(true); + return; + }); + + script?.addEventListener('error', (error) => { + console.warn('>>>>>> Telegram web app script - error', error); + setIsLoaded(false); + return; + }); + }, []); + + const config = useMemo(() => getConfig(), []); + + return isLoaded ? {children} : null; +} diff --git a/src/constants/ChainConstants.ts b/src/constants/ChainConstants.ts new file mode 100644 index 00000000..e1c0bc03 --- /dev/null +++ b/src/constants/ChainConstants.ts @@ -0,0 +1,65 @@ +import { AelfInstancesKey, ChainType, Web3Type } from 'types'; +import { + ACTIVE_CHAIN, + ChainConstantsType, + ERC_CHAIN_TYPE, + DEFAULT_ERC_CHAIN, + SupportedERCChain, + SupportedELFChain, +} from '.'; + +type AElfOwnConstants = { + CONTRACTS?: { [key: string]: string }; + TOKEN_CONTRACT?: string; + CROSS_CHAIN_CONTRACT?: string; + BRIDGE_CONTRACT?: string; + BRIDGE_CONTRACT_OUT?: string; + LIMIT_CONTRACT?: string; + CREATE_TOKEN_CONTRACT?: string; +}; + +type Constants = ChainConstantsType & AElfOwnConstants; + +export class ChainConstants { + public id: number | string; + static chainId: number | string; + static chainType: ChainType; + constructor(id: number | string) { + this.id = id; + } +} + +export class ERCChainConstants extends ChainConstants { + static constants: Constants = SupportedERCChain[DEFAULT_ERC_CHAIN]; + static chainType: ChainType = 'ERC'; + constructor(id: number | string) { + super(id); + // eslint-disable-next-line react/no-is-mounted + this.setStaticAttrs(); + } + setStaticAttrs() { + const chainId = (this.id || window.ethereum?.chainId) as ERC_CHAIN_TYPE; + let attrs; + if (ACTIVE_CHAIN[chainId]) { + attrs = SupportedERCChain[chainId]; + } else { + attrs = SupportedERCChain[DEFAULT_ERC_CHAIN]; + } + ERCChainConstants['constants'] = attrs; + ERCChainConstants['chainId'] = attrs?.CHAIN_INFO.chainId; + } +} +export class ELFChainConstants extends ChainConstants { + static chainType: ChainType = 'ELF'; + static aelfInstances?: Web3Type['aelfInstances']; + static constants: { [k in AelfInstancesKey]: Constants }; + constructor(id: number | string, aelfInstances?: Web3Type['aelfInstances']) { + super(id); + ELFChainConstants['aelfInstances'] = aelfInstances; + // eslint-disable-next-line react/no-is-mounted + this.setStaticAttrs(); + } + setStaticAttrs() { + ELFChainConstants['constants'] = SupportedELFChain as any; + } +} diff --git a/src/constants/abis/bridgeIn.json b/src/constants/abis/bridgeIn.json new file mode 100644 index 00000000..53d2fd7c --- /dev/null +++ b/src/constants/abis/bridgeIn.json @@ -0,0 +1,531 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "receiptId", + "type": "string" + }, + { + "indexed": false, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "NewReceipt", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "TokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "TokenRemoved", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "tokenKey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "receiptIndex", + "type": "uint256" + } + ], + "name": "_generateReceiptId", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "_generateTokenKey", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "addToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "targetAddress", + "type": "bytes32" + } + ], + "name": "createReceipt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + }, + { + "internalType": "string", + "name": "targetAddress", + "type": "string" + } + ], + "name": "lockToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "targetAddress", + "type": "bytes32" + } + ], + "name": "createNativeTokenReceipt", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + } + ], + "name": "getMyReceipts", + "outputs": [ + { + "internalType": "string[]", + "name": "", + "type": "string[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "string[]", + "name": "targetChainIds", + "type": "string[]" + } + ], + "name": "getSendReceiptIndex", + "outputs": [ + { + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + }, + { + "internalType": "uint256", + "name": "fromIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endIndex", + "type": "uint256" + } + ], + "name": "getSendReceiptInfos", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockHeight", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockTime", + "type": "uint256" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + }, + { + "internalType": "string", + "name": "targetAddress", + "type": "string" + }, + { + "internalType": "string", + "name": "receiptId", + "type": "string" + } + ], + "internalType": "struct BridgeIn.Receipt[]", + "name": "_receipts", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "isSupported", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "ownerToReceipts", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "receipts", + "outputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockHeight", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockTime", + "type": "uint256" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + }, + { + "internalType": "string", + "name": "targetAddress", + "type": "string" + }, + { + "internalType": "string", + "name": "receiptId", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "removeToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "totalAmountInReceipts", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/constants/abis/bridgeOut.json b/src/constants/abis/bridgeOut.json new file mode 100644 index 00000000..08698ab4 --- /dev/null +++ b/src/constants/abis/bridgeOut.json @@ -0,0 +1,600 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_merkleTree", + "type": "address" + }, + { + "internalType": "address", + "name": "_regiment", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "swapId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "transmiter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "receiptIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "receiptHash", + "type": "bytes32" + } + ], + "name": "NewTransmission", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "swapId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "SwapPairAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "receiveAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TokenSwapEvent", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "chainId", + "type": "string" + } + ], + "name": "_generateTokenKey", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_receiptId", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_receiverAddress", + "type": "address" + } + ], + "name": "computeLeafHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "_leafHash", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "originShare", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "targetShare", + "type": "uint64" + } + ], + "internalType": "struct BridgeOut.SwapRatio", + "name": "swapRatio", + "type": "tuple" + } + ], + "internalType": "struct BridgeOut.SwapTargetToken[]", + "name": "targetTokens", + "type": "tuple[]" + }, + { + "internalType": "string", + "name": "fromChainId", + "type": "string" + }, + { + "internalType": "bytes32", + "name": "regimentId", + "type": "bytes32" + } + ], + "name": "createSwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "swapHashId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokens", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amounts", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "string[]", + "name": "targetChainIds", + "type": "string[]" + } + ], + "name": "getReceiveReceiptIndex", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "fromChainId", + "type": "string" + }, + { + "internalType": "uint256", + "name": "fromIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endIndex", + "type": "uint256" + } + ], + "name": "getReceivedReceiptInfos", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address", + "name": "targetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockHeight", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockTime", + "type": "uint256" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + }, + { + "internalType": "string", + "name": "receiptId", + "type": "string" + } + ], + "internalType": "struct BridgeOut.ReceivedReceipt[]", + "name": "_receipts", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + } + ], + "name": "getSwapId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "swapId", + "type": "bytes32" + } + ], + "name": "getSwapInfo", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "swapId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "fromChainId", + "type": "string" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint64", + "name": "originShare", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "targetShare", + "type": "uint64" + } + ], + "internalType": "struct BridgeOut.SwapRatio", + "name": "swapRatio", + "type": "tuple" + } + ], + "internalType": "struct BridgeOut.SwapTargetToken[]", + "name": "swapTargetTokenList", + "type": "tuple[]" + }, + { + "internalType": "bytes32", + "name": "regimentId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "spaceId", + "type": "bytes32" + } + ], + "internalType": "struct BridgeOut.SwapInfo", + "name": "swapInfo", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "receiptHash", + "type": "bytes32" + } + ], + "name": "isReceiptRecorded", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "spaceId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "receiptId", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiverAddress", + "type": "address" + } + ], + "name": "merkleTreeVerify", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "swapId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "receiptId", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiverAddress", + "type": "address" + } + ], + "name": "swapToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "swapHashId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_report", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "_rs", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "_ss", + "type": "bytes32[]" + }, + { + "internalType": "bytes32", + "name": "_rawVs", + "type": "bytes32" + } + ], + "name": "transmit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "swapHashId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/constants/abis/createToken.json b/src/constants/abis/createToken.json new file mode 100644 index 00000000..6b733716 --- /dev/null +++ b/src/constants/abis/createToken.json @@ -0,0 +1,112 @@ +[ + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "string", "name": "step", "type": "string" }, + { "indexed": false, "internalType": "string", "name": "message", "type": "string" } + ], + "name": "Log", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "string", "name": "step", "type": "string" }, + { "indexed": false, "internalType": "address", "name": "value", "type": "address" } + ], + "name": "LogAddress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "string", "name": "step", "type": "string" }, + { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "LogUint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "tokenAddress", "type": "address" }, + { "indexed": false, "internalType": "string", "name": "name", "type": "string" }, + { "indexed": false, "internalType": "string", "name": "symbol", "type": "string" }, + { "indexed": false, "internalType": "uint256", "name": "initialSupply", "type": "uint256" }, + { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "mintToAddress", "type": "address" } + ], + "name": "TokenCreated", + "type": "event" + }, + { + "inputs": [{ "internalType": "string", "name": "", "type": "string" }], + "name": "allTokens", + "outputs": [ + { "internalType": "address", "name": "tokenAddress", "type": "address" }, + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "symbol", "type": "string" }, + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "mintToAddress", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "symbol", "type": "string" }, + { "internalType": "uint256", "name": "initialSupply", "type": "uint256" }, + { "internalType": "address", "name": "officialAddress", "type": "address" }, + { "internalType": "address", "name": "mintToAddress", "type": "address" } + ], + "name": "createOfficialToken", + "outputs": [{ "internalType": "address", "name": "tokenAddress", "type": "address" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "symbol", "type": "string" }, + { "internalType": "uint256", "name": "initialSupply", "type": "uint256" } + ], + "name": "createToken", + "outputs": [{ "internalType": "address", "name": "tokenAddress", "type": "address" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "symbol", "type": "string" } + ], + "name": "doesTokenExist", + "outputs": [{ "internalType": "bool", "name": "exists", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "symbol", "type": "string" } + ], + "name": "getTokenInfo", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "tokenAddress", "type": "address" }, + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "symbol", "type": "string" }, + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "mintToAddress", "type": "address" } + ], + "internalType": "struct TokenFactory.TokenInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/constants/abis/erc20.json b/src/constants/abis/erc20.json new file mode 100644 index 00000000..be862177 --- /dev/null +++ b/src/constants/abis/erc20.json @@ -0,0 +1,268 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_spender", "type": "address" }, + { "name": "_value", "type": "uint256" } + ], + "name": "approve", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "wad", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": true, + "name": "dst", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "dst", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + } +] diff --git a/src/constants/abis/index.ts b/src/constants/abis/index.ts new file mode 100644 index 00000000..ad002dd3 --- /dev/null +++ b/src/constants/abis/index.ts @@ -0,0 +1,8 @@ +import ERC20_ABI from './erc20.json'; +import BRIDGE_IN_ABI from './bridgeIn.json'; +import BRIDGE_OUT_ABI from './bridgeOut.json'; +import LIMIT_ABI from './limit.json'; +import CREATE_TOKEN_ABI from './createToken.json'; +import POOLS_ABI from './pools.json'; + +export { ERC20_ABI, BRIDGE_IN_ABI, BRIDGE_OUT_ABI, LIMIT_ABI, CREATE_TOKEN_ABI, POOLS_ABI }; diff --git a/src/constants/abis/limit.json b/src/constants/abis/limit.json new file mode 100644 index 00000000..6a0eaaa8 --- /dev/null +++ b/src/constants/abis/limit.json @@ -0,0 +1,522 @@ +[ + { + "inputs": [], + "name": "BucketOverfilled", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "dailyLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DailyLimitExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "capacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MaxCapacityExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "minWaitInSeconds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "available", + "type": "uint256" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "TokenRateLimitReached", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + } + ], + "name": "getCurrentReceiptTokenBucketState", + "outputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "currentTokenAmount", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "lastUpdatedTime", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "tokenCapacity", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rate", + "type": "uint128" + } + ], + "internalType": "struct RateLimiter.TokenBucket", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "fromChainId", + "type": "string" + } + ], + "name": "getCurrentSwapTokenBucketState", + "outputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "currentTokenAmount", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "lastUpdatedTime", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "tokenCapacity", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rate", + "type": "uint128" + } + ], + "internalType": "struct RateLimiter.TokenBucket", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + } + ], + "name": "GetReceiptBucketMinWaitSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "fromChainId", + "type": "string" + } + ], + "name": "GetSwapBucketMinWaitSeconds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "bucketId", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "isEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "tokenCapacity", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "rate", + "type": "uint128" + } + ], + "internalType": "struct RateLimiter.TokenBucketConfig[]", + "name": "configs", + "type": "tuple[]" + } + ], + "name": "SetTokenBucketConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridgeIn", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridgeOut", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "dailyLimitId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "consumeDailyLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "bucketId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "consumeTokenBucket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "targetChainId", + "type": "string" + } + ], + "name": "getReceiptDailyLimit", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "refreshTime", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "defaultTokenAmount", + "type": "uint256" + } + ], + "internalType": "struct DailyLimiter.DailyLimitTokenInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "swapId", + "type": "bytes32" + } + ], + "name": "getSwapDailyLimit", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "refreshTime", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "defaultTokenAmount", + "type": "uint256" + } + ], + "internalType": "struct DailyLimiter.DailyLimitTokenInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_bridgeIn", + "type": "address" + }, + { + "internalType": "address", + "name": "_bridgeOut", + "type": "address" + }, + { + "internalType": "address", + "name": "_admin", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "dailyLimitId", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "refreshTime", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "defaultTokenAmount", + "type": "uint256" + } + ], + "internalType": "struct DailyLimiter.DailyLimitConfig[]", + "name": "dailyLimitConfigs", + "type": "tuple[]" + } + ], + "name": "setDailyLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/src/constants/abis/pools.json b/src/constants/abis/pools.json new file mode 100644 index 00000000..ccc2890d --- /dev/null +++ b/src/constants/abis/pools.json @@ -0,0 +1,183 @@ +[ + { "inputs": [], "name": "InsufficientLiquidity", "type": "error" }, + { "inputs": [], "name": "PermissionsError", "type": "error" }, + { "inputs": [], "name": "WithdrawalTooHigh", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "provider", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": true, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "LiquidityAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "provider", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": true, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "LiquidityRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": false, "internalType": "string", "name": "chainId", "type": "string" }, + { "indexed": true, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "Locked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": false, "internalType": "string", "name": "chainId", "type": "string" }, + { "indexed": true, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "Released", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "addLiquidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridgeIn", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridgeOut", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_admin", "type": "address" }], + "name": "changeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "token", "type": "address" } + ], + "name": "getUserLiquidity", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_bridgeIn", "type": "address" }, + { "internalType": "address", "name": "_bridgeOut", "type": "address" }, + { "internalType": "address", "name": "_admin", "type": "address" }, + { "internalType": "address", "name": "_nativeToken", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "string", "name": "targetChainId", "type": "string" }, + { "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "lock", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "provider", "type": "address" }, + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "migrator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "nativeToken", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "string", "name": "targetChainId", "type": "string" }, + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "name": "release", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "removeLiquidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/constants/aelf.ts b/src/constants/aelf.ts new file mode 100644 index 00000000..893fcaf2 --- /dev/null +++ b/src/constants/aelf.ts @@ -0,0 +1,3 @@ +import { AELF_NODES } from './index'; + +export const AElfNodes = AELF_NODES; diff --git a/src/constants/chain.ts b/src/constants/chain.ts new file mode 100644 index 00000000..b302bc9e --- /dev/null +++ b/src/constants/chain.ts @@ -0,0 +1,157 @@ +import { IconInfo } from 'types/misc'; + +const IS_MAINNET = process.env.NEXT_PUBLIC_APP_ENV === 'mainnet'; + +export enum SupportedChainId { + MAINNET = 1, + GORELI = 5, + KOVAN = 42, + BSC_MAINNET = 56, + BSC_TESTNET = 97, + HECO_MAINNET = 128, + HECO_TESTNET = 256, + OEC_MAINNET = 66, + OEC_TESTNET = 65, + POLYGON_MAINNET = 137, + POLYGON_TESTNET = 80001, + SEPOLIA = 11155111, + BASE_SEPOLIA = 84532, + BASE = 8453, +} + +export enum SupportedTONChainId { + TESTNET = IS_MAINNET ? 1101 : 1100, + MAINNET = IS_MAINNET ? 1100 : 1101, +} + +export enum SupportedELFChainId { + AELF = 'AELF', + tDVV = 'tDVV', + tDVW = 'tDVW', +} + +export type TBridgeChainId = SupportedChainId | SupportedELFChainId | SupportedTONChainId; + +export const CHAIN_ID_MAP = { + [SupportedChainId.MAINNET]: 'Ethereum', + [SupportedChainId.BSC_MAINNET]: 'BSC', + [SupportedELFChainId.AELF]: 'MainChain_AELF', + [SupportedELFChainId.tDVV]: 'SideChain_tDVV', + [SupportedELFChainId.tDVW]: 'SideChain_tDVW', + [SupportedChainId.KOVAN]: 'Kovan', + [SupportedChainId.GORELI]: 'Goerli', + [SupportedChainId.BSC_TESTNET]: 'BSCTest', + [SupportedChainId.SEPOLIA]: 'Sepolia', + [SupportedChainId.BASE_SEPOLIA]: 'BaseSepolia', + [SupportedChainId.BASE]: 'Base', + [SupportedTONChainId.TESTNET]: 'TonTest', + [SupportedTONChainId.MAINNET]: 'TON', +}; + +/** + * Array of all the supported chain IDs + */ +export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = Object.values(SupportedChainId).filter( + (id) => typeof id === 'number', +) as SupportedChainId[]; + +export const DEFAULT_CHAIN_ICON: { [chainId in TBridgeChainId]: IconInfo } = { + [SupportedChainId.MAINNET]: { + type: 'ETHEREUM-CHAIN', + }, + [SupportedChainId.KOVAN]: { + type: 'ETHEREUM-CHAIN', + }, + [SupportedChainId.GORELI]: { + type: 'ETHEREUM-CHAIN', + }, + [SupportedChainId.BSC_MAINNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedChainId.BSC_TESTNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedChainId.HECO_MAINNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedChainId.HECO_TESTNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedChainId.OEC_MAINNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedChainId.OEC_TESTNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedChainId.POLYGON_MAINNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedChainId.POLYGON_TESTNET]: { + type: 'BINANCE-CHAIN', + }, + [SupportedELFChainId.AELF]: { + type: 'AELF-CHAIN', + }, + [SupportedELFChainId.tDVV]: { + type: 'tDVV-CHAIN', + }, + [SupportedELFChainId.tDVW]: { + type: 'tDVV-CHAIN', + }, + [SupportedChainId.SEPOLIA]: { + // type: 'SEPOLIA', + type: 'ETHEREUM-CHAIN', + }, + [SupportedChainId.BASE_SEPOLIA]: { + // type: 'BASE_SEPOLIA', + type: 'BASE-CHAIN', + }, + [SupportedChainId.BASE]: { + // type: 'BASE_SEPOLIA', + type: 'BASE-CHAIN', + }, + [SupportedTONChainId.TESTNET]: { + type: 'TON-CHAIN', + }, + [SupportedTONChainId.MAINNET]: { + type: 'TON-CHAIN', + }, +}; + +export const DEFAULT_CHAIN_NAME: { [chainId in TBridgeChainId]: string } = { + [SupportedChainId.MAINNET]: 'Ethereum', + [SupportedChainId.KOVAN]: 'Kovan', + [SupportedChainId.GORELI]: 'Goerli', + [SupportedChainId.BSC_MAINNET]: 'Binance Smart Chain', + [SupportedChainId.BSC_TESTNET]: 'Binance Smart Chain Testnet', + [SupportedChainId.HECO_MAINNET]: 'HECO', + [SupportedChainId.HECO_TESTNET]: 'HECO Testnet', + [SupportedChainId.OEC_MAINNET]: 'OEC', + [SupportedChainId.OEC_TESTNET]: 'OEC Testnet', + [SupportedChainId.POLYGON_MAINNET]: 'Polygon', + [SupportedChainId.POLYGON_TESTNET]: 'Polygon Testnet', + [SupportedELFChainId.AELF]: 'aelf MainChain', + [SupportedELFChainId.tDVV]: 'aelf dAppChain', + [SupportedELFChainId.tDVW]: 'aelf dAppChain', + [SupportedChainId.SEPOLIA]: 'Sepolia Testnet', + [SupportedChainId.BASE_SEPOLIA]: 'Base Sepolia', + [SupportedChainId.BASE]: 'Base', + [SupportedTONChainId.MAINNET]: 'TON', + [SupportedTONChainId.TESTNET]: 'TON Testnet', +}; + +export const CHAIN_SHORT_NAME = { + [SupportedChainId.MAINNET]: 'Ethereum', + [SupportedChainId.BSC_MAINNET]: 'Binance', + [SupportedELFChainId.AELF]: 'AELF', + [SupportedELFChainId.tDVV]: 'tDVV', + [SupportedELFChainId.tDVW]: 'tDVW', + [SupportedChainId.KOVAN]: 'Kovan', + [SupportedChainId.GORELI]: 'Goerli', + [SupportedChainId.BSC_TESTNET]: 'Binance', + [SupportedChainId.SEPOLIA]: 'Sepolia', + [SupportedChainId.BASE_SEPOLIA]: 'BaseSepolia', + [SupportedChainId.BASE]: 'Base', + [SupportedTONChainId.MAINNET]: 'TON', + [SupportedTONChainId.TESTNET]: 'TON Testnet', +}; diff --git a/src/constants/evm.ts b/src/constants/evm.ts new file mode 100644 index 00000000..06f99011 --- /dev/null +++ b/src/constants/evm.ts @@ -0,0 +1,56 @@ +import { http, createConfig } from 'wagmi'; +import { + arbitrum, + avalanche, + base, + baseSepolia, + bsc, + bscTestnet, + mainnet, + optimism, + polygon, + sepolia, +} from 'wagmi/chains'; +import { metaMask, coinbaseWallet, walletConnect } from 'wagmi/connectors'; +import { WEBSITE_ICON } from 'constants/index'; +import * as MAINNET from 'constants/platform/main'; +import * as BSC_TESTNET from 'constants/platform/BSC_Test'; +import * as SEPOLIA from 'constants/platform/sepolia'; +import * as BSC from 'constants/platform/BSC'; + +const WalletConnectProjectId = process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID || ''; + +export const EVMProviderConfig = createConfig({ + chains: [mainnet, bsc, base, polygon, avalanche, arbitrum, optimism, sepolia, bscTestnet, baseSepolia], + connectors: [ + metaMask(), + coinbaseWallet({ + appName: process.env.NEXT_PUBLIC_PREFIX || 'eBridge', + appLogoUrl: WEBSITE_ICON, + reloadOnDisconnect: false, + }), + walletConnect({ + projectId: WalletConnectProjectId, + showQrModal: true, + qrModalOptions: { + themeVariables: { + '--wcm-z-index': '9999', + }, + }, + }), + ], + transports: { + [mainnet.id]: http(MAINNET.CHAIN_INFO.rpcUrl), + [bsc.id]: http(BSC.CHAIN_INFO.rpcUrl), + [base.id]: http(), + [polygon.id]: http(), + [avalanche.id]: http(), + [arbitrum.id]: http(), + [optimism.id]: http(), + [sepolia.id]: http(SEPOLIA.CHAIN_INFO.rpcUrl), + [bscTestnet.id]: http(BSC_TESTNET.CHAIN_INFO.rpcUrl), + [baseSepolia.id]: http(), + }, +}); + +export const DEFAULT_EVM_0_ADDRESS = '0x0000000000000000000000000000000000000000'; diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 00000000..60a0e5be --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,3 @@ +export const IS_MAINNET = true; + +export * from './mainnet'; diff --git a/src/constants/link.ts b/src/constants/link.ts new file mode 100644 index 00000000..be5ed4f1 --- /dev/null +++ b/src/constants/link.ts @@ -0,0 +1,125 @@ +import { + telegramIcon, + twitterIcon, + telegramDarkIcon, + twitterDarkIcon, + bridgeIcon, + bridgeBlueIcon, + transactionsIcon, + transactionsBlueIcon, + poolsIcon, + poolsBlueIcon, +} from 'assets/images'; +import { ListingStep } from './listingApplication'; +import { getListingUrl } from 'utils/listingApplication'; + +export const ROUTE_PATHS = { + HOME: '/', + BRIDGE: '/bridge', + TRANSACTIONS: '/transactions', + LISTING_APPLICATION: '/listing-application', + MY_APPLICATIONS: '/my-applications', + POOLS: '/pools', +}; + +export const HIDE_MAIN_PAGE_LIST = [ + { + icon: poolsIcon, + selectedIcon: poolsBlueIcon, + title: 'Pools', + href: ROUTE_PATHS.POOLS, + }, +]; + +export const HIDE_BACKGROUND_IMAGE_PATH_LIST = [ROUTE_PATHS.LISTING_APPLICATION, ROUTE_PATHS.MY_APPLICATIONS]; + +export const NAV_LIST = [ + { + icon: bridgeIcon, + selectedIcon: bridgeBlueIcon, + title: 'Bridge', + href: ROUTE_PATHS.BRIDGE, + }, + { + icon: poolsIcon, + selectedIcon: poolsBlueIcon, + title: 'Pools', + href: ROUTE_PATHS.POOLS, + }, + { + icon: transactionsIcon, + selectedIcon: transactionsBlueIcon, + title: 'Transactions', + href: ROUTE_PATHS.TRANSACTIONS, + }, +]; + +export const LISTING_MENU_CONFIG = [ + { + label: 'Listing Application', + link: getListingUrl?.(ListingStep.TOKEN_INFORMATION), + }, + { + label: 'My Applications', + link: ROUTE_PATHS.MY_APPLICATIONS, + }, +]; + +export const TERMS_OF_SERVICE_URL = 'https://ebridge-exchange.gitbook.io/docs/more-information/terms-of-service'; +export const PRIVACY_POLICY_URL = 'https://ebridge-exchange.gitbook.io/docs/more-information/privacy-policy'; + +export const LEGAL_MENU_CONFIG = [ + { + label: 'Terms of Service', + link: TERMS_OF_SERVICE_URL, + }, + { + label: 'Privacy Policy', + link: PRIVACY_POLICY_URL, + }, +]; + +export const FOOTER_MENU_LIST_CONFIG = [ + { + title: 'Listing', + menu: LISTING_MENU_CONFIG, + }, + { + title: 'Legal', + menu: LEGAL_MENU_CONFIG, + }, +]; + +const COMMUNITY_LINK_MAP = { + TELEGRAM: 'https://t.me/eBridge_official', + TWITTER: 'https://x.com/eBridge_Web3', + DISCORD: 'https://discord.com/invite/MCSP56WV', +}; + +export const FOOTER_COMMUNITY_CONFIG = [ + { + label: 'Telegram', + icon: telegramIcon, + link: COMMUNITY_LINK_MAP.TELEGRAM, + }, + { + label: 'Twitter', + icon: twitterIcon, + link: COMMUNITY_LINK_MAP.TWITTER, + }, +]; + +export const HEADER_COMMUNITY_CONFIG = [ + { + label: 'Telegram', + icon: telegramDarkIcon, + link: COMMUNITY_LINK_MAP.TELEGRAM, + }, + { + label: 'X (Twitter)', + icon: twitterDarkIcon, + link: COMMUNITY_LINK_MAP.TWITTER, + }, +]; + +export const CONTACT_US_FORM_URL = 'https://form.ebridge.exchange/contact'; diff --git a/src/constants/listingApplication.tsx b/src/constants/listingApplication.tsx new file mode 100644 index 00000000..f0df319c --- /dev/null +++ b/src/constants/listingApplication.tsx @@ -0,0 +1,187 @@ +import { ICommonStepsProps } from 'components/CommonSteps'; +import { ApplicationChainStatusEnum } from 'types/api'; +import { + TokenInformationFormKeys, + SelectChainFormKeys, + TTokenInformationFormValidateData, + FormValidateStatus, + TTokenInformationFormValues, + TSelectChainFormValues, + TSelectChainFormValidateData, + TChains, +} from 'types/listingApplication'; + +export const BRIDGE_NOW = 'Bridge Now'; + +export const VIEW_PROGRESS = 'View Progress'; + +export const MY_APPLICATIONS = 'My Applications'; + +export const NO_APPLICATION = 'No application'; + +export const WALLET_CONNECTION_REQUIRED = 'Wallet connection required'; + +export enum ListingStep { + TOKEN_INFORMATION = 0, + SELECT_CHAIN = 1, + INITIALIZE_TOKEN_POOL = 2, +} + +export const LISTING_STEP_PATHNAME_MAP: Record = { + [ListingStep.TOKEN_INFORMATION]: '/token-information', + [ListingStep.SELECT_CHAIN]: '/select-chain', + [ListingStep.INITIALIZE_TOKEN_POOL]: '/initialize-token-pool', +}; + +export const LISTING_STEP_ITEMS: ICommonStepsProps['stepItems'] = [ + { + title: 'Token Information', + }, + { + title: 'Select Chain', + }, + { + title: 'Initialize Token Pool', + }, +]; + +export enum ListingProcessStep { + BASIC_INFO = 0, + SELECT_CHAIN = 1, + INITIALIZE_TOKEN_POOL = 2, + COMPLETE = 3, +} + +export const VIEW_PROGRESS_STEPS: ICommonStepsProps['stepItems'] = [ + { + title: 'Basic info', + }, + { + title: 'Select chain', + }, + { + title: 'Initialize Token Pool', + description: 'It is expected to be completed in 1 business day.', + }, + { + title: 'Complete', + }, +]; + +// ================ Token information ================ + +export const TOKEN_INFORMATION_FORM_LABEL_MAP: Record = { + [TokenInformationFormKeys.TOKEN]: 'Select token', + [TokenInformationFormKeys.OFFICIAL_WEBSITE]: 'Official website (Optional)', + [TokenInformationFormKeys.OFFICIAL_TWITTER]: 'Official Twitter (Optional)', + [TokenInformationFormKeys.TITLE]: 'What is your title on the project/team/company? (Optional)', + [TokenInformationFormKeys.PERSON_NAME]: 'Contact person name (Optional)', + [TokenInformationFormKeys.TELEGRAM_HANDLER]: 'Your Telegram handle (Optional)', + [TokenInformationFormKeys.EMAIL]: 'Contact E-mail', +}; + +export const TOKEN_INFORMATION_FORM_PLACEHOLDER_MAP: Record = { + [TokenInformationFormKeys.TOKEN]: 'Select a token', + [TokenInformationFormKeys.OFFICIAL_WEBSITE]: 'https://', + [TokenInformationFormKeys.OFFICIAL_TWITTER]: 'Twitter account', + [TokenInformationFormKeys.TITLE]: 'Your title', + [TokenInformationFormKeys.PERSON_NAME]: 'Your name', + [TokenInformationFormKeys.TELEGRAM_HANDLER]: 'Example@yourhandle', + [TokenInformationFormKeys.EMAIL]: 'It must be the official email address', +}; + +export const TOKEN_INFORMATION_FORM_INITIAL_VALUES: Partial = { + [TokenInformationFormKeys.TOKEN]: undefined, + [TokenInformationFormKeys.OFFICIAL_WEBSITE]: '', + [TokenInformationFormKeys.OFFICIAL_TWITTER]: '', + [TokenInformationFormKeys.TITLE]: '', + [TokenInformationFormKeys.PERSON_NAME]: '', + [TokenInformationFormKeys.TELEGRAM_HANDLER]: '', + [TokenInformationFormKeys.EMAIL]: '', +}; + +export const TOKEN_INFORMATION_FORM_INITIAL_VALIDATE_DATA: TTokenInformationFormValidateData = { + [TokenInformationFormKeys.TOKEN]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + [TokenInformationFormKeys.OFFICIAL_WEBSITE]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + [TokenInformationFormKeys.OFFICIAL_TWITTER]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + [TokenInformationFormKeys.TITLE]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + [TokenInformationFormKeys.PERSON_NAME]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + [TokenInformationFormKeys.TELEGRAM_HANDLER]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + [TokenInformationFormKeys.EMAIL]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, +}; + +export const REQUIRED_ERROR_MESSAGE = 'This field is required'; + +export const LISTING_TOKEN_TIP = 'Only tokens owned by your connected aelf wallet are displayed here.'; + +// ================ Select chain ================ + +export const SELECT_CHAIN_FORM_LABEL_MAP: Record = { + [SelectChainFormKeys.CHAINS]: 'Select chain(s)', + [SelectChainFormKeys.INITIAL_SUPPLY]: 'Create & Issue Token', +}; + +export const SELECT_CHAIN_FORM_PLACEHOLDER_MAP: Partial> = { + [SelectChainFormKeys.INITIAL_SUPPLY]: 'Initial supply', +}; + +export const SELECT_CHAIN_FORM_INITIAL_VALUES: TSelectChainFormValues = { + [SelectChainFormKeys.CHAINS]: [], + [SelectChainFormKeys.INITIAL_SUPPLY]: '', +}; + +export const SELECT_CHAIN_FORM_INITIAL_VALIDATE_DATA: TSelectChainFormValidateData = { + [SelectChainFormKeys.INITIAL_SUPPLY]: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, +}; + +export const DEFAULT_CHAINS: TChains = { + [SelectChainFormKeys.CHAINS]: [], +}; + +export const SELECT_CHAIN_FORM_CHAIN_NOT_CREATED_STATUS_LIST = [ + ApplicationChainStatusEnum.Unissued, + ApplicationChainStatusEnum.Issuing, + ApplicationChainStatusEnum.Issued, +]; + +export const SELECT_CHAIN_FORM_CHAIN_CREATED_NOT_LISTED_STATUS_LIST = [ + ApplicationChainStatusEnum.PoolInitializing, + ApplicationChainStatusEnum.PoolInitialized, + ApplicationChainStatusEnum.Failed, +]; + +export const SELECT_CHAIN_FORM_CHAIN_LISTED_STATUS_LIST = [ApplicationChainStatusEnum.Complete]; + +export const SELECT_CHAIN_FORM_CHAIN_DISABLED_STATUS_LIST = [ + ...SELECT_CHAIN_FORM_CHAIN_CREATED_NOT_LISTED_STATUS_LIST, + ...SELECT_CHAIN_FORM_CHAIN_LISTED_STATUS_LIST, +]; + +export const SELECT_CHAIN_FORM_CHAIN_TOOLTIP_MAP = { + LISTED: 'The token is already listed on eBridge.', + CREATED_NOT_LISTED: 'The token is in the process of being integrated.', +}; diff --git a/src/constants/mainnet/index.ts b/src/constants/mainnet/index.ts new file mode 100644 index 00000000..68701846 --- /dev/null +++ b/src/constants/mainnet/index.ts @@ -0,0 +1,200 @@ +import * as MAINNET from '../platform/main'; +import * as AELF from '../platform/AELF'; +import * as tDVV from '../platform/tDVV'; +import * as BSC from '../platform/BSC'; +import * as BASE from '../platform/base'; +import * as TON_MAINNET from '../platform/TON'; +import * as TON_TESTNET from '../platform/TON_Test'; +import DefaultWhitelistMap from './tokenWhitelist.json'; + +import { + DEFAULT_CHAIN_ICON, + DEFAULT_CHAIN_NAME, + SupportedChainId, + SupportedELFChainId, + SupportedTONChainId, + TBridgeChainId, +} from '../chain'; +import { NetworkType } from 'types'; +import { NetworkEnum } from '@aelf-web-login/wallet-adapter-base'; + +export const NETWORK_TYPE: NetworkEnum = NetworkEnum.MAINNET; + +export const MAIN_SIDE_CHAIN_ID = { + mainChain: SupportedELFChainId.AELF, + sideChain: SupportedELFChainId.tDVV, +}; + +export type ChainConstantsType = + | typeof MAINNET + | typeof AELF + | typeof tDVV + | typeof BSC + | typeof BASE + | typeof TON_TESTNET + | typeof TON_MAINNET; + +export type ERC_CHAIN_TYPE = keyof typeof SupportedERCChain; +export type ELF_CHAIN_TYPE = keyof typeof SupportedELFChain; + +export const DEFAULT_ELF_CHAIN = SupportedELFChainId.AELF; +export const DEFAULT_ERC_CHAIN = SupportedChainId.MAINNET; + +export const DEFAULT_MODAL_INITIAL_STATE = { + fromOptions: { chainType: 'ERC', chainId: DEFAULT_ERC_CHAIN }, + toOptions: { chainType: 'ELF', chainId: DEFAULT_ELF_CHAIN }, + switchChainInConnectPortkey: { + status: false, + }, +}; +export const SupportedERCChain: { [k: string | number]: ChainConstantsType } = { + [SupportedChainId.MAINNET]: MAINNET, + [SupportedChainId.BSC_MAINNET]: BSC, + [SupportedChainId.BASE]: BASE, +}; + +export const SupportedTONChain: { [k: string | number]: ChainConstantsType } = { + [SupportedTONChainId.MAINNET]: TON_MAINNET, +}; + +export const SupportedExternalChain: { [k: string | number]: ChainConstantsType } = { + ...SupportedERCChain, + ...SupportedTONChain, +}; + +export const DEFAULT_ERC_CHAIN_INFO = SupportedERCChain[DEFAULT_ERC_CHAIN].CHAIN_INFO; + +export type TELFChainConstantsType = typeof AELF | typeof tDVV; +export const SupportedELFChain: { [k: string | number]: TELFChainConstantsType } = { + [SupportedELFChainId.AELF]: AELF, + [SupportedELFChainId.tDVV]: tDVV, +}; + +export const ACTIVE_CHAIN: any = { + [SupportedELFChainId.AELF]: true, + [SupportedELFChainId.tDVV]: true, + [SupportedChainId.BSC_MAINNET]: true, + [SupportedChainId.MAINNET]: true, + [SupportedChainId.BASE]: true, + [SupportedTONChainId.MAINNET]: true, +}; +export const NATIVE_TOKEN_LIST = ['WETH', 'WBNB']; + +export const CHAIN_NAME: { [chainId in TBridgeChainId]: string } = DEFAULT_CHAIN_NAME; + +export const CHAIN_ICON = DEFAULT_CHAIN_ICON; + +export const NetworkList = [ + { + title: CHAIN_NAME[SupportedELFChainId.AELF], + icon: CHAIN_ICON[SupportedELFChainId.AELF], + info: AELF.CHAIN_INFO, + }, + { + title: CHAIN_NAME[SupportedELFChainId.tDVV], + icon: CHAIN_ICON[SupportedELFChainId.tDVV], + info: tDVV.CHAIN_INFO, + }, + { title: CHAIN_NAME[SupportedChainId.MAINNET], icon: CHAIN_ICON[SupportedChainId.MAINNET], info: MAINNET.CHAIN_INFO }, + { + title: CHAIN_NAME[SupportedChainId.BSC_MAINNET], + icon: CHAIN_ICON[SupportedChainId.BSC_MAINNET], + info: BSC.CHAIN_INFO, + }, + { + title: CHAIN_NAME[SupportedChainId.BASE], + icon: CHAIN_ICON[SupportedChainId.BASE], + info: BASE.CHAIN_INFO, + }, + { + title: CHAIN_NAME[SupportedTONChainId.MAINNET], + icon: CHAIN_ICON[SupportedTONChainId.MAINNET], + info: TON_MAINNET.CHAIN_INFO, + }, +] as unknown as NetworkType[]; + +export const AELF_NODES = { + AELF: AELF.CHAIN_INFO, + tDVV: tDVV.CHAIN_INFO, +}; + +export { DefaultWhitelistMap }; + +export const FormatTokenList = [ + { + fromChainId: [SupportedChainId.BSC_MAINNET], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + fromSymbol: 'WBNB', + toSymbol: 'BNB', + }, + { + fromChainId: [SupportedChainId.MAINNET, SupportedChainId.BASE], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + fromSymbol: 'WETH', + toSymbol: 'ETH', + }, +]; + +export const CrossChainTimeList = [ + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '8', + }, + { + fromChainId: SupportedChainId.MAINNET, + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '40', + }, + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: [SupportedChainId.MAINNET], + time: '40', + }, + { + fromChainId: SupportedChainId.BSC_MAINNET, + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '10', + }, + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: [SupportedChainId.BSC_MAINNET], + time: '10', + }, +]; + +export const BRIDGE_TOKEN_MAP: { [key: string]: string } = { + 'SGR-1': 'SGR', +}; + +export const WEB_LOGIN_CONFIG = { + chainId: SupportedELFChainId.tDVV, + portkeyV2: { + networkType: NetworkEnum.MAINNET, + graphQLUrl: 'https://indexer-api.aefinder.io/api/app/graphql/portkey', + apiServer: 'https://aa-portkey.portkey.finance', + connectServer: 'https://auth-aa-portkey.portkey.finance', + caContractAddress: { + AELF: '2UthYi7AHRdfrqc1YCfeQnjdChDLaas65bW4WxESMGMojFiXj9', + tDVV: '2UthYi7AHRdfrqc1YCfeQnjdChDLaas65bW4WxESMGMojFiXj9', + }, + }, +}; + +export const APP_NAME = 'ebridge.exchange'; + +export const WEBSITE_ICON = 'https://ebridge.exchange/favicon.ico'; + +export const SupportedELFChainList = Object.values(SupportedELFChain); + +export const INDEXER_URL = 'https://indexer-api.aefinder.io/api/app/graphql/ebridge_indexer'; + +export const BASE_URL = 'https://ebridge.exchange'; + +export const BASE_AUTH_URL = 'https://ebridge.exchange'; + +export const TELEGRAM_BOT_ID = '6932375590'; + +export const SHOW_V_CONSOLE = false; + +export const AwakenHost = 'https://app.awaken.finance/'; diff --git a/src/constants/mainnet/tokenWhitelist.json b/src/constants/mainnet/tokenWhitelist.json new file mode 100644 index 00000000..0b2241a7 --- /dev/null +++ b/src/constants/mainnet/tokenWhitelist.json @@ -0,0 +1,217 @@ +{ + "ELF": { + "AELF": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "1": { + "name": "ELF Token", + "decimals": 18, + "symbol": "ELF", + "address": "0xbf2179859fc6D5BEE9Bf9158632Dc51678a4100e" + } + }, + "ETH": { + "AELF": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "1": { + "name": "ETH Token", + "decimals": 18, + "symbol": "ETH", + "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "isNativeToken": true + }, + "8453": { + "name": "ETH Token", + "decimals": 18, + "symbol": "ETH", + "address": "0x4200000000000000000000000000000000000006", + "isNativeToken": true + } + }, + "BNB": { + "AELF": { "name": "BNB Token", "decimals": 8, "symbol": "BNB", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "BNB Token", "decimals": 8, "symbol": "BNB", "address": "", "issueChainId": 9992731 }, + "56": { + "name": "BNB Token", + "decimals": 18, + "symbol": "BNB", + "address": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "isNativeToken": true + } + }, + "USDC": { + "AELF": { + "name": "USDC", + "decimals": 6, + "symbol": "USDC", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "USDC", + "decimals": 6, + "symbol": "USDC", + "address": "", + "issueChainId": 9992731 + }, + "1": { + "name": "USDC Token", + "decimals": 6, + "symbol": "USDC", + "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "56": { + "name": "USDC Token", + "decimals": 18, + "symbol": "USDC", + "address": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d" + }, + "8453": { + "name": "USDC Token", + "decimals": 6, + "symbol": "USDC", + "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" + } + }, + "USDT": { + "AELF": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "1": { + "name": "USDT Token", + "decimals": 6, + "symbol": "USDT", + "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7" + }, + "56": { + "name": "USDT Token", + "decimals": 18, + "symbol": "USDT", + "address": "0x55d398326f99059ff775485246999027b3197955" + }, + "1100": { + "name": "USDT Token", + "decimals": 6, + "symbol": "USDT", + "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs" + } + }, + "DAI": { + "AELF": { + "name": "DAI", + "decimals": 8, + "symbol": "DAI", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "DAI", + "decimals": 8, + "symbol": "DAI", + "address": "", + "issueChainId": 9992731 + }, + "1": { + "name": "DAI Token", + "decimals": 18, + "symbol": "DAI", + "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F" + }, + "56": { + "name": "DAI Token", + "decimals": 18, + "symbol": "DAI", + "address": "0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3" + }, + "8453": { + "name": "DAI Token", + "decimals": 18, + "symbol": "DAI", + "address": "0x50c5725949a6f0c72e6c4a641f24049a917db0cb" + } + }, + "SGR": { + "1": { + "name": "Schrodinger", + "decimals": 8, + "symbol": "SGR", + "address": "0x478156DeAbfAc918369044D52A6BdB5Cc5597994" + }, + "tDVV": { + "name": "Schrodinger", + "decimals": 8, + "symbol": "SGR-1", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "Schrodinger", + "decimals": 8, + "symbol": "SGR-1", + "address": "", + "issueChainId": 9992731 + } + }, + "WRITE": { + "AELF": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 } + }, + "READ": { + "AELF": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 } + }, + "STORAGE": { + "AELF": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 } + }, + "TRAFFIC": { + "AELF": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 } + }, + "CPU": { + "AELF": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 } + }, + "RAM": { + "AELF": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 } + }, + "DISK": { + "AELF": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 } + }, + "NET": { + "AELF": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 } + } +} diff --git a/src/constants/media.tsx b/src/constants/media.tsx new file mode 100644 index 00000000..d6a44175 --- /dev/null +++ b/src/constants/media.tsx @@ -0,0 +1,14 @@ +export const breakpointMap = { + md: 799, + xl: 1179, +}; + +export const media = { + md: `(max-width: ${breakpointMap.md}px)`, + xl: `(max-width: ${breakpointMap.xl}px)`, +}; + +export const mediaQueries = { + md: `@media screen and ${media.md}`, + xl: `@media screen and ${media.xl}`, +}; diff --git a/src/constants/misc.ts b/src/constants/misc.ts new file mode 100644 index 00000000..6862d487 --- /dev/null +++ b/src/constants/misc.ts @@ -0,0 +1,57 @@ +import BigNumber from 'bignumber.js'; + +export enum REQ_CODE { + UserDenied = -1, + Fail = -2, + Success = 1, +} + +export const LONG_MAX = new BigNumber('9000000000000000000'); + +export const ZERO = new BigNumber(0); +export const ONE = new BigNumber(1); + +export const APP_NAME = process.env.NEXT_PUBLIC_APP_NAME || 'AppName'; +export const prefixCls = process.env.NEXT_PUBLIC_PREFIX; + +export const MaxUint256 = '115792089237316195423570985008687907853269984665640564039457584007913129639935'; + +export const CrossFeeToken = 'ELF'; +export const CrossFeeTokenDecimals = 8; + +export const crossTokenMin: any = { + ELF: 0, + WBNB: 0, + WETH: 0, + USDT: 0, +}; + +export const SYMBOL_FORMAT_MAP: Record = { + 'SGR-1': 'SGR', +}; + +export const WEBSITE_NAME = 'eBridge'; + +export const IS_ONLY_SIDE_CHAIN_LIST: string[] = []; + +export const EBRIDGE_PORTKEY_PROJECT_CODE = '13003'; + +export const CONNECT_AELF_WALLET = 'Connect aelf Wallet'; + +export const SERVICE_BUSY_TIP = 'The service is busy. Please try again later.'; + +export const GOT_IT = 'Got it'; + +export const LOADING_TEXT = 'Loading...'; + +export const DEFAULT_NULL_VALUE = '--'; + +export const BUTTON_TEXT_NEXT = 'Next'; + +export const BUTTON_TEXT_SUBMIT = 'Submit'; + +export const BUTTON_TEXT_BACK = 'Back'; + +export const SELECT_CHAIN = 'Select Chain'; + +export const PORTKEY_WEB_WALLET_INFO_KEY = 'PortkeyWebWalletWalletInfo'; diff --git a/src/constants/platform/AELF.ts b/src/constants/platform/AELF.ts new file mode 100644 index 00000000..272d2cae --- /dev/null +++ b/src/constants/platform/AELF.ts @@ -0,0 +1,20 @@ +export const CHAIN_INFO = { + chainId: 'AELF', + exploreUrl: 'https://aelfscan.io/AELF/', + rpcUrl: 'https://aelf-public-node.aelf.io', + // rpcUrl: 'https://explorer.aelf.io/chain', +}; + +export const TOKEN_CONTRACT = 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE'; +export const CROSS_CHAIN_CONTRACT = '2SQ9LeGZYSWmfJcYuQkDQxgd3HzwjamAaaL4Tge2eFSXw2cseq'; +export const BRIDGE_CONTRACT = '2dKF3svqDXrYtA5mYwKfADiHajo37mLZHPHVVuGbEDoD9jSgE8'; +export const TOKEN_POOL = '2TXvtjgTiMwjvEyWGEvfbeQ9P6zVK55pTPcmzvLFBDCMLNUYXV'; + +const EXPAND_CONTRACTS: any = {}; +[TOKEN_CONTRACT].map((i) => { + EXPAND_CONTRACTS[i] = i; +}); + +export const CONTRACTS = { + ...EXPAND_CONTRACTS, +}; diff --git a/src/constants/platform/AELF_Test.ts b/src/constants/platform/AELF_Test.ts new file mode 100644 index 00000000..429deff9 --- /dev/null +++ b/src/constants/platform/AELF_Test.ts @@ -0,0 +1,19 @@ +export const CHAIN_INFO = { + chainId: 'AELF', + exploreUrl: 'https://testnet.aelfscan.io/AELF/', + rpcUrl: 'https://aelf-test-node.aelf.io', +}; + +export const TOKEN_CONTRACT = 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE'; +export const CROSS_CHAIN_CONTRACT = '2SQ9LeGZYSWmfJcYuQkDQxgd3HzwjamAaaL4Tge2eFSXw2cseq'; +export const BRIDGE_CONTRACT = '2rC1X1fudEkJ4Yungj5tYNJ93GmBxbSRiyJqfBkzcT6JshSqz9'; +export const TOKEN_POOL = '2WhjZ9VedLuibSX9qT8BNZ89EW3pf4dtmREUFiBgdswF4XoqZN'; + +const EXPAND_CONTRACTS: any = {}; +[TOKEN_CONTRACT].map((i) => { + EXPAND_CONTRACTS[i] = i; +}); + +export const CONTRACTS = { + ...EXPAND_CONTRACTS, +}; diff --git a/src/constants/platform/BSC.ts b/src/constants/platform/BSC.ts new file mode 100644 index 00000000..3692990b --- /dev/null +++ b/src/constants/platform/BSC.ts @@ -0,0 +1,20 @@ +export const CHAIN_INFO = { + chainId: 56, + exploreUrl: 'https://bscscan.com/', + rpcUrl: 'https://bsc-dataseed1.binance.org', + chainName: 'Binance Smart Chain Mainnet', + nativeCurrency: { + name: 'Binance Chain Native Token', + symbol: 'BNB', + decimals: 18, + }, + iconUrls: ['https://etherscan.io/token/images/bnb_28_2.png'], + rpcUrls: ['https://bsc-dataseed1.binance.org', 'https://bsc-dataseed.binance.org'], + blockExplorerUrls: ['https://bscscan.com/'], +}; + +export const BRIDGE_CONTRACT = '0xbAf5D0cA1e63CD10E479F227d2dc88E066F63872'; +export const BRIDGE_CONTRACT_OUT = '0xE383261ABc2A32bdd54dC9cFB5C77407C5E660ef'; +export const LIMIT_CONTRACT = '0xAA8a4d12F7272fFA2e67F82c88D628f0E629299B'; +export const CREATE_TOKEN_CONTRACT = '0x36dD3cEF201C9ED13ef65fBbC8eC9BC9A1B297cd'; +export const TOKEN_POOL = '0xce037d7175C530E0c5e0B9473B8318eea111dA7a'; diff --git a/src/constants/platform/BSC_Test.ts b/src/constants/platform/BSC_Test.ts new file mode 100644 index 00000000..37ce25b5 --- /dev/null +++ b/src/constants/platform/BSC_Test.ts @@ -0,0 +1,21 @@ +export const CHAIN_INFO = { + chainId: 97, + exploreUrl: 'https://testnet.bscscan.com/', + rpcUrl: 'https://data-seed-prebsc-1-s2.binance.org:8545', + chainName: 'Binance Smart Chain Testnet', + nativeCurrency: { + name: 'Binance Chain Native Token', + symbol: 'tBNB', + decimals: 18, + }, + iconUrls: ['https://etherscan.io/token/images/bnb_28_2.png'], + rpcUrls: ['https://data-seed-prebsc-1-s2.binance.org:8545'], + blockExplorerUrls: ['https://testnet.bscscan.com/'], +}; + +export const BRIDGE_CONTRACT = '0xFA51BBf197183ce43509C67ce28095f66F60a518'; +export const BRIDGE_CONTRACT_OUT = '0xA56cb58f75D440258973dBC2a3D78237ca67b705'; +export const LIMIT_CONTRACT = '0x22A05FEAb252fC903880EB37002862c997404AA0'; +export const CREATE_TOKEN_CONTRACT = '0x658dfbB3A929e70cc52248cee174941f089d48f1'; + +export const TOKEN_POOL = '0x2eEE5aa5be92a01f76b9f599fcc4e51aCC3E15eB'; diff --git a/src/constants/platform/TON.ts b/src/constants/platform/TON.ts new file mode 100644 index 00000000..b3e7dc71 --- /dev/null +++ b/src/constants/platform/TON.ts @@ -0,0 +1,22 @@ +const TON_RPC_URL = `https://toncenter.com/api/v2/jsonRPC?api_key=${process.env.NEXT_PUBLIC_TON_API_KEY}`; +export const CHAIN_INFO = { + chainId: 1100, + exploreUrl: 'https://tonviewer.com/', + rpcUrl: TON_RPC_URL, + chainName: 'Ton', + nativeCurrency: { + name: 'TON Chain Native Token', + symbol: 'TON', + decimals: 18, + }, + iconUrls: ['https://etherscan.io/token/images/bnb_28_2.png'], + rpcUrls: [TON_RPC_URL], + blockExplorerUrls: ['https://tonviewer.com/'], +}; + +export const BRIDGE_CONTRACT = 'EQBnIw0A17M18zeEHEekgpL32e6yu9cx57PnC990pByv2qJN'; + +// TON chain one token corresponds to one fund pool address +export const TOKEN_POOL_MAP = { + USDT: 'EQBfBX543e7-MXC9iYoq1td_DsMufSqbfHN5L-biMTJqwf-e', +}; diff --git a/src/constants/platform/TON_Test.ts b/src/constants/platform/TON_Test.ts new file mode 100644 index 00000000..e16327d7 --- /dev/null +++ b/src/constants/platform/TON_Test.ts @@ -0,0 +1,23 @@ +export const CHAIN_INFO = { + chainId: 1100, + exploreUrl: 'https://testnet.tonviewer.com/', + rpcUrl: + 'https://testnet.toncenter.com/api/v2/jsonRPC?api_key=5829467610276f67be156f2b8dccf7369b4c0f52aed79fa2bbee614b3012f162', + chainName: 'Ton TestNet', + nativeCurrency: { + name: 'TON Chain Native Token', + symbol: 'TON', + decimals: 18, + }, + iconUrls: ['https://etherscan.io/token/images/bnb_28_2.png'], + rpcUrls: [ + 'https://testnet.toncenter.com/api/v2/jsonRPC?api_key=5829467610276f67be156f2b8dccf7369b4c0f52aed79fa2bbee614b3012f162', + ], + blockExplorerUrls: ['https://testnet.tonviewer.com/'], +}; + +export const BRIDGE_CONTRACT = 'kQDS511tzowt2x1xyIDgpglhaz6wG9uVP2t4BixFTViYQoM_'; +// TON chain one token corresponds to one fund pool address +export const TOKEN_POOL_MAP = { + USDT: 'kQCOgvqpldcUabiUJdgtHffTy9_-IfvIoZ9Rk26D8q5uVDf9', +}; diff --git a/src/constants/platform/base.ts b/src/constants/platform/base.ts new file mode 100644 index 00000000..59dfe80f --- /dev/null +++ b/src/constants/platform/base.ts @@ -0,0 +1,20 @@ +export const CHAIN_INFO = { + chainId: 8453, + exploreUrl: 'https://basescan.org/', + rpcUrl: 'https://mainnet.base.org', + rpcUrls: ['https://mainnet.base.org'], + chainName: 'Base', + iconUrls: ['https://basescan.org/assets/base/images/svg/logos/chain-light.svg'], + nativeCurrency: { + name: 'Base Chain Native Token', + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: ['https://basescan.org/'], +}; + +export const BRIDGE_CONTRACT = '0x06dFaE0488FCa172500EeAd593Cb978DC5c32193'; +export const BRIDGE_CONTRACT_OUT = '0xE30382636E09a94aAF7b7e8e03a948624AbdE284'; +export const LIMIT_CONTRACT = '0x01A2EA8D36283F2dc93F31EB8378c1E737938ef4'; +export const CREATE_TOKEN_CONTRACT = '0x36dD3cEF201C9ED13ef65fBbC8eC9BC9A1B297cd'; +export const TOKEN_POOL = '0x61e8A390c0bD8a49E2E54568F62169beb2026115'; diff --git a/src/constants/platform/base_sepolia.ts b/src/constants/platform/base_sepolia.ts new file mode 100644 index 00000000..eaae9c12 --- /dev/null +++ b/src/constants/platform/base_sepolia.ts @@ -0,0 +1,21 @@ +export const CHAIN_INFO = { + chainId: 84532, + exploreUrl: 'https://sepolia.basescan.org/', + rpcUrl: 'https://sepolia.base.org', + rpcUrls: ['https://sepolia.base.org'], + chainName: 'Base Sepolia', + iconUrls: ['https://basescan.org/assets/base/images/svg/logos/chain-light.svg'], + nativeCurrency: { + name: 'Base Chain Native Token', + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: ['https://sepolia.basescan.org/'], +}; + +export const BRIDGE_CONTRACT = '0x7e308DC172faa2a6560C2cd806e8282C51E5BFA5'; +export const BRIDGE_CONTRACT_OUT = '0xA251aE4C14C53d980699b14319bf2Ad5A4bC4A14'; +export const LIMIT_CONTRACT = '0x11a86274622fCE5C9d95e9f9ac9A1ae8b4531cA6'; +export const CREATE_TOKEN_CONTRACT = ''; + +export const TOKEN_POOL = '0x66760B644668d4E7de273bc788F915Efd5536332'; diff --git a/src/constants/platform/goreli.ts b/src/constants/platform/goreli.ts new file mode 100644 index 00000000..dc0adbfe --- /dev/null +++ b/src/constants/platform/goreli.ts @@ -0,0 +1,10 @@ +export const CHAIN_INFO = { + chainId: 5, + exploreUrl: 'https://goerli.etherscan.io/', + rpcUrl: 'https://goerli.infura.io/v3/fce90b852ec6426eb706bc1a6dcd35a6', +}; + +export const BRIDGE_CONTRACT = '0xd6A2BbDB1d23155A78aeB3dEB8a4df0d96AB007D'; +export const BRIDGE_CONTRACT_OUT = '0x8E0cF442690a9395C42623F6503Ab926c739f59E'; +export const LIMIT_CONTRACT = ''; +export const CREATE_TOKEN_CONTRACT = ''; diff --git a/src/constants/platform/kovan.ts b/src/constants/platform/kovan.ts new file mode 100644 index 00000000..2c344de0 --- /dev/null +++ b/src/constants/platform/kovan.ts @@ -0,0 +1,9 @@ +export const CHAIN_INFO = { + chainId: 42, + exploreUrl: 'https://kovan.etherscan.io/', + rpcUrl: 'https://kovan.infura.io/v3/ec0fa04ecdc94894b23a773cff776d05', +}; +export const BRIDGE_CONTRACT = '0x164322657FC57EA95CAc4bF6623E53CA0f952E11'; +export const BRIDGE_CONTRACT_OUT = '0x26a44A0383F15f7f83D84eb95c4b7762d8de995A'; +export const LIMIT_CONTRACT = ''; +export const CREATE_TOKEN_CONTRACT = ''; diff --git a/src/constants/platform/main.ts b/src/constants/platform/main.ts new file mode 100644 index 00000000..6070eda9 --- /dev/null +++ b/src/constants/platform/main.ts @@ -0,0 +1,10 @@ +export const CHAIN_INFO = { + chainId: 1, + exploreUrl: 'https://etherscan.io/', + rpcUrl: 'https://eth-mainnet.token.im', +}; +export const BRIDGE_CONTRACT = '0x7ffD4a8823626AF7E181dF36AAFF4270Aeb96Ddd'; +export const BRIDGE_CONTRACT_OUT = '0x648C372668Fb65f46DB478AF0302330d06B16b8B'; +export const LIMIT_CONTRACT = '0xBDDfac1151A307e1bF7A8cEA4fd7999eF67bdb41'; +export const CREATE_TOKEN_CONTRACT = '0x3468E7c7c20c16E8A31F7A0B3A9dCbB99306e5cB'; +export const TOKEN_POOL = '0xce037d7175C530E0c5e0B9473B8318eea111dA7a'; diff --git a/src/constants/platform/sepolia.ts b/src/constants/platform/sepolia.ts new file mode 100644 index 00000000..9d938e87 --- /dev/null +++ b/src/constants/platform/sepolia.ts @@ -0,0 +1,13 @@ +export const CHAIN_INFO = { + chainId: 11155111, + exploreUrl: 'https://sepolia.etherscan.io/', + // rpcUrl: 'https://sepolia.infura.io/v3/fce90b852ec6426eb706bc1a6dcd35a6', + rpcUrl: 'https://sepolia.drpc.org', +}; + +export const BRIDGE_CONTRACT = '0x8243C4927257ef20dbF360b012C9f72f9A6427c3'; +export const BRIDGE_CONTRACT_OUT = '0x3c37E0A09eAFEaA7eFB57107802De1B28A6f5F07'; +export const LIMIT_CONTRACT = '0x69aDad711f41C32FF48A6B95f0d66c635185D521'; +export const CREATE_TOKEN_CONTRACT = '0xBec2bBcb0fA594544c79bBc31d6Fd58F55B6dC30'; + +export const TOKEN_POOL = '0x57932F1F3eBCadb6f03B29ab8ac1986DD6250c1a'; diff --git a/src/constants/platform/tDVV.ts b/src/constants/platform/tDVV.ts new file mode 100644 index 00000000..3a7b6e5d --- /dev/null +++ b/src/constants/platform/tDVV.ts @@ -0,0 +1,20 @@ +export const CHAIN_INFO = { + chainId: 'tDVV', + exploreUrl: 'https://aelfscan.io/tDVV/', + rpcUrl: 'https://tdvv-public-node.aelf.io', + // rpcUrl: 'https://tdvv-explorer.aelf.io/chain', +}; + +export const TOKEN_CONTRACT = '7RzVGiuVWkvL4VfVHdZfQF2Tri3sgLe9U991bohHFfSRZXuGX'; +export const CROSS_CHAIN_CONTRACT = '2snHc8AMh9QMbCAa7XXmdZZVM5EBZUUPDdLjemwUJkBnL6k8z9'; +export const BRIDGE_CONTRACT = 'GZs6wyPDfz3vdEmgVd3FyrQfaWSXo9uRvc7Fbp5KSLKwMAANd'; +export const TOKEN_POOL = 'ttm4YsRRTV2zWx7hDpBV5gfLfWhqtde8N4XYQtZyoTyKi3tq9'; + +const EXPAND_CONTRACTS: any = {}; +[TOKEN_CONTRACT].map((i) => { + EXPAND_CONTRACTS[i] = i; +}); + +export const CONTRACTS = { + ...EXPAND_CONTRACTS, +}; diff --git a/src/constants/platform/tDVV_Test.ts b/src/constants/platform/tDVV_Test.ts new file mode 100644 index 00000000..22e2fb57 --- /dev/null +++ b/src/constants/platform/tDVV_Test.ts @@ -0,0 +1,18 @@ +export const CHAIN_INFO = { + chainId: 'tDVV', + exploreUrl: 'https://explorer.aelf.io/', + rpcUrl: 'http://192.168.66.106:8000', +}; + +export const TOKEN_CONTRACT = '7RzVGiuVWkvL4VfVHdZfQF2Tri3sgLe9U991bohHFfSRZXuGX'; +export const CROSS_CHAIN_CONTRACT = '2snHc8AMh9QMbCAa7XXmdZZVM5EBZUUPDdLjemwUJkBnL6k8z9'; +export const BRIDGE_CONTRACT = '2onFLTnPEiZrXGomzJ8g74cBre2cJuHrn1yBJF3P6Xu9K5Gbth'; + +const EXPAND_CONTRACTS: any = {}; +[TOKEN_CONTRACT].map((i) => { + EXPAND_CONTRACTS[i] = i; +}); + +export const CONTRACTS = { + ...EXPAND_CONTRACTS, +}; diff --git a/src/constants/platform/tDVW_Test.ts b/src/constants/platform/tDVW_Test.ts new file mode 100644 index 00000000..e81c877b --- /dev/null +++ b/src/constants/platform/tDVW_Test.ts @@ -0,0 +1,20 @@ +export const CHAIN_INFO = { + chainId: 'tDVW', + exploreUrl: 'https://testnet.aelfscan.io/tDVW/', + rpcUrl: 'https://tdvw-test-node.aelf.io', +}; + +export const TOKEN_CONTRACT = 'ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx'; +export const CROSS_CHAIN_CONTRACT = '2PC7Jhb5V6iZXxz8uQUWvWubYkAoCVhtRGSL7VhTWX85R8DBuN'; +export const BRIDGE_CONTRACT = '293dHYMKjfEuTEkveb5h775avTyW69jBgHMYiWQqtdSdTfsfEP'; + +export const TOKEN_POOL = '2DCTaXxFH63uG9tdXPGgGst7LK18y5zjBVfxamz1MQSi5hiuPM'; + +const EXPAND_CONTRACTS: any = {}; +[TOKEN_CONTRACT].map((i) => { + EXPAND_CONTRACTS[i] = i; +}); + +export const CONTRACTS = { + ...EXPAND_CONTRACTS, +}; diff --git a/src/constants/storages.ts b/src/constants/storages.ts new file mode 100644 index 00000000..a9c23cf6 --- /dev/null +++ b/src/constants/storages.ts @@ -0,0 +1,16 @@ +const storages = { + useChain: 'use-chain', + useWallet: 'use-wallet-1', + useWhitelist: 'use-white-list', + userERCChainId: 'user-erc-chain-id', + userELFChainId: 'user-elf-chain-id', + contractsFileDescriptorBase64: 'contracts-file-descriptor-base64-250409', + crossChainReceived: 'cross-chain-received', + portkeyCAAddresses: 'portkey-ca-addresses', +}; +export default storages; + +export enum LocalStorageKey { + TOKEN_TYPE = 'token_type', + ACCESS_TOKEN = 'ebridge_access_token', +} diff --git a/src/constants/test1/AELF.ts b/src/constants/test1/AELF.ts new file mode 100644 index 00000000..d99ba829 --- /dev/null +++ b/src/constants/test1/AELF.ts @@ -0,0 +1,18 @@ +export const CHAIN_INFO = { + chainId: 'AELF', + exploreUrl: 'https://explorer-test.aelf.io/', + rpcUrl: 'http://192.168.66.61:8000', +}; + +export const TOKEN_CONTRACT = 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE'; +export const CROSS_CHAIN_CONTRACT = '2SQ9LeGZYSWmfJcYuQkDQxgd3HzwjamAaaL4Tge2eFSXw2cseq'; +export const BRIDGE_CONTRACT = '2dKF3svqDXrYtA5mYwKfADiHajo37mLZHPHVVuGbEDoD9jSgE8'; + +const EXPAND_CONTRACTS: any = {}; +[TOKEN_CONTRACT].map((i) => { + EXPAND_CONTRACTS[i] = i; +}); + +export const CONTRACTS = { + ...EXPAND_CONTRACTS, +}; diff --git a/src/constants/test1/BSC_Test.ts b/src/constants/test1/BSC_Test.ts new file mode 100644 index 00000000..16f65f8e --- /dev/null +++ b/src/constants/test1/BSC_Test.ts @@ -0,0 +1,18 @@ +export const CHAIN_INFO = { + chainId: 97, + exploreUrl: 'https://testnet.bscscan.com/', + rpcUrl: 'https://data-seed-prebsc-1-s2.binance.org:8545', + chainName: 'Binance Smart Chain Testnet', + nativeCurrency: { + name: 'Binance Chain Native Token', + symbol: 'tBNB', + decimals: 18, + }, + iconUrls: ['https://etherscan.io/token/images/bnb_28_2.png'], + rpcUrls: ['https://data-seed-prebsc-1-s2.binance.org:8545'], + blockExplorerUrls: ['https://testnet.bscscan.com/'], +}; + +export const BRIDGE_CONTRACT = '0x802a044dAe0E8a49cb4a255D0dC5C677c35106B7'; +export const BRIDGE_CONTRACT_OUT = '0x26965A367751411BcDC852D0f2004858f873A65F'; +export const LIMIT_CONTRACT = '0xEC5fB890A1f7Db902E7226c82c5B85696e53991f'; diff --git a/src/constants/test1/index.tsx b/src/constants/test1/index.tsx new file mode 100644 index 00000000..e9c5ca48 --- /dev/null +++ b/src/constants/test1/index.tsx @@ -0,0 +1,148 @@ +import * as AELF_Test from './AELF'; +import * as tDVV_Test from './tDVV'; +import * as BSC_TESTNET from './BSC_Test'; +import * as SEPOLIA from './sepolia'; +import DefaultWhitelistMap from './tokenWhitelist.json'; +import { + DEFAULT_CHAIN_ICON, + DEFAULT_CHAIN_NAME, + SupportedChainId, + SupportedELFChainId, + TBridgeChainId, +} from '../chain'; +import { NetworkType } from 'types'; +import { IconInfo } from 'types/misc'; + +export const MAIN_SIDE_CHAIN_ID = { + mainChain: SupportedELFChainId.AELF, + sideChain: SupportedELFChainId.tDVV, +}; + +export type ChainConstantsType = typeof AELF_Test | typeof tDVV_Test | typeof SEPOLIA | typeof BSC_TESTNET; + +export type ERC_CHAIN_TYPE = keyof typeof SupportedERCChain; +export type ELF_CHAIN_TYPE = keyof typeof SupportedELFChain; + +export const DEFAULT_ELF_CHAIN = SupportedELFChainId.AELF; +export const DEFAULT_ERC_CHAIN = SupportedChainId.SEPOLIA; + +export const DEFAULT_MODAL_INITIAL_STATE = { + fromOptions: { chainType: 'ERC', chainId: DEFAULT_ERC_CHAIN }, + toOptions: { chainType: 'ELF', chainId: DEFAULT_ELF_CHAIN }, + switchChainInConnectPortkey: { + status: false, + }, +}; +export const SupportedERCChain: { [k: string | number]: ChainConstantsType } = { + [SupportedChainId.BSC_TESTNET]: BSC_TESTNET, + [SupportedChainId.SEPOLIA]: SEPOLIA, +}; +export const DEFAULT_ERC_CHAIN_INFO = SupportedERCChain[DEFAULT_ERC_CHAIN].CHAIN_INFO; + +export const SupportedELFChain: { [k: string | number]: ChainConstantsType } = { + [SupportedELFChainId.AELF]: AELF_Test, + [SupportedELFChainId.tDVV]: tDVV_Test, +}; + +export const ACTIVE_CHAIN: any = { + [SupportedELFChainId.AELF]: true, + [SupportedELFChainId.tDVV]: true, + [SupportedChainId.BSC_TESTNET]: true, + [SupportedChainId.SEPOLIA]: true, +}; +export const NATIVE_TOKEN_LIST = ['WETH', 'WBNB']; + +export const CHAIN_NAME: { [chainId in TBridgeChainId]: string } = { + ...DEFAULT_CHAIN_NAME, + [SupportedELFChainId.AELF]: 'aelf MainChain Testnet', + [SupportedELFChainId.tDVV]: 'aelf dAppChain Testnet', + [SupportedELFChainId.tDVW]: 'aelf dAppChain Testnet', +}; + +export const CHAIN_ICON: { [chainId in TBridgeChainId]: IconInfo } = { + ...DEFAULT_CHAIN_ICON, + [SupportedELFChainId.AELF]: { + type: 'aelfTestnet', + }, + [SupportedELFChainId.tDVV]: { + type: 'aelfTestnet', + }, + [SupportedELFChainId.tDVW]: { + type: 'aelfTestnet', + }, +}; + +export const NetworkList = [ + { title: CHAIN_NAME[SupportedChainId.SEPOLIA], icon: CHAIN_ICON[SupportedChainId.SEPOLIA], info: SEPOLIA.CHAIN_INFO }, + { + title: CHAIN_NAME[SupportedELFChainId.AELF], + icon: CHAIN_ICON[SupportedELFChainId.AELF], + info: AELF_Test.CHAIN_INFO, + }, + { + title: CHAIN_NAME[SupportedELFChainId.tDVV], + icon: CHAIN_ICON[SupportedELFChainId.tDVV], + info: tDVV_Test.CHAIN_INFO, + }, + { + title: CHAIN_NAME[SupportedChainId.BSC_TESTNET], + icon: CHAIN_ICON[SupportedChainId.BSC_TESTNET], + info: BSC_TESTNET.CHAIN_INFO, + }, +] as unknown as NetworkType[]; + +export const AELF_NODES = { + AELF: AELF_Test.CHAIN_INFO, + tDVV: tDVV_Test.CHAIN_INFO, +}; + +export { DefaultWhitelistMap }; + +export const FormatTokenList = [ + { + fromChainId: [SupportedChainId.BSC_TESTNET], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + fromSymbol: 'WBNB', + toSymbol: 'BNB', + }, + { + fromChainId: [SupportedChainId.SEPOLIA], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + fromSymbol: 'WETH', + toSymbol: 'ETH', + }, +]; + +export const CrossChainTimeList = [ + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '4', + }, + { + fromChainId: SupportedChainId.BSC_TESTNET, + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '10', + }, + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: SupportedChainId.BSC_TESTNET, + time: '10', + }, + { + fromChainId: SupportedChainId.SEPOLIA, + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '40', + }, + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: SupportedChainId.SEPOLIA, + time: '40', + }, +]; + +export const TELEGRAM_BOT_ID = ''; + +export const SHOW_V_CONSOLE = true; + +export const AwakenHost = 'https://test-app.awaken.finance/'; diff --git a/src/constants/test1/sepolia.ts b/src/constants/test1/sepolia.ts new file mode 100644 index 00000000..6d3b5777 --- /dev/null +++ b/src/constants/test1/sepolia.ts @@ -0,0 +1,9 @@ +export const CHAIN_INFO = { + chainId: 11155111, + exploreUrl: 'https://sepolia.etherscan.io/', + rpcUrl: 'https://sepolia.infura.io/v3/fce90b852ec6426eb706bc1a6dcd35a6', +}; + +export const BRIDGE_CONTRACT = '0x6BD06F033B5d1Bc9453B88b1cE6c63A907e7C13a'; +export const BRIDGE_CONTRACT_OUT = '0xc4749636D827107b2df539852648e6E9f19F4520'; +export const LIMIT_CONTRACT = '0x88D3df6d482046c8c538B9c9B7f7342e83811b83'; diff --git a/src/constants/test1/tDVV.ts b/src/constants/test1/tDVV.ts new file mode 100644 index 00000000..8bcfe12e --- /dev/null +++ b/src/constants/test1/tDVV.ts @@ -0,0 +1,19 @@ +export const CHAIN_INFO = { + chainId: 'tDVV', + exploreUrl: 'https://explorer-test-side02.aelf.io/', + // rpcUrl: 'https://tdvv-public-node.aelf.io', + rpcUrl: 'http://192.168.66.100:8000', +}; + +export const TOKEN_CONTRACT = '7RzVGiuVWkvL4VfVHdZfQF2Tri3sgLe9U991bohHFfSRZXuGX'; +export const CROSS_CHAIN_CONTRACT = '2snHc8AMh9QMbCAa7XXmdZZVM5EBZUUPDdLjemwUJkBnL6k8z9'; +export const BRIDGE_CONTRACT = 'bXRnskXaJUPcz4uVmX29Fht87tQbGst7uixFGC7SniwVm7Uf9'; + +const EXPAND_CONTRACTS: any = {}; +[TOKEN_CONTRACT].map((i) => { + EXPAND_CONTRACTS[i] = i; +}); + +export const CONTRACTS = { + ...EXPAND_CONTRACTS, +}; diff --git a/src/constants/test1/tokenWhitelist.json b/src/constants/test1/tokenWhitelist.json new file mode 100644 index 00000000..df3a161c --- /dev/null +++ b/src/constants/test1/tokenWhitelist.json @@ -0,0 +1,136 @@ +{ + "ELF": { + "AELF": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "97": { + "name": "ELF Token", + "decimals": 18, + "symbol": "ELF", + "address": "0xd1CD51a8d28ab58464839ba840E16950A6a635ad" + }, + "11155111": { + "name": "ELF Token", + "decimals": 18, + "symbol": "ELF", + "address": "0x8adD57b8aD6C291BC3E3ffF89F767fcA08e0E7Ab" + } + }, + "USDT": { + "AELF": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "97": { + "name": "USDT Token", + "decimals": 18, + "symbol": "USDT", + "address": "0x3F280eE5876CE8B15081947E0f189E336bb740A5" + }, + "11155111": { + "name": "USDT Token", + "decimals": 6, + "symbol": "USDT", + "address": "0x60eeCc4d19f65B9EaDe628F2711C543eD1cE6679" + } + }, + "ETH": { + "AELF": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "11155111": { + "name": "ETH Token", + "decimals": 18, + "symbol": "ETH", + "address": "0x035900292c309d8beCBCAFb3227238bec0EBa253", + "isNativeToken": true + } + }, + "BNB": { + "AELF": { "name": "BNB Token", "decimals": 8, "symbol": "BNB", "address": "", "issueChainId": 9992731 }, + "97": { + "name": "BNB Token", + "decimals": 18, + "symbol": "BNB", + "address": "0x0CBAb7E71f969Bfb3eF5b13542E9087a73244F02", + "isNativeToken": true + }, + "tDVW": { "name": "BNB Token", "decimals": 8, "symbol": "BNB", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "BNB Token", "decimals": 8, "symbol": "BNB", "address": "", "issueChainId": 9992731 } + }, + "WRITE": { + "AELF": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 } + }, + "READ": { + "AELF": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 } + }, + "STORAGE": { + "AELF": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 } + }, + "TRAFFIC": { + "AELF": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 } + }, + "CPU": { + "AELF": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 } + }, + "RAM": { + "AELF": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 } + }, + "DISK": { + "AELF": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 } + }, + "NET": { + "AELF": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 } + } +} diff --git a/src/constants/testnet/index.ts b/src/constants/testnet/index.ts new file mode 100644 index 00000000..d232620c --- /dev/null +++ b/src/constants/testnet/index.ts @@ -0,0 +1,218 @@ +import * as MAINNET from '../platform/main'; +import * as KOVAN from '../platform/kovan'; +import * as GORELI from '../platform/goreli'; +import * as AELF_Test from '../platform/AELF_Test'; +import * as tDVV_Test from '../platform/tDVV_Test'; +import * as tDVW_Test from '../platform/tDVW_Test'; +import * as BSC_TESTNET from '../platform/BSC_Test'; +import * as TON_TESTNET from '../platform/TON_Test'; +import * as SEPOLIA from '../platform/sepolia'; +import * as BASE_SEPOLIA from '../platform/base_sepolia'; +import DefaultWhitelistMap from './tokenWhitelist.json'; +import { + DEFAULT_CHAIN_ICON, + DEFAULT_CHAIN_NAME, + SupportedChainId, + SupportedELFChainId, + SupportedTONChainId, + TBridgeChainId, +} from '../chain'; +import { NetworkType } from 'types'; +import { NetworkEnum } from '@aelf-web-login/wallet-adapter-base'; + +export const NETWORK_TYPE: NetworkEnum = NetworkEnum.TESTNET; + +export const MAIN_SIDE_CHAIN_ID = { + mainChain: SupportedELFChainId.AELF, + sideChain: SupportedELFChainId.tDVW, +}; + +export type ChainConstantsType = + | typeof MAINNET + | typeof KOVAN + | typeof AELF_Test + | typeof tDVV_Test + | typeof tDVW_Test + | typeof GORELI + | typeof SEPOLIA + | typeof BSC_TESTNET + | typeof BASE_SEPOLIA + | typeof TON_TESTNET; + +export type ERC_CHAIN_TYPE = keyof typeof SupportedERCChain; +export type ELF_CHAIN_TYPE = keyof typeof SupportedELFChain; + +export const DEFAULT_ELF_CHAIN = SupportedELFChainId.AELF; +export const DEFAULT_ERC_CHAIN = SupportedChainId.SEPOLIA; + +export const DEFAULT_MODAL_INITIAL_STATE = { + fromOptions: { chainType: 'ERC', chainId: DEFAULT_ERC_CHAIN }, + toOptions: { chainType: 'ELF', chainId: DEFAULT_ELF_CHAIN }, + switchChainInConnectPortkey: { + status: false, + }, +}; +export const SupportedERCChain: { [k: string | number]: ChainConstantsType } = { + [SupportedChainId.MAINNET]: MAINNET, + [SupportedChainId.KOVAN]: KOVAN, + [SupportedChainId.GORELI]: GORELI, + [SupportedChainId.BSC_TESTNET]: BSC_TESTNET, + [SupportedChainId.SEPOLIA]: SEPOLIA, + [SupportedChainId.BASE_SEPOLIA]: BASE_SEPOLIA, +}; + +export const SupportedTONChain: { [k: string | number]: ChainConstantsType } = { + [SupportedTONChainId.TESTNET]: TON_TESTNET, +}; + +export const SupportedExternalChain: { [k: string | number]: ChainConstantsType } = { + ...SupportedERCChain, + ...SupportedTONChain, +}; + +export const DEFAULT_ERC_CHAIN_INFO = SupportedERCChain[DEFAULT_ERC_CHAIN].CHAIN_INFO; + +export type TELFChainConstantsType = typeof AELF_Test | typeof tDVW_Test; +export const SupportedELFChain: { [k: string | number]: TELFChainConstantsType } = { + [SupportedELFChainId.AELF]: AELF_Test, + [SupportedELFChainId.tDVW]: tDVW_Test, +}; + +export const ACTIVE_CHAIN: any = { + [SupportedELFChainId.AELF]: true, + [SupportedELFChainId.tDVW]: true, + [SupportedChainId.BSC_TESTNET]: true, + [SupportedChainId.SEPOLIA]: true, + [SupportedChainId.BASE_SEPOLIA]: true, + [SupportedTONChainId.TESTNET]: true, +}; +export const NATIVE_TOKEN_LIST = ['WETH', 'WBNB']; + +export const CHAIN_NAME: { [chainId in TBridgeChainId]: string } = { + ...DEFAULT_CHAIN_NAME, + [SupportedELFChainId.AELF]: 'aelf MainChain Testnet', + [SupportedELFChainId.tDVV]: 'aelf dAppChain Testnet', + [SupportedELFChainId.tDVW]: 'aelf dAppChain Testnet', +}; + +export const CHAIN_ICON = DEFAULT_CHAIN_ICON; + +export const NetworkList = [ + { title: CHAIN_NAME[SupportedChainId.SEPOLIA], icon: CHAIN_ICON[SupportedChainId.SEPOLIA], info: SEPOLIA.CHAIN_INFO }, + { + title: CHAIN_NAME[SupportedChainId.BASE_SEPOLIA], + icon: CHAIN_ICON[SupportedChainId.BASE_SEPOLIA], + info: BASE_SEPOLIA.CHAIN_INFO, + }, + // { title: CHAIN_NAME[SupportedChainId.GORELI], icon: CHAIN_ICON[SupportedChainId.GORELI], info: GORELI.CHAIN_INFO }, + // { title: CHAIN_NAME[SupportedChainId.KOVAN], icon: CHAIN_ICON[SupportedChainId.KOVAN], info: KOVAN.CHAIN_INFO }, + { + title: CHAIN_NAME[SupportedELFChainId.AELF], + icon: CHAIN_ICON[SupportedELFChainId.AELF], + info: AELF_Test.CHAIN_INFO, + }, + // { title: CHAIN_NAME[SupportedELFChainId.tDVV], icon: CHAIN_ICON[SupportedELFChainId.tDVV], info: tDVV.CHAIN_INFO }, + { + title: CHAIN_NAME[SupportedELFChainId.tDVW], + icon: CHAIN_ICON[SupportedELFChainId.tDVW], + info: tDVW_Test.CHAIN_INFO, + }, + { + title: CHAIN_NAME[SupportedChainId.BSC_TESTNET], + icon: CHAIN_ICON[SupportedChainId.BSC_TESTNET], + info: BSC_TESTNET.CHAIN_INFO, + }, + { + title: CHAIN_NAME[SupportedTONChainId.TESTNET], + icon: CHAIN_ICON[SupportedChainId.BSC_TESTNET], + info: TON_TESTNET.CHAIN_INFO, + }, +] as unknown as NetworkType[]; + +export const AELF_NODES = { + AELF: AELF_Test.CHAIN_INFO, + tDVW: tDVW_Test.CHAIN_INFO, +}; + +export { DefaultWhitelistMap }; + +export const FormatTokenList = [ + { + fromChainId: [SupportedChainId.BSC_TESTNET], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + fromSymbol: 'WBNB', + toSymbol: 'BNB', + }, + { + fromChainId: [SupportedChainId.SEPOLIA, SupportedChainId.BASE_SEPOLIA], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + fromSymbol: 'WETH', + toSymbol: 'ETH', + }, +]; + +export const CrossChainTimeList = [ + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '8', + }, + { + fromChainId: SupportedChainId.BSC_TESTNET, + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '10', + }, + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: SupportedChainId.BSC_TESTNET, + time: '10', + }, + { + fromChainId: SupportedChainId.SEPOLIA, + toChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + time: '40', + }, + { + fromChainId: [SupportedELFChainId.AELF, SupportedELFChainId.tDVV, SupportedELFChainId.tDVW], + toChainId: SupportedChainId.SEPOLIA, + time: '40', + }, +]; + +export const BRIDGE_TOKEN_MAP: { [key: string]: string } = { + 'TESTSGR-1': 'SGR', + 'SGR-1': 'SGR', + WUSDvT1: 'WUSD', + WUSDTEST: 'WUSD', +}; +export const WEB_LOGIN_CONFIG = { + chainId: SupportedELFChainId.tDVW, + portkeyV2: { + networkType: NetworkEnum.TESTNET, + graphQLUrl: 'https://test-indexer-api.aefinder.io/api/app/graphql/portkey', + apiServer: 'https://aa-portkey-test.portkey.finance', + connectServer: 'https://auth-aa-portkey-test.portkey.finance', + caContractAddress: { + AELF: '238X6iw1j8YKcHvkDYVtYVbuYk2gJnK8UoNpVCtssynSpVC8hb', + tDVW: '238X6iw1j8YKcHvkDYVtYVbuYk2gJnK8UoNpVCtssynSpVC8hb', + }, + }, +}; + +export const APP_NAME = 'ebridge.exchange'; + +export const WEBSITE_ICON = 'https://test.ebridge.exchange/favicon.ico'; + +export const SupportedELFChainList = Object.values(SupportedELFChain); + +export const INDEXER_URL = 'https://test-indexer-api.aefinder.io/api/app/graphql/ebridgeindexer'; + +export const BASE_URL = 'https://test.ebridge.exchange'; + +export const BASE_AUTH_URL = 'https://test.ebridge.exchange'; + +export const TELEGRAM_BOT_ID = '7220041137'; + +export const SHOW_V_CONSOLE = true; + +export const AwakenHost = 'https://test-app.awaken.finance/'; diff --git a/src/constants/testnet/tokenWhitelist.json b/src/constants/testnet/tokenWhitelist.json new file mode 100644 index 00000000..3a32d398 --- /dev/null +++ b/src/constants/testnet/tokenWhitelist.json @@ -0,0 +1,251 @@ +{ + "ELF": { + "AELF": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "Native Token", "decimals": 8, "symbol": "ELF", "address": "", "issueChainId": 9992731 }, + "97": { + "name": "ELF Token", + "decimals": 18, + "symbol": "ELF", + "address": "0xd1CD51a8d28ab58464839ba840E16950A6a635ad" + }, + "11155111": { + "name": "ELF Token", + "decimals": 18, + "symbol": "ELF", + "address": "0x8adD57b8aD6C291BC3E3ffF89F767fcA08e0E7Ab" + } + }, + "USDT": { + "AELF": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "USDT", + "decimals": 6, + "symbol": "USDT", + "address": "", + "issueChainId": 9992731 + }, + "97": { + "name": "USDT Token", + "decimals": 18, + "symbol": "USDT", + "address": "0x3F280eE5876CE8B15081947E0f189E336bb740A5" + }, + "11155111": { + "name": "USDT Token", + "decimals": 6, + "symbol": "USDT", + "address": "0x60eeCc4d19f65B9EaDe628F2711C543eD1cE6679" + }, + "1100": { + "name": "USDT Token", + "decimals": 6, + "symbol": "USDT", + "address": "EQBSKUt9k20Gz8RqG71xloqnzcFHt0MdWg1UnWcyU5Xf9CsU" + } + }, + "ETH": { + "AELF": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "ETH", + "decimals": 8, + "symbol": "ETH", + "address": "", + "issueChainId": 9992731 + }, + "11155111": { + "name": "ETH Token", + "decimals": 18, + "symbol": "ETH", + "address": "0x035900292c309d8beCBCAFb3227238bec0EBa253", + "isNativeToken": true + }, + "84532": { + "name": "ETH Token", + "decimals": 18, + "symbol": "ETH", + "address": "0x13aEe64E227af004De02BA2d651E4e3670e15A83", + "isNativeToken": true + } + }, + "USDC": { + "AELF": { + "name": "USDC", + "decimals": 6, + "symbol": "USDC", + "address": "", + "issueChainId": 9992731 + }, + "tDVV": { + "name": "USDC", + "decimals": 6, + "symbol": "USDC", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "USDC", + "decimals": 6, + "symbol": "USDC", + "address": "", + "issueChainId": 9992731 + }, + "84532": { + "name": "USDC Token", + "decimals": 6, + "symbol": "USDC", + "address": "0xB110e5d737dcfb38CE22E58482F9546D401F0A2D" + } + }, + "AGENT": { + "tDVV": { + "name": "AGENT", + "decimals": 8, + "symbol": "AGENT", + "address": "", + "issueChainId": 1931928 + }, + "tDVW": { + "name": "AGENT", + "decimals": 8, + "symbol": "AGENT", + "address": "", + "issueChainId": 1931928 + }, + "84532": { + "name": "AGENT Token", + "decimals": 18, + "symbol": "AGENT", + "address": "0x048d64c0b70c16b3f99d01b18e613d24d5dfaeab" + } + }, + + "BNB": { + "AELF": { "name": "BNB Token", "decimals": 8, "symbol": "BNB", "address": "", "issueChainId": 9992731 }, + "97": { + "name": "BNB Token", + "decimals": 18, + "symbol": "BNB", + "address": "0x0CBAb7E71f969Bfb3eF5b13542E9087a73244F02", + "isNativeToken": true + }, + "tDVW": { "name": "BNB Token", "decimals": 8, "symbol": "BNB", "address": "", "issueChainId": 9992731 } + }, + "SGR": { + "11155111": { + "name": "Schrodinger", + "decimals": 8, + "symbol": "SGR", + "address": "0x310e7bD119253b9F9F3AC0cD191A1b8b5b1b3b84" + }, + "tDVV": { + "name": "Schrodinger", + "decimals": 8, + "symbol": "SGR-1", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "Schrodinger", + "decimals": 8, + "symbol": "SGR-1", + "address": "", + "issueChainId": 9992731 + } + }, + "WRITE": { + "AELF": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "WRITE Token", "decimals": 8, "symbol": "WRITE", "address": "", "issueChainId": 9992731 } + }, + "WUSD": { + "11155111": { + "name": "WUSD Token", + "decimals": 6, + "symbol": "WUSDvT1", + "address": "0x50A9FC9f46401f2e0AF52835aCD50238431C8ebc" + }, + "tDVV": { + "name": "WUSD Token", + "decimals": 6, + "symbol": "WUSDTEST", + "address": "", + "issueChainId": 9992731 + }, + "tDVW": { + "name": "WUSD Token", + "decimals": 6, + "symbol": "WUSDTEST", + "address": "", + "issueChainId": 9992731 + }, + "AELF": { + "name": "WUSD Token", + "decimals": 6, + "symbol": "WUSDTEST", + "address": "", + "issueChainId": 9992731 + } + }, + "READ": { + "AELF": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "READ Token", "decimals": 8, "symbol": "READ", "address": "", "issueChainId": 9992731 } + }, + "STORAGE": { + "AELF": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "STORAGE Token", "decimals": 8, "symbol": "STORAGE", "address": "", "issueChainId": 9992731 } + }, + "TRAFFIC": { + "AELF": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "TRAFFIC Token", "decimals": 8, "symbol": "TRAFFIC", "address": "", "issueChainId": 9992731 } + }, + "CPU": { + "AELF": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "CPU Token", "decimals": 8, "symbol": "CPU", "address": "", "issueChainId": 9992731 } + }, + "RAM": { + "AELF": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "RAM Token", "decimals": 8, "symbol": "RAM", "address": "", "issueChainId": 9992731 } + }, + "DISK": { + "AELF": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "DISK Token", "decimals": 8, "symbol": "DISK", "address": "", "issueChainId": 9992731 } + }, + "NET": { + "AELF": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 }, + "tDVV": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 }, + "tDVW": { "name": "NET Token", "decimals": 8, "symbol": "NET", "address": "", "issueChainId": 9992731 } + } +} diff --git a/src/constants/theme.ts b/src/constants/theme.ts new file mode 100644 index 00000000..61d398ad --- /dev/null +++ b/src/constants/theme.ts @@ -0,0 +1,4 @@ +export enum Themes { + 'dark', + 'light', +} diff --git a/src/constants/token.ts b/src/constants/token.ts new file mode 100644 index 00000000..c55ef5a3 --- /dev/null +++ b/src/constants/token.ts @@ -0,0 +1,3 @@ +export const API_TOKEN_MAP: Record = { + 'USD₮': 'USDT', +}; diff --git a/src/constants/wallets.ts b/src/constants/wallets.ts new file mode 100644 index 00000000..1c1a9f23 --- /dev/null +++ b/src/constants/wallets.ts @@ -0,0 +1,60 @@ +import { AElfConnectorId, EVMConnectorId, TONConnectorId, TWalletConnectorId, WalletInfo } from 'types'; + +export const SUPPORTED_WALLETS: Record = { + [EVMConnectorId.METAMASK]: { + connectorId: EVMConnectorId.METAMASK, + chainType: 'ERC', + name: 'MetaMask', + iconType: 'MetaMask', + description: 'Easy-to-use browser extension.', + href: null, + }, + [EVMConnectorId.WALLET_CONNECT]: { + connectorId: EVMConnectorId.WALLET_CONNECT, + chainType: 'ERC', + name: 'WalletConnect', + iconType: 'WalletConnect', + description: 'Connect to Trust Wallet, Rainbow Wallet and more...', + href: null, + }, + [EVMConnectorId.COINBASE_WALLET]: { + connectorId: EVMConnectorId.COINBASE_WALLET, + chainType: 'ERC', + name: 'Coinbase Wallet', + iconType: 'coinbaseWallet', + description: 'Connect to Coinbase Wallet', + href: null, + }, + [AElfConnectorId.NIGHTELF]: { + connectorId: AElfConnectorId.NIGHTELF, + chainType: 'ELF', + name: 'NIGHTELF', + iconType: 'nightElf', + description: 'NIGHTELF', + href: null, + }, + [AElfConnectorId.PORTKEY]: { + connectorId: AElfConnectorId.PORTKEY, + chainType: 'ELF', + name: 'Portkey Wallet', + iconType: 'portkeyV2', + description: 'Portkey Wallet', + href: null, + }, + [AElfConnectorId.FAIRY_VAULT]: { + connectorId: AElfConnectorId.FAIRY_VAULT, + chainType: 'ELF', + name: 'FairyVault Wallet', + iconType: 'fairyVault', + description: 'FairyVault Wallet', + href: null, + }, + [TONConnectorId.TON]: { + connectorId: TONConnectorId.TON, + chainType: 'TON', + name: 'Ton connect', + iconType: 'ton-wallet', + description: 'Ton connect', + href: null, + }, +}; diff --git a/src/contexts/__tests__/utils.test.ts b/src/contexts/__tests__/utils.test.ts new file mode 100644 index 00000000..cb2ae224 --- /dev/null +++ b/src/contexts/__tests__/utils.test.ts @@ -0,0 +1,86 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { BasicActions, basicActions, formatLoginInfo } from 'contexts/utils'; +import { describe, expect, test } from 'vitest'; + +describe('basicActions', () => { + test('should return an action with type and payload', () => { + const actionType = 'TEST_ACTION'; + const payload = { key: 'value' }; + const action = basicActions(actionType, payload); + + expect(action).toEqual({ + type: actionType, + payload: payload, + }); + }); + + test('should return an action with only type if payload is undefined', () => { + const actionType = 'TEST_ACTION'; + const action = basicActions(actionType); + + expect(action).toEqual({ + type: actionType, + payload: undefined, + }); + }); +}); + +describe('BasicActions', () => { + test('should define the dispatch function type correctly', () => { + const dispatchMock: BasicActions['dispatch'] = (action) => { + expect(action.type).toBe('TEST_ACTION'); + expect(action.payload).toEqual({ key: 'value' }); + }; + + dispatchMock({ type: 'TEST_ACTION', payload: { key: 'value' } }); + }); +}); + +describe('formatLoginInfo', () => { + test('should format publicKey when it is an object', () => { + const loginInfo = JSON.stringify({ + publicKey: { + x: '1234567890abcdef', + y: '0987654321fedcba', + }, + }); + + const formattedInfo = formatLoginInfo(loginInfo); + + expect(formattedInfo.pubKey).toBe( + '040000000000000000000000000000000000000000000000001234567890abcdef0000000000000000000000000000000000000000000000000987654321fedcba', + ); + }); + + test('should format publicKey when it is a JSON string', () => { + const loginInfo = JSON.stringify({ + publicKey: '{"x":"1234567890abcdef","y":"0987654321fedcba"}', + }); + + const formattedInfo = formatLoginInfo(loginInfo); + + expect(formattedInfo.pubKey).toBe( + '040000000000000000000000000000000000000000000000001234567890abcdef0000000000000000000000000000000000000000000000000987654321fedcba', + ); + }); + + test('should return the original publicKey if it is not an object or JSON string', () => { + const loginInfo = JSON.stringify({ + publicKey: '041234567890abcdef0987654321fedcba', + }); + + const formattedInfo = formatLoginInfo(loginInfo); + + expect(formattedInfo.pubKey).toBe('041234567890abcdef0987654321fedcba'); + }); + + test('should handle invalid JSON string gracefully', () => { + const loginInfo = JSON.stringify({ + publicKey: '{invalid json}', + }); + + const formattedInfo = formatLoginInfo(loginInfo); + + expect(formattedInfo.pubKey).toBe('{invalid json}'); + }); +}); diff --git a/src/contexts/useAElfContract/actions.ts b/src/contexts/useAElfContract/actions.ts new file mode 100644 index 00000000..3cd1f536 --- /dev/null +++ b/src/contexts/useAElfContract/actions.ts @@ -0,0 +1,17 @@ +import { basicActions } from 'contexts/utils'; +import { ContractBasic } from 'utils/contract'; + +export enum AElfContractActions { + setContract = 'SET_CONTRACT', + destroy = 'DESTROY', +} + +export type AElfContractState = { [key: string]: ContractBasic }; + +export const basicAElfContractActions = { + setContract: (contract: { [key: string]: ContractBasic }) => + basicActions(AElfContractActions['setContract'], contract), + aelfContractDestroy: () => basicActions(AElfContractActions['destroy']), +}; + +export const { setContract, aelfContractDestroy } = basicAElfContractActions; diff --git a/src/contexts/useAElfContract/hooks.ts b/src/contexts/useAElfContract/hooks.ts new file mode 100644 index 00000000..388e8c02 --- /dev/null +++ b/src/contexts/useAElfContract/hooks.ts @@ -0,0 +1,8 @@ +import { useAElfContractContext } from '.'; +import { useMemo } from 'react'; +import { ContractBasic } from 'utils/contract'; + +export function useAElfContractByContext(k: string): ContractBasic | undefined { + const [contracts] = useAElfContractContext(); + return useMemo(() => contracts?.[k], [contracts, k]); +} diff --git a/src/contexts/useAElfContract/index.tsx b/src/contexts/useAElfContract/index.tsx new file mode 100644 index 00000000..68245d36 --- /dev/null +++ b/src/contexts/useAElfContract/index.tsx @@ -0,0 +1,36 @@ +import { BasicActions } from 'contexts/utils'; +import React, { createContext, useContext, useMemo, useReducer } from 'react'; +import { AElfContractState, AElfContractActions } from './actions'; + +const INITIAL_STATE: AElfContractState = {}; +const ContractContext = createContext(INITIAL_STATE); + +export function useAElfContractContext(): [AElfContractState, BasicActions] { + return useContext(ContractContext); +} + +//reducer +function reducer(state: AElfContractState, { type, payload }: { type: AElfContractActions; payload: any }) { + switch (type) { + case AElfContractActions.destroy: { + return {}; + } + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +} +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [AElfContractState, BasicActions['dispatch']] = useReducer( + reducer, + INITIAL_STATE, + ); + const actions = useMemo(() => ({ dispatch }), [dispatch]); + return ( + [state, actions], [state, actions])}> + {children} + + ); +} diff --git a/src/contexts/useChain/actions.ts b/src/contexts/useChain/actions.ts new file mode 100644 index 00000000..46965f47 --- /dev/null +++ b/src/contexts/useChain/actions.ts @@ -0,0 +1,34 @@ +import { basicActions } from 'contexts/utils'; +import { EVMConnectorId, WalletType } from 'types'; + +export declare type ChainState = { + userERCChainId?: number; + userELFChainId?: string; + selectERCWallet?: EVMConnectorId; + selectELFWallet?: WalletType; +}; + +export enum ChainActions { + setSelectERCWallet = 'SET_SELECT_ERC_WALLET', + setSelectELFWallet = 'SET_SELECT_ELF_WALLET', + default = 'DEFAULT', + destroy = 'DESTROY', +} + +export const useChainView = { + setSelectERCWallet: (selectERCWallet?: EVMConnectorId) => basicActions(ChainActions['default'], { selectERCWallet }), + setUserERCChainId: (userERCChainId: string) => basicActions(ChainActions['default'], { userERCChainId }), + setUserELFChainId: (userELFChainId: string) => basicActions(ChainActions['default'], { userELFChainId }), + setSelectELFWallet: (selectELFWallet?: WalletType) => + basicActions(ChainActions['setSelectELFWallet'], { selectELFWallet }), + chainProviderDestroy: () => basicActions(ChainActions['destroy']), +}; + +export const { + setSelectERCWallet, + setUserERCChainId, + setUserELFChainId, + chainProviderDestroy, + // setAELFType, + setSelectELFWallet, +} = useChainView; diff --git a/src/contexts/useChain/index.tsx b/src/contexts/useChain/index.tsx new file mode 100644 index 00000000..b6b36b5e --- /dev/null +++ b/src/contexts/useChain/index.tsx @@ -0,0 +1,62 @@ +import storages from 'constants/storages'; +import { BasicActions } from 'contexts/utils'; +import useStorageReducer from 'hooks/useStorageReducer'; +import { createContext, useCallback, useContext, useEffect, useMemo } from 'react'; +import { eventBus } from 'utils/eBridgeEventBus'; +import { ChainActions, ChainState, setUserELFChainId, setUserERCChainId } from './actions'; + +const INITIAL_STATE = { userERCChainId: undefined }; + +const ChainContext = createContext([INITIAL_STATE]); + +export function useChain(): [ChainState, BasicActions] { + return useContext(ChainContext); +} + +export function useChainDispatch() { + const [, { dispatch }] = useChain(); + return useCallback(dispatch, [dispatch]); +} + +const reducer = (state: ChainState, { type, payload }: { type: ChainActions; payload: any }) => { + switch (type) { + case ChainActions.destroy: { + return {}; + } + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +}; + +const options = { key: storages.useChain }; +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [ChainState, BasicActions['dispatch']] = useStorageReducer( + reducer, + INITIAL_STATE, + options, + ); + const Listeners = useMemo(() => { + return [ + { eventName: storages.userERCChainId, listener: (id: string) => dispatch(setUserERCChainId(id)) }, + { eventName: storages.userELFChainId, listener: (id: string) => dispatch(setUserELFChainId(id)) }, + ]; + }, [dispatch]); + + useEffect(() => { + Listeners.forEach(({ eventName, listener }) => { + eventBus.addListener(eventName, listener); + }); + return () => { + Listeners.forEach(({ eventName, listener }) => { + eventBus.removeListener(eventName, listener); + }); + }; + }, [Listeners]); + const actions = useMemo(() => ({ dispatch }), [dispatch]); + return ( + [state, actions], [actions, state])}>{children} + ); +} diff --git a/src/contexts/useModal/actions.ts b/src/contexts/useModal/actions.ts new file mode 100644 index 00000000..4db8136c --- /dev/null +++ b/src/contexts/useModal/actions.ts @@ -0,0 +1,71 @@ +import { basicActions } from 'contexts/utils'; +import type { ChainId, ChainType, WalletType, Web3Type } from 'types'; + +export enum ModalActions { + setWalletModal = 'SET_WALLET_MODAL', + setWalletsModal = 'SET_WALLETS_MODAL', + setAccountModal = 'SET_ACCOUNT_MODAL', + setNetWorkDrawer = 'SET_NETWORK_DRAWER', + destroy = 'DESTROY', + setPortketNotConnectModal = 'SET_PORTKEY_NOT_CONNECT_MODAL', +} + +export type ModalState = { + walletModal?: boolean; + walletsModal?: boolean; + netWorkDrawer?: boolean; + accountModal?: boolean; + accountChainId?: ChainId; + walletChainId?: ChainId; + accountWallet?: Web3Type; + walletWallet?: Web3Type; + accountWalletType?: WalletType; + walletWalletType?: WalletType; + walletChainType?: ChainType; + portketNotConnectModal?: { + visible?: boolean; + chainId?: ChainId; + }; +}; + +export const basicModalView = { + setWalletModal: ( + walletModal: boolean, + options?: { walletWalletType?: WalletType; walletChainType?: ChainType; walletChainId?: ChainId }, + ) => { + let obj: any = { walletModal }; + if (walletModal) { + obj = { ...obj, ...options }; + obj.destroy = true; + } + return basicActions(ModalActions['setWalletModal'], obj); + }, + setWalletsModal: (walletsModal: boolean) => { + const obj: any = { walletsModal }; + if (walletsModal) { + obj.destroy = true; + } + return basicActions(ModalActions['setWalletsModal'], obj); + }, + setAccountModal: (accountModal: boolean, options?: { accountWalletType?: WalletType; accountChainId?: ChainId }) => { + let obj: any = { accountModal }; + if (accountModal) { + obj = { ...obj, ...options }; + obj.destroy = true; + } + return basicActions(ModalActions['setAccountModal'], obj); + }, + setNetWorkDrawer: (netWorkDrawer: boolean) => basicActions(ModalActions['setNetWorkDrawer'], { netWorkDrawer }), + modalDestroy: () => basicActions(ModalActions['destroy']), + setPortketNotConnectModal: (portketNotConnectModal: { visible?: boolean; chainId?: ChainId }) => + basicActions(ModalActions['setPortketNotConnectModal'], { portketNotConnectModal }), +}; + +export const { + setWalletModal, + setWalletsModal, + setAccountModal, + setNetWorkDrawer, + modalDestroy, + setPortketNotConnectModal, +} = basicModalView; diff --git a/src/contexts/useModal/hooks.ts b/src/contexts/useModal/hooks.ts new file mode 100644 index 00000000..e74cbf7b --- /dev/null +++ b/src/contexts/useModal/hooks.ts @@ -0,0 +1,7 @@ +import { useCallback } from 'react'; +import { useModal } from '.'; + +export function useModalDispatch() { + const [, { dispatch }] = useModal(); + return useCallback(dispatch, [dispatch]); +} diff --git a/src/contexts/useModal/index.tsx b/src/contexts/useModal/index.tsx new file mode 100644 index 00000000..ea18a7fb --- /dev/null +++ b/src/contexts/useModal/index.tsx @@ -0,0 +1,113 @@ +import { BasicActions } from 'contexts/utils'; +import { useAElf, usePortkey, useTon, useWeb3 } from 'hooks/web3'; +import React, { createContext, useContext, useEffect, useMemo, useReducer } from 'react'; +import { ModalActions, ModalState, basicModalView } from './actions'; +import { formatPortkeyWallet } from 'contexts/useWallet/utils'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { setSwitchChainInConnectPortkey } from 'contexts/useWallet/actions'; + +const INITIAL_STATE = {}; +const ModalContext = createContext(INITIAL_STATE); + +export function useModal(): [ModalState, BasicActions] { + return useContext(ModalContext); +} + +//reducer +function reducer(state: ModalState, { type, payload }: { type: ModalActions; payload: any }) { + switch (type) { + case ModalActions.destroy: { + return {}; + } + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +} + +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [ModalState, BasicActions['dispatch']] = useReducer(reducer, INITIAL_STATE); + const { accountWalletType, walletWalletType, walletChainId, accountChainId } = state; + const actions = useMemo(() => ({ dispatch }), [dispatch]); + const aelfWallet = useAElf(); + const web3Wallet = useWeb3(); + const portkeyWallet = usePortkey(); + const tonWallet = useTon(); + const { switchChainInConnectPortkey } = useWallet(); + const [walletWallet, accountWallet] = useMemo(() => { + let accountWallet, walletWallet; + switch (accountWalletType) { + case 'ERC': + accountWallet = web3Wallet; + break; + case 'PORTKEY': + accountWallet = formatPortkeyWallet(portkeyWallet, accountChainId as any); + break; + case 'NIGHTELF': + case 'FAIRY_VAULT': + accountWallet = aelfWallet; + break; + case 'TON': + accountWallet = tonWallet; + break; + default: + break; + } + + switch (walletWalletType) { + case 'ERC': + walletWallet = web3Wallet; + break; + case 'PORTKEY': + walletWallet = formatPortkeyWallet(portkeyWallet, walletChainId as any); + break; + case 'NIGHTELF': + case 'FAIRY_VAULT': + walletWallet = aelfWallet; + break; + case 'TON': + walletWallet = tonWallet; + break; + default: + break; + } + + return [walletWallet, accountWallet]; + }, [ + accountChainId, + accountWalletType, + aelfWallet, + portkeyWallet, + tonWallet, + walletChainId, + walletWalletType, + web3Wallet, + ]); + + useEffect(() => { + if (switchChainInConnectPortkey?.status) { + dispatch( + basicModalView.setPortketNotConnectModal({ + visible: true, + chainId: switchChainInConnectPortkey?.chainId, + }), + ); + setSwitchChainInConnectPortkey({ + status: false, + chainId: undefined, + }); + } + }, [switchChainInConnectPortkey]); + + return ( + [{ ...state, walletWallet, accountWallet }, actions], + [state, walletWallet, accountWallet, actions], + )}> + {children} + + ); +} diff --git a/src/contexts/usePools/actions.ts b/src/contexts/usePools/actions.ts new file mode 100644 index 00000000..5993ef92 --- /dev/null +++ b/src/contexts/usePools/actions.ts @@ -0,0 +1,21 @@ +import { basicActions } from 'contexts/utils'; +import { APIPoolItem, TPoolOverview } from 'types/api'; +export declare type PoolsState = { + poolList?: { + totalCount: number; + items: APIPoolItem[]; + }; + poolOverview?: TPoolOverview; +}; +export enum PoolsActions { + destroy = 'DESTROY', + setPoolList = 'SET_POOL_LIST', + setPoolOverview = 'SET_POOL_OVERVIEW', +} + +export const whitelistActions = { + setPoolList: (poolList?: APIPoolItem[]) => basicActions(PoolsActions['setPoolList'], { poolList }), + setPoolOverview: (poolOverview?: TPoolOverview) => basicActions(PoolsActions['setPoolOverview'], { poolOverview }), +}; + +export const { setPoolList, setPoolOverview } = whitelistActions; diff --git a/src/contexts/usePools/hooks.ts b/src/contexts/usePools/hooks.ts new file mode 100644 index 00000000..ef56bd5b --- /dev/null +++ b/src/contexts/usePools/hooks.ts @@ -0,0 +1,7 @@ +import { useCallback } from 'react'; +import { usePools } from '.'; + +export function usePoolsDispatch() { + const [, { dispatch }] = usePools(); + return useCallback(dispatch, [dispatch]); +} diff --git a/src/contexts/usePools/index.tsx b/src/contexts/usePools/index.tsx new file mode 100644 index 00000000..80a70aa3 --- /dev/null +++ b/src/contexts/usePools/index.tsx @@ -0,0 +1,32 @@ +import { BasicActions } from 'contexts/utils'; +import { createContext, useContext, useMemo, useReducer } from 'react'; +import { PoolsActions, PoolsState } from './actions'; + +const INITIAL_STATE = {}; + +const PoolsContext = createContext([INITIAL_STATE]); + +export function usePools(): [PoolsState, BasicActions] { + return useContext(PoolsContext); +} + +const reducer = (state: PoolsState, { type, payload }: { type: PoolsActions; payload: any }) => { + switch (type) { + case PoolsActions.destroy: { + return {}; + } + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +}; + +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [PoolsState, BasicActions['dispatch']] = useReducer(reducer, INITIAL_STATE); + const actions = useMemo(() => ({ dispatch }), [dispatch]); + return ( + [state, actions], [actions, state])}>{children} + ); +} diff --git a/src/contexts/useStore/hooks.ts b/src/contexts/useStore/hooks.ts new file mode 100644 index 00000000..1592cecb --- /dev/null +++ b/src/contexts/useStore/hooks.ts @@ -0,0 +1,7 @@ +import { useMemo } from 'react'; +import { useStore } from '.'; + +export function useMobile() { + const [{ mobile }] = useStore(); + return useMemo(() => !!mobile, [mobile]); +} diff --git a/src/contexts/useStore/index.tsx b/src/contexts/useStore/index.tsx new file mode 100644 index 00000000..68171bc7 --- /dev/null +++ b/src/contexts/useStore/index.tsx @@ -0,0 +1,80 @@ +import { ELFChainConstants, ERCChainConstants } from 'constants/ChainConstants'; +import { ZERO } from 'constants/misc'; +import { useAElf, useWeb3 } from 'hooks/web3'; +import { createContext, useContext, useEffect, useMemo, useReducer, useState } from 'react'; +import { useEffectOnce, useSearchParam } from 'react-use'; +import { isMobileDevices } from 'utils/isMobile'; +import { Web3Type } from 'types'; +import { breakpointMap } from 'constants/media'; +import { useLanguage } from 'i18n'; +import { LOCAL_LANGUAGE_LIST } from 'i18n/config'; +const INITIAL_STATE = {}; +const StoreContext = createContext(INITIAL_STATE); + +const mobileWidth = ZERO.plus(breakpointMap.md); +declare type StoreState = { mobile?: boolean }; +export function useStore(): [StoreState] { + return useContext(StoreContext); +} + +//reducer payload +function reducer(state: any, { type, payload }: any) { + switch (type) { + default: + return Object.assign({}, state, payload); + } +} + +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch] = useReducer(reducer, INITIAL_STATE); + const { chainId } = useWeb3(); + const { chainId: aelfChainId, aelfInstances } = useAElf(); + const [mobile, setMobile] = useState(); + const { changeLanguage } = useLanguage(); + const language = useSearchParam('language'); + useMemo(() => { + if (!chainId) return; + initialized(chainId); + }, [chainId]); + useMemo(() => { + initializedELF(aelfChainId || '', aelfInstances); + }, [aelfChainId, aelfInstances]); + + // isMobile + useEffectOnce(() => { + if (language && LOCAL_LANGUAGE_LIST.includes(language)) changeLanguage(language); + const resize = () => { + setMobile(mobileWidth.gt(window.innerWidth) || isMobileDevices()); + }; + resize(); + window.addEventListener('resize', resize); + return () => { + window.removeEventListener('resize', resize); + }; + }); + + // className + useEffect(() => { + const body = window.document.getElementsByTagName('body')[0]; + if (!body) return; + body.className = 'pc-site'; + const addClassName = [mobile ? 'mobile-site' : 'pc-site']; + body.className = ''; + addClassName.forEach((i) => { + if (!body.className.includes(i)) body.className = (body.className.trim() + ' ' + i).trim(); + }); + }, [mobile]); + + return ( + [{ ...state, mobile }, { dispatch }], [state, mobile])}> + {children} + + ); +} +function initialized(chainId: number | string) { + new ERCChainConstants(chainId); +} + +function initializedELF(chainId: number | string, aelfInstances?: Web3Type['aelfInstances']) { + new ELFChainConstants(chainId, aelfInstances); +} diff --git a/src/contexts/useToken/actions.ts b/src/contexts/useToken/actions.ts new file mode 100644 index 00000000..c1de2336 --- /dev/null +++ b/src/contexts/useToken/actions.ts @@ -0,0 +1,15 @@ +import { basicActions } from 'contexts/utils'; +export declare type TokenState = { + tokenPriceMap?: { [symbol: string]: string | number }; +}; +export enum TokenActions { + destroy = 'DESTROY', + addTokenPrice = 'ADD_TOKEN_PRICE', +} + +export const tokenActions = { + addTokenPrice: (symbol: string, amount: string | number) => + basicActions(TokenActions['addTokenPrice'], { symbol, amount }), +}; + +export const { addTokenPrice } = tokenActions; diff --git a/src/contexts/useToken/hooks.ts b/src/contexts/useToken/hooks.ts new file mode 100644 index 00000000..8f99227d --- /dev/null +++ b/src/contexts/useToken/hooks.ts @@ -0,0 +1,15 @@ +import { useCallback, useMemo } from 'react'; +import { useToken } from '.'; + +export function useTokenDispatch() { + const [, { dispatch }] = useToken(); + return useCallback(dispatch, [dispatch]); +} + +export function useTokenPriceByContext(symbol?: string) { + const [{ tokenPriceMap }] = useToken(); + return useMemo(() => { + if (!tokenPriceMap || !symbol) return '0'; + return tokenPriceMap[symbol]; + }, [symbol, tokenPriceMap]); +} diff --git a/src/contexts/useToken/index.tsx b/src/contexts/useToken/index.tsx new file mode 100644 index 00000000..61abf0f4 --- /dev/null +++ b/src/contexts/useToken/index.tsx @@ -0,0 +1,37 @@ +import { BasicActions } from 'contexts/utils'; +import { createContext, useContext, useMemo, useReducer } from 'react'; +import { TokenActions, TokenState } from './actions'; + +const INITIAL_STATE = {}; + +const TokenContext = createContext([INITIAL_STATE]); + +export function useToken(): [TokenState, BasicActions] { + return useContext(TokenContext); +} + +const reducer = (state: TokenState, { type, payload }: { type: TokenActions; payload: any }) => { + switch (type) { + case TokenActions.destroy: { + return {}; + } + case TokenActions.addTokenPrice: { + const { symbol, amount } = payload; + const tokenPriceMap = { ...state.tokenPriceMap, [symbol]: amount }; + return Object.assign({}, state, { tokenPriceMap }); + } + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +}; + +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [TokenState, BasicActions['dispatch']] = useReducer(reducer, INITIAL_STATE); + const actions = useMemo(() => ({ dispatch }), [dispatch]); + return ( + [state, actions], [actions, state])}>{children} + ); +} diff --git a/src/contexts/useWallet/actions.ts b/src/contexts/useWallet/actions.ts new file mode 100644 index 00000000..f6b1e7a7 --- /dev/null +++ b/src/contexts/useWallet/actions.ts @@ -0,0 +1,52 @@ +import { basicActions } from 'contexts/utils'; +import type { ChainId, ChainType, Web3Type } from 'types'; + +export type Options = { + chainType: ChainType; + chainId?: ChainId; + isPortkey?: boolean; +}; + +export enum WalletActions { + resetFromToWallet = 'RESET_FROM_TO_WALLET', + setFromWallet = 'SET_FROM_WALLET', + setToWallet = 'SET_TO_WALLET', + changeWallet = 'CHANGE_WALLET', + changeEnd = 'CHANGE_END', + destroy = 'DESTROY', + setSwitchChainInConnectPortkey = 'SET_SWITCH_CHAIN_IN_CONNECT_PORTKEY', +} + +export type ModalState = { + fromWallet?: Web3Type; + toWallet?: Web3Type; + fromOptions?: Options; + toOptions?: Options; + changing?: boolean; + isHomogeneous?: boolean; + switchChainInConnectPortkey?: { + status: boolean; + chainId?: ChainId; + }; +}; + +export const basicWalletActions = { + setFromWallet: (options: Options) => basicActions(WalletActions['setFromWallet'], { fromOptions: options }), + resetFromToWallet: () => basicActions(WalletActions['resetFromToWallet']), + setToWallet: (options: Options) => basicActions(WalletActions['setToWallet'], { toOptions: options }), + changeWallet: () => basicActions(WalletActions.changeWallet), + changeEnd: () => basicActions(WalletActions.changeEnd), + web3ProviderDestroy: () => basicActions(WalletActions['destroy']), + setSwitchChainInConnectPortkey: (info: { status: boolean; chainId?: ChainId }) => + basicActions(WalletActions['setSwitchChainInConnectPortkey'], info), +}; + +export const { + resetFromToWallet, + setFromWallet, + setToWallet, + web3ProviderDestroy, + changeWallet, + changeEnd, + setSwitchChainInConnectPortkey, +} = basicWalletActions; diff --git a/src/contexts/useWallet/hooks.ts b/src/contexts/useWallet/hooks.ts new file mode 100644 index 00000000..3440f752 --- /dev/null +++ b/src/contexts/useWallet/hooks.ts @@ -0,0 +1,12 @@ +import { useMemo } from 'react'; +import { useWalletContext } from '.'; + +export function useWallet() { + const [state] = useWalletContext(); + return useMemo(() => state, [state]); +} + +export function useWalletActions() { + const [, actions] = useWalletContext(); + return useMemo(() => actions, [actions]); +} diff --git a/src/contexts/useWallet/index.tsx b/src/contexts/useWallet/index.tsx new file mode 100644 index 00000000..3d2caf94 --- /dev/null +++ b/src/contexts/useWallet/index.tsx @@ -0,0 +1,161 @@ +import { ACTIVE_CHAIN, DEFAULT_MODAL_INITIAL_STATE } from 'constants/index'; +import storages from 'constants/storages'; +import { BasicActions } from 'contexts/utils'; +import useStorageReducer, { StorageOptions } from 'hooks/useStorageReducer'; +import { useAElf, usePortkey, useTon, useWeb3 } from 'hooks/web3'; +import React, { createContext, useContext, useEffect, useMemo } from 'react'; +import { isELFChain } from 'utils/aelfUtils'; +import { WalletActions, ModalState, setToWallet, setFromWallet, setSwitchChainInConnectPortkey } from './actions'; +import { getWalletByOptions, isChange } from './utils'; +import { useChain } from 'contexts/useChain'; +import { usePrevious } from 'react-use'; +import { Web3Type } from 'types'; +import { isPortkeyConnector, isSelectPortkey } from 'utils/portkey'; +import { SupportedELFChainId } from 'constants/chain'; +import { setUserELFChainId } from 'contexts/useChain/actions'; +import { Accounts } from '@portkey/provider-types'; +import { useThrottleCallback } from 'hooks'; + +const INITIAL_STATE = DEFAULT_MODAL_INITIAL_STATE as ModalState; +const ModalContext = createContext(INITIAL_STATE); + +export function useWalletContext(): [ModalState, BasicActions] { + return useContext(ModalContext); +} + +//reducer +function reducer(state: ModalState, { type, payload }: { type: WalletActions; payload: any }) { + switch (type) { + case WalletActions.destroy: { + return {}; + } + case WalletActions.changeWallet: { + const { fromOptions, toOptions } = state; + return Object.assign({}, state, { fromOptions: toOptions, toOptions: fromOptions, changing: true }); + } + case WalletActions.changeEnd: { + return Object.assign({}, state, { changing: false }); + } + case WalletActions.setFromWallet: { + const { toOptions, fromOptions } = state; + const newState: ModalState = { ...payload }; + if (isChange(toOptions, payload.fromOptions)) newState.toOptions = fromOptions; + return Object.assign({}, state, newState); + } + case WalletActions.resetFromToWallet: { + return Object.assign({}, state, INITIAL_STATE); + } + case WalletActions.setToWallet: { + const { toOptions, fromOptions } = state; + const newState: ModalState = { ...payload }; + if (isChange(fromOptions, payload.toOptions)) newState.fromOptions = toOptions; + return Object.assign({}, state, newState); + } + case WalletActions.setSwitchChainInConnectPortkey: { + return Object.assign({}, state, { + switchChainInConnectPortkey: payload, + }); + } + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +} + +const options: StorageOptions = { + key: storages.useWallet + (process.env.NEXT_PUBLIC_APP_ENV || ''), + blacklist: ['switchChainInConnectPortkey'], +}; +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [ModalState, BasicActions['dispatch']] = useStorageReducer( + reducer, + INITIAL_STATE, + options, + ); + + const { fromOptions, toOptions } = state; + + const [{ selectELFWallet }, { dispatch: chainDispatch }] = useChain(); + + const aelfWallet = useAElf(); + const web3Wallet = useWeb3(); + const portkeyWallet = usePortkey(); + const tonWallet = useTon(); + + const [fromWallet, toWallet]: [Web3Type, Web3Type] = useMemo( + () => [ + getWalletByOptions(aelfWallet, web3Wallet, portkeyWallet, tonWallet, fromOptions, selectELFWallet), + getWalletByOptions(aelfWallet, web3Wallet, portkeyWallet, tonWallet, toOptions, selectELFWallet), + ], + [aelfWallet, web3Wallet, portkeyWallet, tonWallet, fromOptions, selectELFWallet, toOptions], + ); + + const portkeyActive = useMemo(() => portkeyWallet.isActive, [portkeyWallet.isActive]); + + const prePortkeyActive = usePrevious(portkeyActive); + + const changeWallet = useThrottleCallback(() => { + let selectWallet; + if (isPortkeyConnector(fromWallet.connectorId) && fromWallet.isActive && !fromWallet.account) { + selectWallet = 'from'; + } else if (isPortkeyConnector(toWallet.connectorId) && toWallet.isActive && !toWallet.account) { + selectWallet = 'to'; + } + + if (selectWallet) { + const isForm = selectWallet === 'from'; + const _wallet = ((isForm ? fromWallet : toWallet) as { accounts: Accounts })?.accounts || {}; + const activeChainId = Object.keys(_wallet)?.[0] as SupportedELFChainId; + if (!ACTIVE_CHAIN[activeChainId]) return; + + if ( + (portkeyActive && isForm && activeChainId !== fromOptions?.chainId) || + (portkeyActive && !isForm && activeChainId !== toOptions?.chainId) + ) { + dispatch( + setSwitchChainInConnectPortkey({ + status: true, + chainId: isForm ? fromOptions?.chainId : toOptions?.chainId, + }), + ); + } + + dispatch((isForm ? setFromWallet : setToWallet)({ chainId: activeChainId, chainType: 'ELF' })); + chainDispatch(setUserELFChainId(activeChainId)); + } + }, [fromWallet, toWallet, portkeyActive, fromOptions?.chainId, toOptions?.chainId, dispatch, chainDispatch]); + + useEffect(() => { + if (isSelectPortkey(selectELFWallet) && portkeyActive && fromOptions?.chainType === toOptions?.chainType) { + dispatch(setToWallet({ chainType: 'ERC' })); + } else { + changeWallet(); + } + }, [ + dispatch, + fromOptions?.chainType, + portkeyActive, + prePortkeyActive, + toOptions?.chainType, + changeWallet, + portkeyWallet, + selectELFWallet, + ]); + + const actions = useMemo(() => ({ dispatch }), [dispatch]); + const isHomogeneous = useMemo( + () => isELFChain(fromWallet?.chainId) && isELFChain(toWallet?.chainId), + [fromWallet?.chainId, toWallet?.chainId], + ); + return ( + [{ ...state, fromWallet, toWallet, isHomogeneous }, actions], + [state, fromWallet, toWallet, isHomogeneous, actions], + )}> + {children} + + ); +} diff --git a/src/contexts/useWallet/utils.ts b/src/contexts/useWallet/utils.ts new file mode 100644 index 00000000..ca6fd0be --- /dev/null +++ b/src/contexts/useWallet/utils.ts @@ -0,0 +1,54 @@ +import { WalletType, Web3Type } from 'types'; +import { Options } from './actions'; +import { Accounts, ChainId } from '@portkey/provider-types'; +import { getELFAddress } from 'utils/aelfUtils'; +import { isSelectPortkey } from 'utils/portkey'; + +export function formatPortkeyWallet(portkeyWallet: Web3Type, chainId: ChainId) { + const _accounts = portkeyWallet?.accounts as Accounts; + const chainAccounts = portkeyWallet.isActive ? _accounts?.[chainId as ChainId] : undefined; + return { + ...portkeyWallet, + chainId, + account: getELFAddress(chainAccounts?.[0]), + }; +} + +export function getWalletByOptions( + aelfWallet: Web3Type, + web3Wallet: Web3Type, + portkeyWallet: Web3Type, + tonWallet: Web3Type, + options?: Options, + selectELFWallet?: WalletType, +) { + const { chainType, chainId } = options || {}; + let wallet: any; + + if (chainType === 'ELF') { + if (isSelectPortkey(selectELFWallet)) { + wallet = formatPortkeyWallet(portkeyWallet, chainId as ChainId); + } else { + wallet = { ...aelfWallet, chainId }; + } + } else if (chainType === 'TON') { + wallet = tonWallet; + } else { + wallet = web3Wallet; + } + return wallet; +} + +export function isChange(stateOptions?: Options, payloadOptions?: Options) { + const { chainType: stateType, chainId: stateChainId } = stateOptions || {}; + const { chainType, chainId, isPortkey } = payloadOptions || {}; + + const isExternalState = stateType !== 'ELF'; + const isExternalPayload = chainType !== 'ELF'; + + return ( + (isPortkey && stateType === 'ELF' && chainType === 'ELF') || + ((isExternalState || isExternalPayload) && isExternalState === isExternalPayload) || + (stateType === 'ELF' && chainType === 'ELF' && stateChainId === chainId) + ); +} diff --git a/src/contexts/useWhitelist/actions.ts b/src/contexts/useWhitelist/actions.ts new file mode 100644 index 00000000..f1471f7f --- /dev/null +++ b/src/contexts/useWhitelist/actions.ts @@ -0,0 +1,26 @@ +import { basicActions } from 'contexts/utils'; +import { ChainId, TokenInfo } from 'types'; +export type WhitelistItem = { + [key in ChainId]?: TokenInfo; +}; + +export type WhitelistMap = { [key: string]: WhitelistItem | undefined }; +export declare type WhitelistState = { + whitelistMap?: WhitelistMap; + defaultWhitelistMap: WhitelistMap; +}; +export enum WhitelistActions { + addWhitelist = 'ADD_WHITE_LIST', + setDefaultWhitelist = 'SET_DEFAULT_WHITE_LIST', + default = 'DEFAULT', + destroy = 'DESTROY', +} + +export const whitelistActions = { + addWhitelist: (token?: WhitelistMap) => basicActions(WhitelistActions['addWhitelist'], { token }), + setDefaultWhitelist: (defaultWhitelistMap?: WhitelistMap) => + basicActions(WhitelistActions['setDefaultWhitelist'], { defaultWhitelistMap }), + whitelistProviderDestroy: () => basicActions(WhitelistActions['destroy']), +}; + +export const { addWhitelist, whitelistProviderDestroy, setDefaultWhitelist } = whitelistActions; diff --git a/src/contexts/useWhitelist/hooks.ts b/src/contexts/useWhitelist/hooks.ts new file mode 100644 index 00000000..8a758b98 --- /dev/null +++ b/src/contexts/useWhitelist/hooks.ts @@ -0,0 +1,7 @@ +import { useMemo } from 'react'; +import { useWhitelist } from '.'; + +export function useWhitelistActions() { + const [, actions] = useWhitelist(); + return useMemo(() => actions, [actions]); +} diff --git a/src/contexts/useWhitelist/index.tsx b/src/contexts/useWhitelist/index.tsx new file mode 100644 index 00000000..374c24a2 --- /dev/null +++ b/src/contexts/useWhitelist/index.tsx @@ -0,0 +1,76 @@ +import storages from 'constants/storages'; +import { BasicActions } from 'contexts/utils'; +import useStorageReducer from 'hooks/useStorageReducer'; +import { createContext, useCallback, useContext, useMemo } from 'react'; +import { setDefaultWhitelist, WhitelistActions, WhitelistItem, WhitelistState } from './actions'; +import { getTokenWhiteList } from 'utils/api/common'; +import { DefaultWhitelistMap } from 'constants/index'; +import useInterval from 'hooks/useInterval'; + +const INITIAL_STATE = { defaultWhitelistMap: DefaultWhitelistMap }; + +const WhitelistContext = createContext([INITIAL_STATE]); + +export function useWhitelist(): [WhitelistState, BasicActions] { + return useContext(WhitelistContext); +} + +const reducer = (state: WhitelistState, { type, payload }: { type: WhitelistActions; payload: any }) => { + switch (type) { + case WhitelistActions.destroy: { + return {}; + } + case WhitelistActions.addWhitelist: { + const { token } = payload; + const { whitelistMap } = state; + let tmpMap = {}; + Object.entries(token).map(([key, value]) => { + const old = whitelistMap?.[key]; + if (old) { + tmpMap = Object.assign(tmpMap, { + [key]: { + ...old, + ...(value as WhitelistItem), + }, + }); + } else { + tmpMap = Object.assign(tmpMap, { + [key]: value, + }); + } + }); + return Object.assign({}, state, { whitelistMap: Object.assign({}, whitelistMap, tmpMap) }); + } + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +}; + +const options = { key: storages.useWhitelist }; +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [WhitelistState, BasicActions['dispatch']] = useStorageReducer( + reducer, + INITIAL_STATE, + options, + ); + const actions = useMemo(() => ({ dispatch }), [dispatch]); + + const onGetTokenWhiteList = useCallback(async () => { + try { + const data = await getTokenWhiteList(); + dispatch(setDefaultWhitelist(data)); + } catch (error) { + console.debug(error, '===onGetTokenWhiteList'); + } + }, [dispatch]); + useInterval(onGetTokenWhiteList, [onGetTokenWhiteList], 30000); + + return ( + [state, actions], [actions, state])}> + {children} + + ); +} diff --git a/src/contexts/utils.ts b/src/contexts/utils.ts new file mode 100644 index 00000000..fc0d23f4 --- /dev/null +++ b/src/contexts/utils.ts @@ -0,0 +1,33 @@ +export function basicActions(type: T, payload?: any) { + return { + type, + payload, + }; +} + +export type BasicActions = { + dispatch: (actions: { type: T; payload: any }) => void; +}; + +const formatPubKey = (publicKey: any) => { + const toStr = (x: string, y: string) => '04' + x?.padStart(64, '0') + y?.padStart(64, '0'); + + if (typeof publicKey === 'object') { + const { x, y } = publicKey ?? {}; + return toStr(x, y); + } else { + try { + const { x, y } = JSON.parse(publicKey) ?? {}; + return toStr(x, y); + } catch (e) { + return publicKey; + } + } +}; + +export const formatLoginInfo = (loginInfo: any) => { + const detail = JSON.parse(loginInfo); + const { publicKey } = detail; + const pubKey = formatPubKey(publicKey); + return { ...detail, pubKey }; +}; diff --git a/src/contracts/__tests__/elf.test.ts b/src/contracts/__tests__/elf.test.ts new file mode 100644 index 00000000..7f6407d6 --- /dev/null +++ b/src/contracts/__tests__/elf.test.ts @@ -0,0 +1,187 @@ +import { describe, expect, test, vi, beforeEach } from 'vitest'; +import { REQ_CODE } from 'constants/misc'; +import CommonMessage from 'components/CommonMessage'; +import { ContractBasic } from 'utils/contract'; +import { checkELFApprove, checkElfChainAllowanceAndApprove, getELFChainBalance } from 'contracts/elf'; +import { isUserDenied } from 'utils/provider'; + +// Mock dependencies +vi.mock('components/CommonMessage'); +vi.mock('utils/calculate'); +vi.mock('utils/provider'); + +const mockTokenContract = { + callViewMethod: vi.fn(), + callSendMethod: vi.fn(), +} as unknown as ContractBasic; + +describe('getELFChainBalance', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + test('should return balance when exists', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValue({ balance: 100 }); + + const result = await getELFChainBalance(mockTokenContract, 'ELF', '0x123'); + + expect(result).toBe(100); + expect(mockTokenContract.callViewMethod).toHaveBeenCalledWith('GetBalance', { + symbol: 'ELF', + owner: '0x123', + }); + }); + + test('should return amount when balance not exists', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValue({ amount: 50 }); + + const result = await getELFChainBalance(mockTokenContract, 'ELF', '0x123'); + + expect(result).toBe(50); + }); + + test('should return 0 when no balance/amount', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValue({}); + + const result = await getELFChainBalance(mockTokenContract, 'ELF', '0x123'); + + expect(result).toBe(0); + }); +}); + +describe('checkElfChainAllowanceAndApprove', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + const baseParams = { + tokenContract: mockTokenContract, + approveTargetAddress: '0x456', + account: '0x123', + symbol: 'ELF', + }; + + test('should return allowance error', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ error: 'ERROR' }); + + const result = await checkElfChainAllowanceAndApprove(baseParams); + + expect(result).toEqual({ error: 'ERROR' }); + }); + + test('should return true when allowance is sufficient', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ allowance: 1000 }); + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ decimals: 8 }); + + const result = await checkElfChainAllowanceAndApprove({ + ...baseParams, + pivotBalance: 1, + }); + + expect(result).toBe(true); + }); + + test('should return true when allowance is sufficient and callViewMethod return amount', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ amount: 100 }); + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ decimals: 8 }); + + const result = await checkElfChainAllowanceAndApprove({ + ...baseParams, + pivotBalance: 1, + }); + + expect(result).toBe(true); + }); + + test('should call approve when allowance insufficient and callViewMethod return undefined', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({}); + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({}); + + const result = await checkElfChainAllowanceAndApprove({ + ...baseParams, + pivotBalance: 1, + }); + + expect(result).toBe(true); + }); + + test('should call approve when allowance insufficient', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ allowance: 100 }); + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ decimals: 8 }); + vi.mocked(mockTokenContract.callSendMethod).mockResolvedValue({ transactionId: '0x789' }); + + const result = await checkElfChainAllowanceAndApprove({ + ...baseParams, + contractUseAmount: 200, + }); + + expect(mockTokenContract.callSendMethod).toHaveBeenCalledWith('approve', '0x123', ['0x456', 'ELF', '200']); + expect(result).toEqual({ transactionId: '0x789' }); + }); + + test('should catch error when approve failed', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ allowance: 100 }); + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({ decimals: 8 }); + const error = { error: 'Approve failed' }; + vi.mocked(mockTokenContract.callSendMethod).mockResolvedValue(error); + + const result = await checkElfChainAllowanceAndApprove({ + ...baseParams, + contractUseAmount: 200, + }); + + expect(mockTokenContract.callSendMethod).toHaveBeenCalledWith('approve', '0x123', ['0x456', 'ELF', '200']); + expect(result).toEqual(error); + }); +}); + +describe('checkELFApprove', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + test('should return Success when approved', async () => { + // Mock + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValue({ allowance: 1000 }); + + const result = await checkELFApprove('ELF', '0x123', '0x456', mockTokenContract); + + expect(result).toBe(REQ_CODE.Success); + }); + + test('should handle user denied error', async () => { + // Mock + const error = { error: { message: 'User rejected request' } }; + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({}); + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({}); + vi.mocked(mockTokenContract.callSendMethod).mockResolvedValue(error); + vi.mocked(isUserDenied).mockReturnValue(true); + + const result = await checkELFApprove('ELF', '0x123', '0x456', mockTokenContract, 100); + + expect(CommonMessage.error).toHaveBeenCalledTimes(2); + expect(result).toBe(REQ_CODE.UserDenied); + }); + + test('should handle general error', async () => { + // Mock + const error = { error: { message: 'Other error' } }; + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({}); + vi.mocked(mockTokenContract.callViewMethod).mockResolvedValueOnce({}); + vi.mocked(mockTokenContract.callSendMethod).mockResolvedValue(error); + vi.mocked(isUserDenied).mockReturnValue(false); + + const result = await checkELFApprove('ELF', '0x123', '0x456', mockTokenContract, 100); + + expect(result).toBe(REQ_CODE.Fail); + }); +}); diff --git a/src/contracts/__tests__/ethereum.test.ts b/src/contracts/__tests__/ethereum.test.ts new file mode 100644 index 00000000..337c4f6c --- /dev/null +++ b/src/contracts/__tests__/ethereum.test.ts @@ -0,0 +1,484 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { describe, expect, test, vi, beforeEach } from 'vitest'; +import Web3 from 'web3'; +import { REQ_CODE, MaxUint256 } from 'constants/misc'; +import { + checkErcApprove, + checkAllowanceAndApprove, + getContract, + callERC20ViewMethod, + createToken, + createOfficialToken, + getAllowance, + getBalance, + getTotalSupply, +} from 'contracts/ethereum'; +import CommonMessage from 'components/CommonMessage'; +import { ContractBasic } from 'utils/contract'; +import { isUserDenied } from 'utils/provider'; + +// Mock dependencies +vi.mock('web3', async () => { + const Web3Mock: any = vi.fn().mockImplementation(() => ({ + eth: { + Contract: vi.fn(), + }, + providers: { + HttpProvider: vi.fn().mockImplementation((url: string) => ({ + url, + options: { keepAlive: true, withCredentials: false, timeout: 20000 }, + })), + }, + })); + + Web3Mock.eth = { + Contract: vi.fn(), + }; + Web3Mock.providers = { + HttpProvider: vi.fn().mockImplementation((url: string) => ({ + url, + options: { keepAlive: true, withCredentials: false, timeout: 20000 }, + })), + }; + + return { + default: Web3Mock, + }; +}); +vi.mock('web3-core'); + +// Define the controllable simulation object at the top of the file +const mockBehavior = { + callViewMethod: vi.fn().mockResolvedValue({ error: { details: 'User denied transaction' } }), + callSendMethod: vi.fn().mockResolvedValue({}), +}; + +vi.mock('utils/contract', () => { + class MockContractBasic { + callViewMethod = mockBehavior.callViewMethod; + callSendMethod = mockBehavior.callSendMethod; + } + return { ContractBasic: MockContractBasic }; +}); + +// vi.mock('utils/contract', () => { +// // Mock ContractBasic +// const error = { error: { details: 'User denied transaction' } }; +// class MockContractBasic { +// callSendMethod = vi.fn().mockResolvedValue({}); +// callViewMethod = vi.fn().mockResolvedValueOnce(error); +// } + +// return { +// ContractBasic: MockContractBasic, +// }; +// }); +vi.mock('utils/provider'); +vi.mock('utils/error'); +vi.mock('components/CommonMessage'); + +const mockProvider = {} as any; +const mockContract = { + methods: { + allowance: vi.fn().mockReturnThis(), + balanceOf: vi.fn().mockReturnThis(), + call: vi.fn(), + }, +} as any; + +describe('checkErcApprove', () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + test('should handle user denied error properly', async () => { + // Mock console.log + vi.spyOn(console, 'error').mockImplementation(() => {}); + + // Reassignment mockBehavior.callViewMethod + mockBehavior.callViewMethod.mockResolvedValue({ error: { details: 'User denied transaction' } }); + mockBehavior.callSendMethod.mockResolvedValue({}); + + vi.mocked(isUserDenied).mockReturnValue(true); + + const result = await checkErcApprove(1, '0x123', '0x456', '0x789', '0xabc'); + expect(CommonMessage.error).toHaveBeenCalledTimes(2); + expect(result).toBe(REQ_CODE.UserDenied); + }); + + test('should handle Fail error properly', async () => { + // Mock console.log + vi.spyOn(console, 'error').mockImplementation(() => {}); + + // Reassignment mockBehavior.callViewMethod + mockBehavior.callViewMethod.mockResolvedValue({ error: { details: 'User denied transaction' } }); + mockBehavior.callSendMethod.mockResolvedValue({}); + + vi.mocked(isUserDenied).mockReturnValue(false); + + const result = await checkErcApprove(1, '0x123', '0x456', '0x789', '0xabc'); + expect(CommonMessage.error).toHaveBeenCalledTimes(2); + expect(result).toBe(REQ_CODE.Fail); + }); + + test('should return success if approve is sufficient', async () => { + // Mock console.log + vi.spyOn(console, 'error').mockImplementation(() => {}); + + // Reassignment mockBehavior.callViewMethod + mockBehavior.callViewMethod.mockResolvedValue({}); + mockBehavior.callSendMethod.mockResolvedValue({}); + + vi.mocked(isUserDenied).mockReturnValue(false); + + const result = await checkErcApprove(1, '0x123', '0x456', '0x789', '0xabc'); + + expect(result).toBe(REQ_CODE.Success); + }); +}); + +describe('getAllowance', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + const masterChefContract = { options: { address: '' } } as any; + + it('should return the allowance', async () => { + const allowance = '100'; + const lpContract = { + methods: { + allowance: () => { + return { call: vi.fn().mockResolvedValue(allowance) }; + }, + }, + } as any; + + const account = '0x123'; + + const result = await getAllowance(lpContract, masterChefContract, account); + + expect(result).toBe(allowance); + }); + + it('should return "0" if an error occurs', async () => { + const lpContract = { + methods: { + allowance: () => { + return { call: vi.fn().mockRejectedValue(new Error('error')) }; + }, + }, + } as any; + + const account = '0x123'; + + const result = await getAllowance(lpContract, masterChefContract, account); + + expect(result).toBe('0'); + }); +}); + +describe('ERC20 Functions', () => { + beforeEach(() => { + vi.clearAllMocks(); + (Web3 as any).mockImplementation(() => ({ + eth: { + Contract: vi.fn().mockReturnValue(mockContract), + }, + })); + }); + + describe('getContract', () => { + test('should create contract instance with correct parameters', () => { + const contract = getContract(mockProvider, '0x123'); + expect(Web3).toHaveBeenCalledWith(mockProvider); + expect(contract.methods).toBeDefined(); + }); + }); + + describe('callERC20ViewMethod', () => { + test('should call method with parameters when paramsOption exists', async () => { + mockContract.methods.testMethod = vi.fn().mockReturnValueOnce({ call: vi.fn().mockResolvedValue('success') }); + const result = await callERC20ViewMethod('testMethod', mockProvider, '0x123', ['param1']); + expect(result).toBe('success'); + }); + + test('should return 0 on error', async () => { + mockContract.methods.testMethod = vi.fn().mockReturnValueOnce({ call: vi.fn().mockRejectedValue(new Error()) }); + const result = await callERC20ViewMethod('testMethod', mockProvider, '0x123'); + expect(result).toBe('0'); + }); + }); + + describe('getBalance', () => { + test('should call method with parameters when paramsOption exists', async () => { + mockContract.methods.balanceOf = vi.fn().mockReturnValueOnce({ call: vi.fn().mockResolvedValue(123) }); + const result = await getBalance(mockProvider, '0x123', 'mockUserAddress'); + expect(result).toBe(123); + }); + + test('should return 0 on error', async () => { + mockContract.methods.balanceOf = vi.fn().mockReturnValueOnce({ call: vi.fn().mockRejectedValue(new Error()) }); + const result = await getBalance(mockProvider, '0x123', 'mockUserAddress'); + expect(result).toBe('0'); + }); + }); + + describe('getTotalSupply', () => { + test('should call method with parameters when paramsOption exists', async () => { + mockContract.methods.totalSupply = vi.fn().mockReturnValueOnce({ call: vi.fn().mockResolvedValue(123) }); + const result = await getTotalSupply(mockProvider, '0x123'); + expect(result).toBe(123); + }); + + test('should return 0 on error', async () => { + mockContract.methods.totalSupply = vi.fn().mockReturnValueOnce({ call: vi.fn().mockRejectedValue(new Error()) }); + const result = await getTotalSupply(mockProvider, '0x123'); + expect(result).toBe('0'); + }); + }); + + describe('checkAllowanceAndApprove', () => { + const mockERC20Contract = { + callViewMethod: vi.fn(), + callSendMethod: vi.fn(), + } as unknown as ContractBasic; + + test('should return error when allowance check fails', async () => { + vi.mocked(mockERC20Contract.callViewMethod).mockResolvedValueOnce({ error: 'INSUFFICIENT_BALANCE' }); + const result = await checkAllowanceAndApprove({ + erc20Contract: mockERC20Contract, + approveTargetAddress: '0x456', + account: '0x123', + }); + expect(result.error).toBe('INSUFFICIENT_BALANCE'); + }); + + test('should return error when allowance check fails', async () => { + vi.mocked(mockERC20Contract.callViewMethod).mockResolvedValueOnce('100').mockResolvedValueOnce(18); + vi.mocked(mockERC20Contract.callSendMethod).mockResolvedValue({ error: 'Failed to approve' }); + + const result = await checkAllowanceAndApprove({ + erc20Contract: mockERC20Contract, + approveTargetAddress: '0x456', + account: '0x123', + }); + expect(result.error).toBe('Failed to approve'); + }); + + test('should trigger approval when allowance is insufficient', async () => { + vi.mocked(mockERC20Contract.callViewMethod).mockResolvedValueOnce('100').mockResolvedValueOnce(18); + vi.mocked(mockERC20Contract.callSendMethod).mockResolvedValue({ transactionHash: '0x789' }); + + const result = await checkAllowanceAndApprove({ + erc20Contract: mockERC20Contract, + approveTargetAddress: '0x456', + account: '0x123', + }); + + expect(mockERC20Contract.callSendMethod).toHaveBeenCalledWith('approve', '0x123', ['0x456', MaxUint256]); + expect(result.transactionHash).toBe('0x789'); + }); + + test('should return true when allowance is insufficient', async () => { + vi.mocked(mockERC20Contract.callViewMethod).mockResolvedValueOnce('100').mockResolvedValueOnce(18); + vi.mocked(mockERC20Contract.callSendMethod).mockResolvedValue({ transactionHash: '0x789' }); + + const result = await checkAllowanceAndApprove({ + erc20Contract: mockERC20Contract, + approveTargetAddress: '0x456', + account: '0x123', + pivotBalance: 1, + contractUseAmount: 1, + }); + + expect(result).toBe(true); + }); + }); + + describe('checkErcApprove', () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + test('should handle user denied error properly', async () => { + // Mock console.log + vi.spyOn(console, 'error').mockImplementation(() => {}); + + // Reassignment mockBehavior.callViewMethod + mockBehavior.callViewMethod.mockResolvedValue({ error: { details: 'User denied transaction' } }); + mockBehavior.callSendMethod.mockResolvedValue({}); + + vi.mocked(isUserDenied).mockReturnValue(true); + + const result = await checkErcApprove(1, '0x123', '0x456', '0x789', '0xabc'); + expect(CommonMessage.error).toHaveBeenCalledTimes(2); + expect(result).toBe(REQ_CODE.UserDenied); + }); + + test('should handle Fail error properly', async () => { + // Mock console.log + vi.spyOn(console, 'error').mockImplementation(() => {}); + + // Reassignment mockBehavior.callViewMethod + mockBehavior.callViewMethod.mockResolvedValue({ error: { details: 'User denied transaction' } }); + mockBehavior.callSendMethod.mockResolvedValue({}); + + ContractBasic.prototype.callViewMethod = vi.fn().mockResolvedValue({}); + + vi.mocked(isUserDenied).mockReturnValue(false); + + const result = await checkErcApprove(1, '0x123', '0x456', '0x789', '0xabc'); + expect(CommonMessage.error).toHaveBeenCalledTimes(2); + expect(result).toBe(REQ_CODE.Fail); + }); + + test('should return success if approve is sufficient', async () => { + // Mock console.log + vi.spyOn(console, 'error').mockImplementation(() => {}); + + // Reassignment mockBehavior.callViewMethod + mockBehavior.callViewMethod.mockResolvedValue({}); + mockBehavior.callSendMethod.mockResolvedValue({}); + + vi.mocked(isUserDenied).mockReturnValue(false); + + const result = await checkErcApprove(1, '0x123', '0x456', '0x789', '0xabc'); + + expect(result).toBe(REQ_CODE.Success); + }); + }); + + describe('createToken', () => { + it('should create a token and return the transaction hash', async () => { + const transactionHash = '0xabc'; + const mockCreateContract = { + callSendMethod: vi.fn().mockResolvedValue({ transactionHash }), + } as unknown as ContractBasic; + const createTokenContract = mockCreateContract; + const account = '0x123'; + const name = 'Test Token'; + const symbol = 'TTK'; + const initialSupply = '1000'; + + const result = await createToken({ + createTokenContract, + account, + name, + symbol, + initialSupply, + }); + expect(mockCreateContract.callSendMethod).toHaveBeenCalledWith( + 'createToken', + account, + [name, symbol, initialSupply], + { + onMethod: 'transactionHash', + }, + ); + expect(result).toEqual({ transactionHash }); + }); + + it('should return the error if an error occurs', async () => { + const error = 'Create token error'; + const mockCreateContract = { + callSendMethod: vi.fn().mockRejectedValue(new Error(error)), + } as unknown as ContractBasic; + + const createTokenContract = mockCreateContract; + const account = '0x123'; + const name = 'Test Token'; + const symbol = 'TTK'; + const initialSupply = '1000'; + + await expect( + createToken({ + createTokenContract, + account, + name, + symbol, + initialSupply, + }), + ).rejects.toThrow(Error(error)); + expect(mockCreateContract.callSendMethod).toHaveBeenCalledWith( + 'createToken', + account, + [name, symbol, initialSupply], + { + onMethod: 'transactionHash', + }, + ); + }); + }); + + describe('createOfficialToken', () => { + it('should create a token and return the transaction hash', async () => { + const transactionHash = '0xabc'; + const mockCreateContract = { + callSendMethod: vi.fn().mockResolvedValue({ transactionHash }), + } as unknown as ContractBasic; + const createTokenContract = mockCreateContract; + const account = '0x123'; + const name = 'Test Token'; + const symbol = 'TTK'; + const initialSupply = '1000'; + const officialAddress = 'mockOfficialAddress'; + const mintToAddress = 'mockMintToAddress'; + + const result = await createOfficialToken({ + createTokenContract, + account, + name, + symbol, + initialSupply, + officialAddress, + mintToAddress, + }); + expect(mockCreateContract.callSendMethod).toHaveBeenCalledWith( + 'createOfficialToken', + account, + [name, symbol, initialSupply, officialAddress, mintToAddress], + { + onMethod: 'transactionHash', + }, + ); + expect(result).toEqual({ transactionHash }); + }); + + it('should return the error if an error occurs', async () => { + const error = 'Create token error'; + const mockCreateContract = { + callSendMethod: vi.fn().mockRejectedValue(new Error(error)), + } as unknown as ContractBasic; + + const createTokenContract = mockCreateContract; + const account = '0x123'; + const name = 'Test Token'; + const symbol = 'TTK'; + const initialSupply = '1000'; + + const officialAddress = 'mockOfficialAddress'; + const mintToAddress = 'mockMintToAddress'; + + await expect( + createOfficialToken({ + createTokenContract, + account, + name, + symbol, + initialSupply, + officialAddress, + mintToAddress, + }), + ).rejects.toThrow(Error(error)); + expect(mockCreateContract.callSendMethod).toHaveBeenCalledWith( + 'createOfficialToken', + account, + [name, symbol, initialSupply, officialAddress, mintToAddress], + { + onMethod: 'transactionHash', + }, + ); + }); + }); +}); diff --git a/src/contracts/__tests__/index.test.ts b/src/contracts/__tests__/index.test.ts new file mode 100644 index 00000000..c4e759eb --- /dev/null +++ b/src/contracts/__tests__/index.test.ts @@ -0,0 +1,96 @@ +import { checkApprove } from 'contracts/index'; +import { checkErcApprove } from 'contracts/ethereum'; +import { checkELFApprove } from 'contracts/elf'; +import { ContractBasic } from 'utils/contract'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// Mock external dependencies +vi.mock('contracts/ethereum', () => ({ + checkErcApprove: vi.fn().mockResolvedValue(false), +})); + +vi.mock('contracts/elf', () => ({ + checkELFApprove: vi.fn().mockResolvedValue(true), +})); + +const mockTokenContract = { + callSendMethod: vi.fn(), +} as unknown as ContractBasic; + +describe('checkApprove', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('When tokenContract exists', () => { + it('checkELFApprove should be called and return the correct value', async () => { + const result = await checkApprove( + 1, + 'ELF_TOKEN_ADDRESS', + 'ACCOUNT_ADDRESS', + 'TARGET_ADDRESS', + '100', + '500', + mockTokenContract, + ); + + expect(checkELFApprove).toHaveBeenCalledWith( + 'ELF_TOKEN_ADDRESS', + 'ACCOUNT_ADDRESS', + 'TARGET_ADDRESS', + mockTokenContract, + '100', + '500', + ); + expect(result).toBe(true); + }); + + it('optional parameters should be handled by default', async () => { + await checkApprove( + 1, + 'ELF_TOKEN_ADDRESS', + 'ACCOUNT_ADDRESS', + 'TARGET_ADDRESS', + undefined, + undefined, + mockTokenContract, + ); + + expect(checkELFApprove).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.anything(), + expect.anything(), + undefined, + undefined, + ); + }); + }); + + describe('When tokenContract does not exist', () => { + it('checkErcApprove should be called and return the correct value', async () => { + const result = await checkApprove(1, 'ERC20_TOKEN_ADDRESS', 'ACCOUNT_ADDRESS', 'TARGET_ADDRESS', '200'); + + expect(checkErcApprove).toHaveBeenCalledWith( + 1, + 'ERC20_TOKEN_ADDRESS', + 'ACCOUNT_ADDRESS', + 'TARGET_ADDRESS', + '200', + undefined, + ); + expect(result).toBe(false); + }); + }); + + describe('Exception handling', () => { + it('should catch exceptions from checkELFApprove and return an error object', async () => { + // Mock checkELFApprove to throw an error + (checkELFApprove as ReturnType).mockRejectedValue(new Error('approve error')); + + await expect( + checkApprove(1, 'ELF_TOKEN_ADDRESS', 'ACCOUNT_ADDRESS', 'TARGET_ADDRESS', '100', '500', mockTokenContract), + ).rejects.toThrow(Error('approve error')); + }); + }); +}); diff --git a/src/contracts/elf.ts b/src/contracts/elf.ts new file mode 100644 index 00000000..c73dbbae --- /dev/null +++ b/src/contracts/elf.ts @@ -0,0 +1,79 @@ +import BigNumber from 'bignumber.js'; +import { ContractBasic } from '../utils/contract'; +import { REQ_CODE } from 'constants/misc'; +import { timesDecimals } from 'utils/calculate'; +import { isUserDenied } from 'utils/provider'; +import CommonMessage from 'components/CommonMessage'; +// elf +export const getELFChainBalance = async (tokenContract: ContractBasic, symbol: string, account: string) => { + const balance = await tokenContract.callViewMethod('GetBalance', { + symbol: symbol, + owner: account, + }); + return balance?.balance ?? balance?.amount ?? 0; +}; + +export const checkElfChainAllowanceAndApprove = async ({ + tokenContract, + approveTargetAddress, + account, + contractUseAmount, + pivotBalance, + symbol, +}: { + tokenContract: ContractBasic; + approveTargetAddress: string; + account: string; + contractUseAmount?: string | number; + pivotBalance?: string | number; + symbol: string; +}): Promise => { + const [allowance, info] = await Promise.all([ + tokenContract.callViewMethod('GetAllowance', [symbol, account, approveTargetAddress]), + tokenContract.callViewMethod('GetTokenInfo', [symbol]), + ]); + if (allowance.error) { + return allowance; + } + const allowanceBN = new BigNumber(allowance.allowance ?? allowance.amount ?? 0); + const pivotBalanceBN = contractUseAmount + ? new BigNumber(contractUseAmount) + : timesDecimals(pivotBalance, info.decimals ?? 8); + if (allowanceBN.lt(pivotBalanceBN)) { + const approveResult = await tokenContract.callSendMethod('approve', account, [ + approveTargetAddress, + symbol, + pivotBalanceBN.toFixed(0), + ]); + if (approveResult.error) { + return approveResult; + } else { + return approveResult; + } + } + return true; +}; +export const checkELFApprove = async ( + symbol: string, + account: string, + approveTargetAddress: string, + tokenContract: ContractBasic, + contractUseAmount?: string | number, + pivotBalance?: string | number, +): Promise => { + const approveResult = await checkElfChainAllowanceAndApprove({ + tokenContract, + approveTargetAddress, + account, + contractUseAmount, + pivotBalance, + symbol: symbol, + }); + if (typeof approveResult !== 'boolean' && approveResult.error) { + CommonMessage.error('Approval Failed'); + CommonMessage.error(approveResult.error.message); + if (isUserDenied(approveResult.error.message)) return REQ_CODE.UserDenied; + return REQ_CODE.Fail; + } + return REQ_CODE.Success; +}; diff --git a/src/contracts/ethereum.ts b/src/contracts/ethereum.ts new file mode 100644 index 00000000..fd7cdd6a --- /dev/null +++ b/src/contracts/ethereum.ts @@ -0,0 +1,177 @@ +import Web3 from 'web3'; +import { provider } from 'web3-core'; +import { AbiItem } from 'web3-utils/types'; +import { Contract } from 'web3-eth-contract'; +import BigNumber from 'bignumber.js'; +import { ContractBasic } from '../utils/contract'; +import { ERC20_ABI } from 'constants/abis'; +import { MaxUint256, REQ_CODE } from 'constants/misc'; +import { isUserDenied } from 'utils/provider'; +import CommonMessage from 'components/CommonMessage'; +import { ChainId } from 'types'; +import { handleErrorMessage } from 'utils/error'; + +// ethereum +export const getContract = (provider: provider, address: string) => { + const web3 = new Web3(provider); + const contract = new web3.eth.Contract(ERC20_ABI as unknown as AbiItem, address); + return contract; +}; + +export const callERC20ViewMethod = async ( + functionName: string, + provider: provider, + nftAddress: string, + paramsOption?: any, +): Promise => { + try { + const contract = getContract(provider, nftAddress); + if (paramsOption) { + return await contract.methods[functionName](paramsOption).call(); + } + return await contract.methods[functionName]().call(); + } catch (e) { + console.debug('callNFTShardsViewMethod error', e); + return '0'; + } +}; + +export const getAllowance = async ( + lpContract: Contract, + masterChefContract: Contract, + account: string, +): Promise => { + try { + const allowance: string = await lpContract.methods.allowance(account, masterChefContract.options.address).call(); + return allowance; + } catch (e) { + return '0'; + } +}; + +export const getBalance = async (provider: provider, tokenAddress: string, userAddress: string): Promise => { + try { + const lpContract = getContract(provider, tokenAddress); + const balance: string = await lpContract.methods.balanceOf(userAddress).call(); + return balance; + } catch (e) { + return '0'; + } +}; +export const getTotalSupply = async (provider: provider, tokenAddress: string): Promise => { + try { + const lpContract = getContract(provider, tokenAddress); + return await lpContract.methods.totalSupply().call(); + } catch (e) { + return '0'; + } +}; + +export const checkErcApprove = async ( + fromChainId: ChainId, + contractAddress: string, + account: string, + approveTargetAddress: string, + contractUseAmount?: string | number, + pivotBalance?: string | number, +): Promise => { + const lpContract = new ContractBasic({ + contractABI: ERC20_ABI, + contractAddress: contractAddress, + chainId: fromChainId, + }); + const approveResult = await checkAllowanceAndApprove({ + erc20Contract: lpContract, + approveTargetAddress, + account, + contractUseAmount, + pivotBalance, + }); + if (typeof approveResult !== 'boolean' && approveResult.error) { + CommonMessage.error('Check allowance and Approved failed'); + CommonMessage.error(handleErrorMessage(approveResult)); + if (isUserDenied(approveResult.error.details)) return REQ_CODE.UserDenied; + return REQ_CODE.Fail; + } + return REQ_CODE.Success; +}; + +export const checkAllowanceAndApprove = async ({ + erc20Contract, + approveTargetAddress, + account, + contractUseAmount, + pivotBalance, +}: { + erc20Contract: ContractBasic; + approveTargetAddress: string; + account: string; + contractUseAmount?: string | number; + pivotBalance?: string | number; +}): Promise => { + const [allowance, decimals] = await Promise.all([ + erc20Contract.callViewMethod('allowance', [account, approveTargetAddress]), + erc20Contract.callViewMethod('decimals'), + ]); + if (allowance.error) { + return allowance; + } + const allowanceBN = new BigNumber(allowance); + const pivotBalanceBN = contractUseAmount + ? new BigNumber(contractUseAmount) + : new BigNumber(pivotBalance || 1).times(10 ** decimals); + if (allowanceBN.lt(pivotBalanceBN)) { + const approveResult = await erc20Contract.callSendMethod('approve', account, [approveTargetAddress, MaxUint256]); + if (approveResult.error) { + return approveResult; + } else { + return approveResult; + } + } + return true; +}; + +export const createToken = async ({ + createTokenContract, + account, + name, + symbol, + initialSupply, +}: { + createTokenContract: ContractBasic; + account: string; + name: string; + symbol: string; + initialSupply: string; +}) => { + return createTokenContract.callSendMethod('createToken', account, [name, symbol, initialSupply], { + onMethod: 'transactionHash', + }); +}; + +export const createOfficialToken = async ({ + createTokenContract, + account, + name, + symbol, + initialSupply, + officialAddress, + mintToAddress, +}: { + createTokenContract: ContractBasic; + account: string; + name: string; + symbol: string; + initialSupply: string; + officialAddress: string; + mintToAddress: string; +}) => { + return createTokenContract.callSendMethod( + 'createOfficialToken', + account, + [name, symbol, initialSupply, officialAddress, mintToAddress], + { + onMethod: 'transactionHash', + }, + ); +}; diff --git a/src/contracts/index.ts b/src/contracts/index.ts new file mode 100644 index 00000000..9780243b --- /dev/null +++ b/src/contracts/index.ts @@ -0,0 +1,22 @@ +import { ContractBasic } from 'utils/contract'; +import { checkELFApprove } from './elf'; +import { checkErcApprove } from './ethereum'; +import { ChainId } from 'types'; + +export const checkApprove = async ( + fromChainId: ChainId, + // ethereum from token address elf from token symbol + fromToken: string, + account: string, + approveTargetAddress: string, + contractUseAmount?: string | number, + pivotBalance?: string | number, + // elf token contract + tokenContract?: ContractBasic, +): Promise => { + if (tokenContract) + return checkELFApprove(fromToken, account, approveTargetAddress, tokenContract, contractUseAmount, pivotBalance); + return checkErcApprove(fromChainId, fromToken, account, approveTargetAddress, contractUseAmount, pivotBalance); +}; +export * from './elf'; +export * from './ethereum'; diff --git a/src/hooks/aelfAuthToken.ts b/src/hooks/aelfAuthToken.ts new file mode 100644 index 00000000..5c3c5f14 --- /dev/null +++ b/src/hooks/aelfAuthToken.ts @@ -0,0 +1,249 @@ +import { WalletTypeEnum as AelfWalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { APP_NAME } from 'constants/index'; +import { useCallback, useRef, useState } from 'react'; +import AElf from 'aelf-sdk'; +import { zeroFill } from '@portkey/utils'; +import { useAElf } from './web3'; +import { AuthTokenSource, getAuthPlainText, getLocalJWT, QueryAuthApiExtraRequest } from 'utils/aelfAuthToken'; +import { ExtraInfoForDiscover, WebLoginWalletInfo } from 'types/wallet'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; +import { queryAuthApi } from 'utils/api/auth'; +import { service } from 'api/utils'; +import { getCaHashAndOriginChainIdByWallet, getManagerAddressByWallet, useIsAelfLogin } from './wallet'; +import { recoverPubKey } from 'utils/aelfUtils'; +import { eBridgeInstance } from 'utils/eBridgeInstance'; +import { useConnectWallet } from '@aelf-web-login/wallet-adapter-react'; +import { useEffectOnce } from 'react-use'; +import useGlobalLoading from './useGlobalLoading'; + +export function useAelfAuthToken() { + const { account } = useAElf(); + const { walletInfo, walletType, disConnectWallet, getSignature } = useConnectWallet(); + + const isAelfLogin = useIsAelfLogin(); + const isAelfLoginRef = useRef(isAelfLogin); + isAelfLoginRef.current = isAelfLogin; + + const loginWalletTypeRef = useRef(walletType); + loginWalletTypeRef.current = walletType; + + const walletInfoRef = useRef(walletInfo); + walletInfoRef.current = walletInfo; + const { setGlobalLoading } = useGlobalLoading(); + + const loginSuccessActive = useCallback(() => { + console.log('%c login success and emit event', 'color: green'); + eBridgeEventBus.AelfLoginSuccess.emit(); + }, []); + + const handleSignMessage = useCallback(async () => { + if (!account) return; + const plainText = getAuthPlainText(); + const plainTextHex: any = plainText.plainTextHex; + let signResult: { + error: number; + errorMessage: string; + signature: string; + from: string; + } | null; + + if (loginWalletTypeRef.current === AelfWalletTypeEnum.discover) { + // discover + const discoverInfo = walletInfoRef.current?.extraInfo as ExtraInfoForDiscover; + if ((discoverInfo?.provider as any).methodCheck('wallet_getManagerSignature')) { + const sin = await discoverInfo?.provider?.request({ + method: 'wallet_getManagerSignature', + payload: { hexData: plainTextHex }, + }); + const signInfo = [zeroFill(sin.r), zeroFill(sin.s), `0${sin.recoveryParam.toString()}`].join(''); + signResult = { + error: 0, + errorMessage: '', + signature: signInfo, + from: AelfWalletTypeEnum.discover, + }; + } else { + const signInfo = AElf.utils.sha256(plainTextHex); + signResult = await getSignature({ + appName: APP_NAME, + address: account, + signInfo, + }); + } + } else if (loginWalletTypeRef.current === AelfWalletTypeEnum.elf) { + // nightElf + const signInfo = AElf.utils.sha256(plainTextHex); + signResult = await getSignature({ + appName: APP_NAME, + address: account, + signInfo, + }); + } else { + // portkey sdk + const signInfo = Buffer.from(plainTextHex).toString('hex'); + signResult = await getSignature({ + appName: APP_NAME, + address: account, + signInfo, + }); + } + + if (signResult?.error) throw signResult.errorMessage; + + return { signature: signResult?.signature || '', plainText: plainTextHex }; + }, [account, getSignature]); + + const queryAuth = useCallback( + async (isThrowError: boolean, isAfterErrorDisconnect: boolean): Promise => { + if (!isAelfLoginRef.current || !loginWalletTypeRef.current) return; + if (eBridgeInstance.obtainingSignature) return; + try { + // Mark: only one signature process can be performed at the same time + eBridgeInstance.setObtainingSignature(true); + setGlobalLoading(true); + const { caHash, originChainId } = await getCaHashAndOriginChainIdByWallet( + walletInfoRef.current as WebLoginWalletInfo, + loginWalletTypeRef.current, + ); + const signatureResult = await handleSignMessage(); + if (!signatureResult) throw Error('Signature error'); + const pubkey = recoverPubKey(signatureResult.plainText, signatureResult.signature) + ''; + const managerAddress = await getManagerAddressByWallet( + walletInfoRef.current as WebLoginWalletInfo, + loginWalletTypeRef.current, + pubkey, + ); + const apiParams: QueryAuthApiExtraRequest = { + pubkey, + signature: signatureResult.signature, + plain_text: signatureResult.plainText, + source: + loginWalletTypeRef.current === AelfWalletTypeEnum.elf ? AuthTokenSource.NightElf : AuthTokenSource.Portkey, + managerAddress: managerAddress, + ca_hash: caHash || undefined, + chain_id: originChainId || undefined, + }; + + const authToken = await queryAuthApi(apiParams); + eBridgeInstance.setUnauthorized(false); + console.log('login status isAelfLoginRef.current', isAelfLoginRef.current); + loginSuccessActive(); + return authToken; + } catch (error: any) { + console.log('queryAuthApi error', error); + if (isThrowError) throw error; + if (isAfterErrorDisconnect) await disConnectWallet(); + + return; + } finally { + eBridgeInstance.setUnauthorized(false); + eBridgeInstance.setObtainingSignature(false); + } + }, + [setGlobalLoading, handleSignMessage, loginSuccessActive, disConnectWallet], + ); + + const getAuth = useCallback( + async (isThrowError: boolean, isAfterErrorDisconnect: boolean): Promise => { + if (!isAelfLoginRef.current || !loginWalletTypeRef.current) return; + if (eBridgeInstance.obtainingSignature) return; + try { + const { caHash } = await getCaHashAndOriginChainIdByWallet( + walletInfoRef.current as WebLoginWalletInfo, + loginWalletTypeRef.current, + ); + const managerAddress = await getManagerAddressByWallet( + walletInfoRef.current as WebLoginWalletInfo, + loginWalletTypeRef.current, + ); + const source = + loginWalletTypeRef.current === AelfWalletTypeEnum.elf ? AuthTokenSource.NightElf : AuthTokenSource.Portkey; + const key = (caHash || source) + managerAddress; + const data = getLocalJWT(key); + // 1: local storage has JWT token + if (data) { + const token_type = data.token_type; + const access_token = data.access_token; + + service.defaults.headers.common['Authorization'] = `${token_type} ${access_token}`; + loginSuccessActive(); + return `${token_type} ${access_token}`; + } else { + // 2: local storage don not has JWT token + return await queryAuth(isThrowError, isAfterErrorDisconnect); + } + } catch (error) { + console.log('getAuth error:', error); + if (isThrowError) throw error; + return; + } + }, + [loginSuccessActive, queryAuth], + ); + + return { getAuth, queryAuth, loginSuccessActive }; +} + +export function useSetAelfAuthFromStorage() { + const { loginWalletType } = useAElf(); + const { walletInfo } = useConnectWallet(); + const walletInfoRef = useRef(walletInfo); + walletInfoRef.current = walletInfo; + + return useCallback(async () => { + if (!walletInfoRef.current || loginWalletType === AelfWalletTypeEnum.unknown || !loginWalletType) return false; + + const { caHash } = await getCaHashAndOriginChainIdByWallet( + walletInfoRef.current as WebLoginWalletInfo, + loginWalletType, + ); + const managerAddress = await getManagerAddressByWallet( + walletInfoRef.current as WebLoginWalletInfo, + loginWalletType, + ); + const source = loginWalletType === AelfWalletTypeEnum.elf ? AuthTokenSource.NightElf : AuthTokenSource.Portkey; + const key = (caHash || source) + managerAddress; + const data = getLocalJWT(key); + // local storage has JWT token + if (data) { + const token_type = data.token_type; + const access_token = data.access_token; + service.defaults.headers.common['Authorization'] = `${token_type} ${access_token}`; + return true; + } + + return false; + }, [loginWalletType]); +} + +export function useShowLoginButtonLoading() { + const { isConnected, walletInfo } = useConnectWallet(); + const [loading, setLoading] = useState(true); + const walletInfoRef = useRef(walletInfo); + walletInfoRef.current = walletInfo; + + const timerRef = useRef(null); + const stopLoading = useCallback(() => { + timerRef.current = setTimeout(() => { + setLoading(false); + timerRef.current = null; + }, 3000); + }, []); + + useEffectOnce(() => { + if (isConnected && !walletInfoRef.current) { + stopLoading(); + } else { + setLoading(false); + } + + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = null; + } + }; + }); + + return loading; +} diff --git a/src/hooks/analytics.ts b/src/hooks/analytics.ts new file mode 100644 index 00000000..edc44aac --- /dev/null +++ b/src/hooks/analytics.ts @@ -0,0 +1,15 @@ +import { useEffect } from 'react'; +import ReactGA from 'react-ga'; + +export function useGoogleAnalytics(id?: string) { + useEffect(() => { + if (typeof window === 'object' && id) { + ReactGA.initialize(id); + ReactGA.pageview(window.location.pathname + window.location.search); + // ReactGA.event({ + // category: 'User', + // action: 'Created an Account', + // }); + } + }, [id]); +} diff --git a/src/hooks/checkTxnFee.ts b/src/hooks/checkTxnFee.ts new file mode 100644 index 00000000..a1b0ce40 --- /dev/null +++ b/src/hooks/checkTxnFee.ts @@ -0,0 +1,98 @@ +import { useCallback, useMemo, useState } from 'react'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { divDecimals } from 'utils/calculate'; +import { CrossFeeToken, CrossFeeTokenDecimals, ZERO } from 'constants/misc'; +import { getELFChainBalance } from 'contracts'; +import { useTokenContract } from 'hooks/useContract'; +import lodash from 'lodash'; +import { useHomeContext } from 'page-components/Bridge/HomeContext'; + +export function useCheckTxnFeeEnoughAuto() { + const [{ selectToken, fromInput, crossFee }] = useHomeContext(); + const { fromWallet } = useWallet(); + const { chainId: fromChainId, account: fromAccount } = fromWallet || {}; + const fromTokenInfo = useMemo(() => { + if (!fromChainId) return; + const token = lodash.cloneDeep(selectToken?.[fromChainId]); + if (token?.isNativeToken) token.address = ''; + return token; + }, [fromChainId, selectToken]); + const tokenContract = useTokenContract(fromChainId, fromTokenInfo?.address, fromWallet?.isPortkey); + const [isTxnFeeEnoughTip, setIsTxnFeeEnoughTip] = useState(false); + const isCanCheckTxnFeeEnoughTip = useMemo(() => { + return ( + fromWallet && + fromWallet?.walletType !== 'ERC' && + fromAccount && + fromInput && + crossFee && + ZERO.plus(crossFee).gt(ZERO) && + tokenContract + ); + }, [crossFee, fromAccount, fromInput, fromWallet, tokenContract]); + const isShowTxnFeeEnoughTip = useMemo(() => { + return isCanCheckTxnFeeEnoughTip && isTxnFeeEnoughTip; + }, [isCanCheckTxnFeeEnoughTip, isTxnFeeEnoughTip]); + + useMemo(async () => { + if (!tokenContract || !crossFee || !fromAccount || !isCanCheckTxnFeeEnoughTip) return; + + try { + const userBalance = await getELFChainBalance(tokenContract, CrossFeeToken, fromAccount); + if (divDecimals(userBalance, CrossFeeTokenDecimals).lt(crossFee)) { + return setIsTxnFeeEnoughTip(true); + } + return setIsTxnFeeEnoughTip(false); + } catch (error) { + return setIsTxnFeeEnoughTip(false); + } + }, [crossFee, fromAccount, isCanCheckTxnFeeEnoughTip, tokenContract]); + + return isShowTxnFeeEnoughTip; +} + +export function useCheckTxnFeeEnough() { + const [{ selectToken, fromInput, crossFee }] = useHomeContext(); + const { fromWallet } = useWallet(); + const { chainId: fromChainId, account: fromAccount } = fromWallet || {}; + const fromTokenInfo = useMemo(() => { + if (!fromChainId) return; + const token = lodash.cloneDeep(selectToken?.[fromChainId]); + if (token?.isNativeToken) token.address = ''; + return token; + }, [fromChainId, selectToken]); + const tokenContract = useTokenContract(fromChainId, fromTokenInfo?.address, fromWallet?.isPortkey); + const [isTxnFeeEnoughTip, setIsTxnFeeEnoughTip] = useState(false); + const isCanCheckTxnFeeEnoughTip = useMemo(() => { + return ( + fromWallet && + fromWallet?.walletType !== 'ERC' && + fromAccount && + fromInput && + crossFee && + ZERO.plus(crossFee).gt(ZERO) && + tokenContract + ); + }, [crossFee, fromAccount, fromInput, fromWallet, tokenContract]); + const isShowTxnFeeEnoughTip = useMemo(() => { + return isCanCheckTxnFeeEnoughTip && isTxnFeeEnoughTip; + }, [isCanCheckTxnFeeEnoughTip, isTxnFeeEnoughTip]); + const checkTxnFeeEnough = useCallback(async () => { + if (!isCanCheckTxnFeeEnoughTip || !tokenContract || !crossFee || !fromAccount) return; + + try { + const userBalance = await getELFChainBalance(tokenContract, CrossFeeToken, fromAccount); + if (divDecimals(userBalance, CrossFeeTokenDecimals).lt(crossFee)) { + setIsTxnFeeEnoughTip(true); + return; + } + setIsTxnFeeEnoughTip(false); + return; + } catch (error) { + setIsTxnFeeEnoughTip(false); + return; + } + }, [crossFee, fromAccount, isCanCheckTxnFeeEnoughTip, tokenContract]); + + return { checkTxnFeeEnough, isShowTxnFeeEnoughTip }; +} diff --git a/src/hooks/crossChain.ts b/src/hooks/crossChain.ts new file mode 100644 index 00000000..1cade017 --- /dev/null +++ b/src/hooks/crossChain.ts @@ -0,0 +1,14 @@ +import { useWallet } from 'contexts/useWallet/hooks'; +import { useMemo } from 'react'; +import { CrossChainType } from 'types'; +import { isELFChain } from 'utils/aelfUtils'; + +export function useCrossChainType() { + const { fromWallet, toWallet } = useWallet(); + const fromChainId = fromWallet?.chainId; + const toChainId = toWallet?.chainId; + return useMemo(() => { + if (isELFChain(fromChainId) && isELFChain(toChainId)) return CrossChainType.homogeneous; + return CrossChainType.heterogeneous; + }, [fromChainId, toChainId]); +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 00000000..206b13a1 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,62 @@ +import { DependencyList, useCallback, useRef } from 'react'; + +/** + * that deal with the debounced function. + */ +export function useDebounceCallback any>( + callback: T | undefined, + deps: DependencyList, + delay = 500, +) { + const timer = useRef(); + const callbackRef = useLatestRef(callback); + + return useCallback((...args: any[]) => { + if (!callbackRef.current) return; + timer.current && clearTimeout(timer.current); + timer.current = setTimeout(() => { + callbackRef.current?.(...args); + }, delay); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); +} + +/** + * that deal with the throttled function. + */ +export function useThrottleCallback any>( + callback: T | undefined, + deps: DependencyList, + delay = 500, +) { + const lock = useRef(); + return useCallback((...args: any[]) => { + if (!callback) return; + const now = Date.now(); + if (lock.current && lock.current + delay > now) return; + lock.current = now; + return callback(...args); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); +} +/** + * returns the latest value, effectively avoiding the closure problem. + */ +export function useLatestRef(value: T) { + const ref = useRef(value); + ref.current = value; + + return ref; +} + +export function useReturnLastCallback any>(callback: T, deps: DependencyList) { + const last = useRef(0); + return useCallback(async (...args: any) => { + ++last.current; + const id = last.current; + const req = await callback(...args); + if (last.current !== id) throw new Error('Not the latest request'); + return req; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); +} diff --git a/src/hooks/pools.ts b/src/hooks/pools.ts new file mode 100644 index 00000000..8b2c838a --- /dev/null +++ b/src/hooks/pools.ts @@ -0,0 +1,142 @@ +import { useCallback, useEffect, useState } from 'react'; +import { ChainId, TokenInfo } from 'types'; +import { ContractBasic } from 'utils/contract'; +import useInterval from './useInterval'; +import { getMyLiquidity, getTotalLiquidity } from 'utils/pools'; +import { divDecimals } from 'utils/calculate'; +import { request } from 'api'; +import { useActiveAddresses } from './web3'; +import { usePoolsDispatch } from 'contexts/usePools/hooks'; +import { setPoolList, setPoolOverview } from 'contexts/usePools/actions'; +import { usePools } from 'contexts/usePools'; +import { getChainIdByAPI } from 'utils/chain'; + +export function usePoolTotalLiquidity( + { + poolContract, + tokenContract, + tokenInfo, + }: { + poolContract?: ContractBasic; + tokenContract?: ContractBasic; + tokenInfo?: TokenInfo; + }, + delay: null | number = 10000, +) { + const [totalLiquidity, setTotalLiquidity] = useState(); + + const onGetTotalLiquidity = useCallback(async () => { + if (!poolContract || !tokenContract || !tokenInfo) return; + + try { + const liquidity = await getTotalLiquidity({ + poolContract, + tokenContract, + symbol: tokenInfo.symbol, + }); + setTotalLiquidity(liquidity); + } catch (error) { + console.debug(error); + } + }, [poolContract, tokenContract, tokenInfo]); + + useInterval(onGetTotalLiquidity, [onGetTotalLiquidity], delay); + + return { + totalLiquidity, + showTotalLiquidity: divDecimals(totalLiquidity, tokenInfo?.decimals), + onGetTotalLiquidity, + }; +} + +export function usePoolMyLiquidity( + { + poolContract, + account, + tokenInfo, + }: { + poolContract?: ContractBasic; + account?: string; + tokenInfo?: TokenInfo; + }, + delay: null | number = 10000, +) { + const [myLiquidity, setMyLiquidity] = useState(); + const onGetMyLiquidity = useCallback(async () => { + if (!poolContract || !account || !tokenInfo) return; + try { + const liquidity = await getMyLiquidity({ + poolContract, + account, + tokenInfo, + }); + setMyLiquidity(liquidity); + } catch (error) { + console.debug(error); + } + }, [account, poolContract, tokenInfo]); + + useInterval(onGetMyLiquidity, [getTotalLiquidity], delay); + + return { + myLiquidity, + showMyLiquidity: divDecimals(myLiquidity, tokenInfo?.decimals), + onGetMyLiquidity, + }; +} + +export function usePoolOverview() { + const addresses = useActiveAddresses(); + const [{ poolOverview }] = usePools(); + const dispatch = usePoolsDispatch(); + const getPoolOverview = useCallback(async () => { + try { + const req = await request.pool.overview({ params: { addresses } }); + dispatch(setPoolOverview(req.data)); + } catch (error) { + console.debug(error, 'getPoolOverview'); + } + }, [addresses, dispatch]); + + useEffect(() => { + getPoolOverview(); + }, [getPoolOverview]); + + return { poolOverview, getPoolOverview }; +} + +export function usePoolList() { + const addresses = useActiveAddresses(); + const [{ poolList }] = usePools(); + const dispatch = usePoolsDispatch(); + const getPoolList = useCallback(async () => { + try { + const req = await request.pool.list({ params: { addresses, skipCount: 0, maxResultCount: 200 } }); + dispatch(setPoolList(req.data)); + } catch (error) { + console.debug(error, 'getPoolList'); + } + }, [addresses, dispatch]); + + useEffect(() => { + getPoolList(); + }, [getPoolList]); + + return { poolList, getPoolList }; +} + +export function useGetTokenInfoByPoolList() { + const { poolList } = usePoolList(); + return useCallback( + (chainId?: ChainId, symbol?: string) => { + if (!chainId || !symbol) return; + try { + const item = poolList?.items.find((i) => getChainIdByAPI(i.chainId) === chainId && i.token?.symbol === symbol); + if (item?.token?.chainId) return { ...item?.token, chainId: getChainIdByAPI(item.token.chainId) }; + } catch (error) { + console.debug(error, 'useGetTokenInfoByPoolList'); + } + }, + [poolList], + ); +} diff --git a/src/hooks/telegram.ts b/src/hooks/telegram.ts new file mode 100644 index 00000000..90e65265 --- /dev/null +++ b/src/hooks/telegram.ts @@ -0,0 +1,28 @@ +import { useRef, useState, useEffect } from 'react'; +import { TelegramPlatform } from 'utils/telegram/telegram'; + +export function useIsTelegramPlatform() { + const getTelegramPlatformCount = useRef(0); + const getTelegramPlatformTimer = useRef(); + const [isTelegramPlatform, setIsTelegramPlatform] = useState(false); + useEffect((): any => { + setIsTelegramPlatform(TelegramPlatform.isTelegramPlatform()); + getTelegramPlatformTimer.current = setInterval(() => { + if (getTelegramPlatformCount.current <= 10) { + setIsTelegramPlatform(TelegramPlatform.isTelegramPlatform()); + getTelegramPlatformCount.current++; + console.log('>>>>>> isTelegramPlatform interval in _app.tsx', TelegramPlatform.isTelegramPlatform()); + } else { + clearInterval(getTelegramPlatformTimer.current); + getTelegramPlatformCount.current = 0; + } + }, 1000); + return () => { + clearInterval(getTelegramPlatformTimer.current); + getTelegramPlatformTimer.current = undefined; + getTelegramPlatformCount.current = 0; + }; + }, []); + + return isTelegramPlatform; +} diff --git a/src/hooks/token.ts b/src/hooks/token.ts new file mode 100644 index 00000000..8f5bf08b --- /dev/null +++ b/src/hooks/token.ts @@ -0,0 +1,102 @@ +import { useCallback, useEffect } from 'react'; +import { useActiveWhitelist } from './whitelist'; +import { ChainId, TokenInfo } from 'types'; +import { BRIDGE_TOKEN_MAP } from 'constants/index'; +import { getTokenPrice } from 'utils/api/common'; +import { useTokenDispatch, useTokenPriceByContext } from 'contexts/useToken/hooks'; +import { addTokenPrice } from 'contexts/useToken/actions'; +import { useEVMSwitchChain } from './web3'; +import { CREATE_TOKEN_ABI } from 'constants/abis'; +import { getContract } from './useContract'; +import { getBridgeChainInfo } from 'utils/chain'; +import { createOfficialToken, createToken } from 'contracts'; + +export function useGetTokenInfoByWhitelist() { + const activeWhitelist = useActiveWhitelist(); + return useCallback( + (chainId?: ChainId, symbol?: string) => { + if (!chainId || !symbol) return; + try { + return (activeWhitelist as any)[BRIDGE_TOKEN_MAP?.[symbol] || symbol]?.[chainId] as TokenInfo; + } catch (error) { + console.debug(error, 'useGetTokenInfoByWhitelist'); + } + }, + [activeWhitelist], + ); +} + +export function useTokenPrice(symbol1?: string) { + const dispatch = useTokenDispatch(); + + const price = useTokenPriceByContext(symbol1); + const onGetTokenPrice = useCallback( + async (symbol2?: string) => { + const symbol = symbol2 || symbol1; + if (!symbol) return '0'; + try { + const req = await getTokenPrice({ symbol, amount: '1' }); + dispatch(addTokenPrice(symbol, req.tokenAmountInUsd)); + return req.tokenAmountInUsd; + } catch (error) { + console.debug(error, 'onGetTokenPrice'); + } + }, + [dispatch, symbol1], + ); + useEffect(() => { + onGetTokenPrice(); + }, [onGetTokenPrice]); + + return { price, onGetTokenPrice }; +} + +export function useCallEVMCreateToken() { + const evmSwitchChain = useEVMSwitchChain(); + + return useCallback( + async ({ + chainId, + ...args + }: { + chainId: ChainId; + account: string; + name: string; + symbol: string; + initialSupply: string; + }) => { + await evmSwitchChain(chainId); + const address = getBridgeChainInfo(chainId)?.CREATE_TOKEN_CONTRACT; + const contract = getContract(address, CREATE_TOKEN_ABI, chainId); + return createToken({ createTokenContract: contract, ...args }); + }, + [evmSwitchChain], + ); +} + +export function useCallEVMCreateOfficialToken() { + const evmSwitchChain = useEVMSwitchChain(); + + return useCallback( + async ({ + chainId, + ...args + }: { + chainId: ChainId; + account: string; + name: string; + symbol: string; + initialSupply: string; + officialAddress: string; + mintToAddress: string; + }) => { + await evmSwitchChain(chainId); + console.log('====== useCallEVMCreateOfficialToken', ''); + const address = getBridgeChainInfo(chainId)?.CREATE_TOKEN_CONTRACT; + const contract = getContract(address, CREATE_TOKEN_ABI, chainId); + console.log('====== useCallEVMCreateOfficialToken address', address, contract); + return createOfficialToken({ createTokenContract: contract, ...args }); + }, + [evmSwitchChain], + ); +} diff --git a/src/hooks/useAllowance.ts b/src/hooks/useAllowance.ts new file mode 100644 index 00000000..2f4664b0 --- /dev/null +++ b/src/hooks/useAllowance.ts @@ -0,0 +1,48 @@ +import BigNumber from 'bignumber.js'; +import { ZERO } from 'constants/misc'; +import { useReturnLastCallback } from 'hooks'; +import { useCallback, useEffect, useState } from 'react'; +import { ContractBasic } from 'utils/contract'; +export function useAllowance( + tokenContract?: ContractBasic, + account?: string, + approveTargetAddress?: string, + symbol?: string, +): [BigNumber | undefined, () => void] { + const [allowance, setAllowance] = useState(); + + const getAllowance = useReturnLastCallback(async () => { + if (!tokenContract) return undefined; + if (tokenContract.contractType === 'ELF') { + if (!symbol) return undefined; + const req = await tokenContract?.callViewMethod('GetAllowance', [symbol, account, approveTargetAddress]); + if (!req.error) { + const allowanceBN = new BigNumber(req.allowance ?? req.amount ?? 0); + return allowanceBN; + } + } else { + const req = await tokenContract?.callViewMethod('allowance', [account, approveTargetAddress]); + if (!req.error) { + return ZERO.plus(req); + } + } + return undefined; + }, [account, approveTargetAddress, symbol, tokenContract]); + + const refreshAllowance = useCallback(async () => { + try { + const result = await getAllowance(); + setAllowance(result); + } catch (error) { + console.log('refreshAllowance', error); + } + }, [getAllowance]); + + useEffect(() => { + refreshAllowance(); + }, [refreshAllowance]); + useEffect(() => { + if (!account) setAllowance(undefined); + }, [account]); + return [allowance, refreshAllowance]; +} diff --git a/src/hooks/useBalances.ts b/src/hooks/useBalances.ts new file mode 100644 index 00000000..ed194a37 --- /dev/null +++ b/src/hooks/useBalances.ts @@ -0,0 +1,76 @@ +import BigNumber from 'bignumber.js'; +import { ZERO } from 'constants/misc'; +import { getELFChainBalance } from 'contracts'; +import { useCallback, useMemo, useState } from 'react'; +import { TokenInfo, Web3Type } from 'types'; +import { isERCAddress, isTonChain } from 'utils'; +import { isELFChain } from 'utils/aelfUtils'; +import { useTokenContract } from './useContract'; +import useInterval from './useInterval'; +import { getTonChainBalance } from 'utils/ton'; +import { getBalanceByWagmi } from 'utils/wagmi'; +import { useGetTokenInfoByWhitelist } from './token'; + +export const useBalances = ( + wallet?: Web3Type, + tokens?: string | TokenInfo | Array, + delay: null | number = 10000, + targetAddress?: string, +): [BigNumber[], () => void] => { + const [balanceMap, setBalanceMap] = useState<{ [key: string]: BigNumber }>(); + const { chainId, account: owner } = wallet || {}; + const account = useMemo(() => targetAddress || owner, [targetAddress, owner]); + const tokenContract = useTokenContract(chainId, undefined, wallet?.isPortkey); + const tokensList = useMemo(() => (Array.isArray(tokens) ? tokens : [tokens]), [tokens]); + const getTokenInfoByWhitelist = useGetTokenInfoByWhitelist(); + const onGetBalance = useCallback(async () => { + if (!account) return setBalanceMap(undefined); + let promise; + if (isELFChain(chainId)) { + // elf chain + promise = tokensList.map((info) => { + if (!tokenContract) return '0'; + const isSymbol = typeof info === 'string'; + const symbol = isSymbol ? info : info?.symbol; + const tokenInfo = isSymbol ? getTokenInfoByWhitelist(chainId, symbol) : info; + if (!tokenInfo) return '0'; + if (symbol) return getELFChainBalance(tokenContract, tokenInfo.symbol, account); + }); + } else { + if (isTonChain(chainId)) { + // ton chain + promise = tokensList.map(async (info) => { + const isAddress = typeof info === 'string'; + const address = isAddress ? info : info?.address; + if (!address || !account) return; + return getTonChainBalance(address, account); + }); + } else { + // erc20 chain + promise = tokensList.map(async (info) => { + const isAddress = typeof info === 'string'; + const address = isAddress ? info : info?.address; + if (info && address) { + const params = { address: account as any, token: address as any, chainId: chainId }; + if (!isERCAddress(address)) delete params.token; + const req = await getBalanceByWagmi(params); + return req.value.toString(); + } + }); + } + } + const bs = await Promise.all(promise); + const obj: any = {}; + tokensList.forEach((key, index) => { + if (key) obj[key + account + chainId] = bs[index]; + }); + setBalanceMap((preObj) => ({ ...preObj, ...obj })); + }, [account, chainId, tokensList, tokenContract, getTokenInfoByWhitelist]); + useInterval(onGetBalance, [onGetBalance, chainId, tokenContract], delay); + const memoBalances = useMemo(() => { + if (tokensList) + return tokensList.map((key) => (balanceMap && key ? balanceMap[key + (account || '') + chainId] : ZERO)); + return [ZERO]; + }, [account, balanceMap, chainId, tokensList]); + return [memoBalances, onGetBalance]; +}; diff --git a/src/hooks/useCheckPortkeyStatus.tsx b/src/hooks/useCheckPortkeyStatus.tsx new file mode 100644 index 00000000..6d90f4ca --- /dev/null +++ b/src/hooks/useCheckPortkeyStatus.tsx @@ -0,0 +1,39 @@ +import { useChain } from 'contexts/useChain'; +import { useModal } from 'contexts/useModal'; +import { useCallback } from 'react'; +import { ChainId } from 'types'; +import { isSelectPortkey } from 'utils/portkey'; +import { basicModalView } from 'contexts/useModal/actions'; +import { useGetWalletManagerStatus } from './wallet'; +import { ChainId as TChainId } from '@portkey/types'; + +export default function useCheckPortkeyStatus() { + const getWalletManagerStatus = useGetWalletManagerStatus(); + const [{ selectELFWallet }] = useChain(); + const [, { dispatch }] = useModal(); + + const checkPortkeyConnect = useCallback( + async (chainId: ChainId) => { + if (!isSelectPortkey(selectELFWallet)) { + return true; + } + + const status = await getWalletManagerStatus(chainId as TChainId); + console.log('getWalletManagerStatus', status); + + if (!status) { + dispatch( + basicModalView.setPortketNotConnectModal({ + visible: true, + chainId, + }), + ); + } + + return status; + }, + [dispatch, getWalletManagerStatus, selectELFWallet], + ); + + return { checkPortkeyConnect }; +} diff --git a/src/hooks/useConnect.ts b/src/hooks/useConnect.ts new file mode 100644 index 00000000..152e52b5 --- /dev/null +++ b/src/hooks/useConnect.ts @@ -0,0 +1,38 @@ +import { useCallback } from 'react'; +import { ChainId, ChainType } from 'types'; +import { useAelfLogin } from './wallet'; +import { useTonConnectModal } from '@tonconnect/ui-react'; +import { setWalletModal } from 'contexts/useModal/actions'; +import { useModalDispatch } from 'contexts/useModal/hooks'; + +export function useConnect() { + const login = useAelfLogin(); + const { open } = useTonConnectModal(); + const dispatch = useModalDispatch(); + return useCallback( + (chainType?: ChainType, chainId?: ChainId) => { + switch (chainType) { + case 'ELF': { + login(); + break; + } + case 'TON': { + open(); + break; + } + default: { + // ERC + dispatch( + setWalletModal(true, { + walletWalletType: 'ERC', + walletChainType: chainType, + walletChainId: chainId, + }), + ); + break; + } + } + }, + [dispatch, login, open], + ); +} diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts new file mode 100644 index 00000000..eb5cada8 --- /dev/null +++ b/src/hooks/useContract.ts @@ -0,0 +1,363 @@ +import { BRIDGE_IN_ABI, BRIDGE_OUT_ABI, ERC20_ABI, CREATE_TOKEN_ABI, LIMIT_ABI, POOLS_ABI } from 'constants/abis'; +import { useCallback, useEffect, useMemo } from 'react'; +import { AelfInstancesKey, ChainId } from 'types'; +import { getAElf, getNodeByChainId, getWallet, isELFChain } from 'utils/aelfUtils'; +import { useAElf } from './web3'; +import { ELFChainConstants, ERCChainConstants } from 'constants/ChainConstants'; +import { isELFAddress, isTonChain, sleep } from 'utils'; +import { AElfDappBridge } from '@aelf-react/types'; +import { checkAElfBridge } from 'utils/checkAElfBridge'; +import { setContract } from 'contexts/useAElfContract/actions'; +import { useAElfContractContext } from 'contexts/useAElfContract'; +import { ContractBasic, PortkeySDKContractBasic } from 'utils/contract'; +import { WalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { useConnectWallet } from '@aelf-web-login/wallet-adapter-react'; +import { getContractBasic } from '@portkey/contracts'; +import { SupportedTONChain, WEB_LOGIN_CONFIG } from 'constants/index'; +import { IContract } from '@portkey/types'; +import { ExtraInfoForDiscover, WebLoginWalletInfo } from 'types/wallet'; +import { useGetAccount } from './wallet'; +import { SupportedELFChainId } from 'constants/chain'; +import { useTonConnectUI } from '@tonconnect/ui-react'; +import { getBridgeChainInfo } from 'utils/chain'; +import { getPortkeyWebWalletInfo } from 'utils/portkey'; +import detectProvider from '@portkey/detect-provider'; + +const ContractMap: { [key: string]: ContractBasic } = {}; + +export function getContract(address: string, ABI: any, chainId?: ChainId) { + return new ContractBasic({ + contractAddress: address, + contractABI: ABI, + chainId, + }); +} + +export const getPortkeyContract = async ( + contractAddress: string, + chainId: ChainId, + walletInfo: WebLoginWalletInfo, + walletType: WalletTypeEnum, +) => { + const key = `${contractAddress}_${chainId}_${walletInfo?.address}_${walletInfo}`; + if (walletType === WalletTypeEnum.discover) { + if (!ContractMap[key]) { + const portkeyDiscoverInfo = walletInfo?.extraInfo as ExtraInfoForDiscover; + if (!portkeyDiscoverInfo.provider) throw new Error('Portkey Provider undefined'); + const portkeyChain = await portkeyDiscoverInfo.provider.getChain(chainId as any); + const contract = new ContractBasic({ + contractAddress, + chainId, + portkeyChain, + }); + ContractMap[key] = contract; + } + return ContractMap[key]; + } else { + const portkeyAAInfo = getPortkeyWebWalletInfo(); + let sdkContract: undefined | IContract; + let caContract: undefined | IContract; + if (portkeyAAInfo) { + const portkeyConfig = WEB_LOGIN_CONFIG.portkeyV2; + sdkContract = await getContractBasic({ + chainType: 'aelf', + account: { address: portkeyAAInfo?.caAddress }, + contractAddress: contractAddress, + caContractAddress: portkeyConfig.caContractAddress[chainId as keyof typeof portkeyConfig.caContractAddress], + callType: 'ca', + caHash: portkeyAAInfo?.caHash || '', + rpcUrl: getNodeByChainId(chainId).rpcUrl, + }); + caContract = await getContractBasic({ + chainType: 'aelf', + account: { address: portkeyAAInfo?.caAddress }, + contractAddress: portkeyConfig.caContractAddress[chainId as keyof typeof portkeyConfig.caContractAddress], + callType: 'eoa', + rpcUrl: getNodeByChainId(chainId).rpcUrl, + }); + } + const viewInstance = chainId ? getAElf(chainId) : null; + const viewContract = await viewInstance?.chain.contractAt(contractAddress, getWallet()); + + const sdkContractBasic = new PortkeySDKContractBasic({ + sdkContract, + viewContract, + chainId, + contractAddress, + portkeyWallet: walletInfo, + caContract, + }); + return sdkContractBasic as unknown as ContractBasic; + } +}; + +export const getFairyVaultContract = async ( + contractAddress: string, + chainId: ChainId, + walletInfo: WebLoginWalletInfo, + walletType: WalletTypeEnum, +) => { + const key = `${contractAddress}_${chainId}_${walletInfo?.address}_${walletInfo}_${walletType}`; + if (!ContractMap[key]) { + const provider = await detectProvider({ providerName: 'FairyVault' }); + const fairyVaultChain = await provider?.getChain(chainId as any); + const contract = new ContractBasic({ + contractAddress, + chainId, + fairyVaultChain, + }); + ContractMap[key] = contract; + } + return ContractMap[key]; +}; + +export function useERCContract(address: string | undefined, ABI: any, chainId?: ChainId) { + return useMemo(() => { + if (!address || isELFChain(chainId) || isTonChain(chainId)) return undefined; + try { + return getContract(address, ABI, chainId); + } catch (error) { + console.log(error, '====useERCContract'); + return undefined; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ABI, address]); +} + +export async function getELFContract( + contractAddress: string, + aelfInstance?: AElfDappBridge, + account?: string, + chainId?: ChainId, +) { + const key = contractAddress + account + chainId + aelfInstance?.chainId || ''; + if (!ContractMap[key]) { + const viewInstance = chainId ? getAElf(chainId) : null; + const wallet = account ? { address: account } : getWallet(); + if (aelfInstance) await checkAElfBridge(aelfInstance); + const [viewContract, aelfContract] = await Promise.all([ + viewInstance?.chain.contractAt(contractAddress, getWallet()), + aelfInstance?.chain.contractAt(contractAddress, wallet), + ]); + + ContractMap[key] = new ContractBasic({ + aelfContract, + contractAddress, + chainId, + aelfInstance, + viewContract, + }); + } + return ContractMap[key]; +} + +export function useAElfContract(contractAddress: string, chainId?: ChainId) { + const { account, aelfInstances } = useAElf(); + const [contracts, { dispatch }] = useAElfContractContext(); + const aelfInstance = useMemo(() => aelfInstances?.[chainId as AelfInstancesKey], [aelfInstances, chainId]); + const key = useMemo(() => contractAddress + '_' + chainId + '_' + account, [account, chainId, contractAddress]); + const getContract = useCallback( + async (reCount = 0) => { + if (!chainId || !contractAddress || !isELFAddress(contractAddress)) return; + try { + const contract = await getELFContract(contractAddress, aelfInstance, account, chainId); + dispatch(setContract({ [key]: contract })); + } catch (error) { + await sleep(1000); + reCount++; + if (reCount < 20) { + getContract(reCount); + } else { + console.warn(error, reCount, '====getContract', contractAddress); + } + } + }, + [chainId, aelfInstance, contractAddress, account, dispatch, key], + ); + + useEffect(() => { + getContract(); + }, [getContract]); + + return useMemo(() => { + return contracts?.[key]; + }, [contracts, key]); +} + +export function useTonContract(contractAddress: string, chainId?: ChainId) { + const [tonConnectUI] = useTonConnectUI(); + + return useMemo(() => { + if (!isTonChain(chainId)) return; + return new ContractBasic({ contractAddress, chainId, tonConnectUI }); + }, [chainId, contractAddress, tonConnectUI]); +} + +export function usePortkeyContract(contractAddress: string, chainId?: SupportedELFChainId) { + const { walletType, walletInfo } = useConnectWallet(); + const accounts = useGetAccount(); + + const account: string = useMemo(() => { + if (!chainId) return ''; + return accounts?.[chainId] || ''; + }, [accounts, chainId]); + const [contracts, { dispatch }] = useAElfContractContext(); + const key = useMemo(() => `${contractAddress}_${chainId}_${account}`, [account, chainId, contractAddress]); + const getContract = useCallback( + async (reCount = 0) => { + if (!chainId || !isELFChain(chainId) || !isELFAddress(contractAddress)) return; + try { + dispatch( + setContract({ + [key]: await getPortkeyContract(contractAddress, chainId, walletInfo as WebLoginWalletInfo, walletType), + }), + ); + } catch (error) { + console.log(error, '====error-getContract'); + await sleep(1000); + reCount++; + if (reCount < 5) { + getContract(reCount); + } else { + console.warn(error, reCount, '====getContract', contractAddress); + } + } + }, + [chainId, contractAddress, dispatch, key, walletInfo, walletType], + ); + + useEffect(() => { + getContract(); + }, [getContract]); + + return useMemo(() => { + return contracts?.[key]; + }, [contracts, key]); +} + +export function useFairyVaultContract(contractAddress: string, chainId?: SupportedELFChainId) { + const { walletType, walletInfo } = useConnectWallet(); + const accounts = useGetAccount(); + const account: string = useMemo(() => { + if (!chainId) return ''; + return accounts?.[chainId] || ''; + }, [accounts, chainId]); + const [contracts, { dispatch }] = useAElfContractContext(); + const key = useMemo(() => `${contractAddress}_${chainId}_${account}_FairyVault`, [account, chainId, contractAddress]); + const getContract = useCallback( + async (reCount = 0) => { + if ( + !chainId || + !isELFChain(chainId) || + !isELFAddress(contractAddress) || + !walletInfo || + walletType !== WalletTypeEnum.fairyVault + ) + return; + try { + dispatch( + setContract({ + [key]: await getFairyVaultContract(contractAddress, chainId, walletInfo as WebLoginWalletInfo, walletType), + }), + ); + } catch (error) { + console.log(error, '====error-getContract-useFairyVaultContract'); + await sleep(1000); + reCount++; + if (reCount < 5) { + getContract(reCount); + } else { + console.warn(error, reCount, '====getContract-useFairyVaultContract', contractAddress); + } + } + }, + [chainId, contractAddress, dispatch, key, walletInfo, walletType], + ); + + useEffect(() => { + getContract(); + }, [getContract]); + + return useMemo(() => { + return contracts?.[key]; + }, [contracts, key]); +} + +function useContract(address: string, ABI: any, chainId?: ChainId, isPortkey?: boolean): ContractBasic | undefined { + const ercContract = useERCContract(address, ABI, chainId); + const elfContract = useAElfContract(address, chainId); + const tonContract = useTonContract(address, chainId); + const portkeyContract = usePortkeyContract(address, chainId as SupportedELFChainId); + const fairyVaultContract = useFairyVaultContract(address, chainId as SupportedELFChainId); + + const { walletType } = useConnectWallet(); + return useMemo(() => { + const isFairyVault = walletType === WalletTypeEnum.fairyVault; + if (isPortkey) return portkeyContract; + if (isTonChain(chainId)) return tonContract; + return isELFChain(chainId) ? (isFairyVault ? fairyVaultContract : elfContract) : ercContract; + }, [chainId, elfContract, ercContract, fairyVaultContract, isPortkey, portkeyContract, tonContract, walletType]); +} + +export function useTokenContract(chainId?: ChainId, address?: string, isPortkey?: boolean) { + const contractAddress = useMemo(() => { + if (isELFChain(chainId)) return ELFChainConstants.constants[chainId as AelfInstancesKey].TOKEN_CONTRACT; + return address; + }, [address, chainId]); + return useContract(contractAddress || '', ERC20_ABI, chainId, isPortkey); +} +export function useCrossChainContract(chainId?: ChainId, address?: string, isPortkey?: boolean) { + const contractAddress = useMemo(() => { + if (isELFChain(chainId)) return ELFChainConstants.constants[chainId as AelfInstancesKey].CROSS_CHAIN_CONTRACT; + return address; + }, [address, chainId]); + return useContract(contractAddress || '', ERC20_ABI, chainId, isPortkey); +} + +export function useBridgeContract(chainId?: ChainId, isPortkey?: boolean) { + const contractAddress = useMemo(() => { + if (isELFChain(chainId)) return ELFChainConstants.constants[chainId as AelfInstancesKey]?.BRIDGE_CONTRACT; + if (chainId && isTonChain(chainId)) return SupportedTONChain[chainId].BRIDGE_CONTRACT; + return ERCChainConstants.constants?.BRIDGE_CONTRACT; + }, [chainId]); + + return useContract(contractAddress || '', BRIDGE_IN_ABI, chainId, isPortkey); +} +export function useBridgeOutContract(chainId?: ChainId, isPortkey?: boolean) { + const contractAddress = useMemo(() => { + if (isELFChain(chainId)) return ELFChainConstants.constants[chainId as AelfInstancesKey]?.BRIDGE_CONTRACT; + return ERCChainConstants.constants?.BRIDGE_CONTRACT_OUT; + }, [chainId]); + return useContract(contractAddress || '', BRIDGE_OUT_ABI, chainId, isPortkey); +} + +export function useLimitContract(fromChainId?: ChainId, toChainId?: ChainId, symbol?: string) { + const contractAddress = useMemo(() => { + // TON chain limit contract is equal to token pool contract + if (fromChainId && isTonChain(fromChainId) && symbol) { + const fromChainInfo = getBridgeChainInfo(fromChainId); + return fromChainInfo?.TOKEN_POOL_MAP[symbol]; + } + return ERCChainConstants?.constants?.LIMIT_CONTRACT || ''; + }, [fromChainId, symbol]); + + return useContract(contractAddress, LIMIT_ABI, isELFChain(fromChainId) ? toChainId : fromChainId); +} + +export function useCreateTokenContract(chainId?: ChainId) { + const contractAddress = useMemo(() => { + if (isELFChain(chainId) || isTonChain(chainId)) return ''; + return ERCChainConstants.constants.CREATE_TOKEN_CONTRACT || ''; + }, [chainId]); + return useContract(contractAddress, CREATE_TOKEN_ABI, chainId, false); +} + +export function usePoolContract(chainId?: ChainId, address?: string, isPortkey?: boolean, symbol?: string) { + const contractAddress = useMemo(() => { + const chainInfo = getBridgeChainInfo(chainId); + // TON chain one token corresponds to one fund pool address + if (isTonChain(chainId) && symbol) return chainInfo?.TOKEN_POOL_MAP[symbol]; + return chainInfo?.TOKEN_POOL || ''; + }, [chainId, symbol]); + return useContract(address || contractAddress, POOLS_ABI, chainId, isPortkey); +} diff --git a/src/hooks/useCrossFee.ts b/src/hooks/useCrossFee.ts new file mode 100644 index 00000000..0532d13e --- /dev/null +++ b/src/hooks/useCrossFee.ts @@ -0,0 +1,40 @@ +import { CrossFeeTokenDecimals } from 'constants/misc'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { useCallback, useState } from 'react'; +import { isELFChain } from 'utils/aelfUtils'; +import { divDecimals } from 'utils/calculate'; +import { getChainIdToMap } from 'utils/chain'; +import { useBridgeContract } from './useContract'; +import useInterval from './useInterval'; +import { useReturnLastCallback } from 'hooks'; +import BigNumber from 'bignumber.js'; + +export function useCrossFee() { + const { fromWallet, toWallet } = useWallet(); + const { chainId: fromChainId } = fromWallet || {}; + const bridgeContract = useBridgeContract(fromChainId, fromWallet?.isPortkey); + const { chainId: toChainId } = toWallet || {}; + const [fee, setFee] = useState(); + const getFeeByChainId = useReturnLastCallback(async () => { + if (!bridgeContract || !(isELFChain(fromChainId) && !isELFChain(toChainId))) return undefined; + + const req = await bridgeContract.callViewMethod('GetFeeByChainId', [getChainIdToMap(toChainId)]); + + if (req && !req.error) { + return divDecimals(req.value, CrossFeeTokenDecimals).dp(2, BigNumber.ROUND_UP).toFixed(); + } + return undefined; + }, [bridgeContract, fromChainId, toChainId]); + + const refreshFeeByChainId = useCallback(async () => { + try { + const result = await getFeeByChainId(); + setFee(result); + } catch (error) { + console.log('refreshFeeByChainId error', error); + } + }, [getFeeByChainId]); + + useInterval(refreshFeeByChainId, [refreshFeeByChainId], 30000); + return fee; +} diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts new file mode 100644 index 00000000..588a0485 --- /dev/null +++ b/src/hooks/useDebounce.ts @@ -0,0 +1,22 @@ +import { useEffect, useState } from 'react'; + +// modified from https://usehooks.com/useDebounce/ +export default function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + // Update debounced value after delay + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + // Cancel the timeout if value changes (also on delay change or unmount) + // This is how we prevent debounced value from updating if value is changed ... + // .. within the delay period. Timeout gets cleared and restarted. + return () => { + clearTimeout(handler); + }; + }, [value, delay]); + + return debouncedValue; +} diff --git a/src/hooks/useGlobalLoading.ts b/src/hooks/useGlobalLoading.ts new file mode 100644 index 00000000..88e4c2d8 --- /dev/null +++ b/src/hooks/useGlobalLoading.ts @@ -0,0 +1,8 @@ +import { useCallback, useMemo } from 'react'; +import { emitLoading } from 'utils/events'; + +export default function useGlobalLoading() { + const _setGlobalLoading = useCallback((isLoading: boolean) => emitLoading(isLoading), []); + + return useMemo(() => ({ setGlobalLoading: _setGlobalLoading }), [_setGlobalLoading]); +} diff --git a/src/hooks/useInterval.ts b/src/hooks/useInterval.ts new file mode 100644 index 00000000..60cdc0cb --- /dev/null +++ b/src/hooks/useInterval.ts @@ -0,0 +1,20 @@ +import { DependencyList, useEffect, useRef } from 'react'; +import { useDeepCompareEffect } from 'react-use'; + +const useInterval = (callback: () => void, deps?: DependencyList, delay?: number | null): void => { + const savedCallback = useRef<() => void>(); + useEffect(() => { + savedCallback.current = callback; + }); + useDeepCompareEffect(() => { + savedCallback.current?.(); + if (delay !== null) { + const interval = setInterval(() => savedCallback.current?.(), delay || 0); + return () => clearInterval(interval); + } + return undefined; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [delay, ...(deps || [])]); +}; + +export default useInterval; diff --git a/src/hooks/useLoadingModal.tsx b/src/hooks/useLoadingModal.tsx new file mode 100644 index 00000000..80ed1fbd --- /dev/null +++ b/src/hooks/useLoadingModal.tsx @@ -0,0 +1,37 @@ +import LoadingModal, { ILoadingModalProps } from 'components/Loading/LoadingModal'; +import ResultModal, { IResultModalProps, ResultType } from 'components/Loading/ResultModal'; +import { useMemo } from 'react'; +import { useSetState } from 'react-use'; +const INIT_RESULT_MODAL_PROPS = { + open: false, + type: ResultType.APPROVED, + onRetry: undefined, +}; + +export type TLoadingModalParams = { + loadingModalProps?: ILoadingModalProps; + resultModalProps?: IResultModalProps; +}; + +export default function useLoadingModal(params?: TLoadingModalParams) { + const [loadingModalProps, setLoadingModal] = useSetState(); + const [resultModalProps, setResultModal] = useSetState(INIT_RESULT_MODAL_PROPS); + const modal = useMemo(() => { + return ( + <> + + { + setResultModal(INIT_RESULT_MODAL_PROPS); + resultModalProps.onClose?.(); + params?.resultModalProps?.onClose?.(); + }} + /> + + ); + }, [loadingModalProps, params?.loadingModalProps, params?.resultModalProps, resultModalProps, setResultModal]); + + return { loadingOpen: loadingModalProps.open, modal, setLoadingModal, setResultModal }; +} diff --git a/src/hooks/useLockCallback.ts b/src/hooks/useLockCallback.ts new file mode 100644 index 00000000..4b7c1c6d --- /dev/null +++ b/src/hooks/useLockCallback.ts @@ -0,0 +1,19 @@ +import { DependencyList, useCallback, useRef } from 'react'; + +// useLockCallback prevent concurrent execution +export default function useLockCallback any>(callback: T, deps: DependencyList) { + const lock = useRef(false); + return useCallback(async (...args: any) => { + if (lock.current) return; + lock.current = true; + try { + const req = await callback(...args); + lock.current = false; + return req; + } catch (e) { + lock.current = false; + throw e; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); +} diff --git a/src/hooks/useMaskQuery.ts b/src/hooks/useMaskQuery.ts new file mode 100644 index 00000000..ca99ceea --- /dev/null +++ b/src/hooks/useMaskQuery.ts @@ -0,0 +1,22 @@ +import { useEffect, useMemo, useState } from 'react'; +import { request } from 'api'; + +export default function useMaskQuery() { + const [isShowMask, setIsShowMask] = useState(true); + const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + async function fetchData() { + setIsLoading(true); + try { + const rs = await request.cms.getToggleResultOfMask(); + setIsShowMask(rs?.data?.isShowMask); + // eslint-disable-next-line no-empty + } catch (e) {} + setIsLoading(false); + } + fetchData(); + }, []); + return useMemo(() => { + return { isShowMask, isLoading }; + }, [isShowMask, isLoading]); +} diff --git a/src/hooks/useMediaQueries.ts b/src/hooks/useMediaQueries.ts new file mode 100644 index 00000000..6af8f71c --- /dev/null +++ b/src/hooks/useMediaQueries.ts @@ -0,0 +1,5 @@ +import { media } from 'constants/media'; +import { useMedia } from 'react-use'; +export default function useMediaQueries(key: keyof typeof media) { + return useMedia(media[key]); +} diff --git a/src/hooks/useStorageReducer.ts b/src/hooks/useStorageReducer.ts new file mode 100644 index 00000000..c1dea00a --- /dev/null +++ b/src/hooks/useStorageReducer.ts @@ -0,0 +1,35 @@ +import { Dispatch, Reducer, ReducerAction, ReducerState, useCallback, useMemo, useReducer } from 'react'; + +export type StorageOptions = { + key?: string; + blacklist?: string[]; + storage?: Storage; +}; + +export default function useStorageReducer>( + reducer: R, + initialState: ReducerState, + options?: StorageOptions, +): [ReducerState, Dispatch>] { + const { key, blacklist, storage = localStorage } = options || {}; + const initialStorageState = useMemo(() => { + if (!key) return null; + const state = storage.getItem(key); + if (!state) return state; + return JSON.parse(state); + }, [key, storage]); + const f: Reducer = useCallback( + (...rest) => { + const state = reducer(...rest); + const storageState = { ...state }; + if (blacklist) + blacklist.forEach((i) => { + delete storageState[i]; + }); + key && storage.setItem(key, JSON.stringify(storageState)); + return state; + }, + [reducer, blacklist, key, storage], + ); + return useReducer(f, initialStorageState || initialState); +} diff --git a/src/hooks/useUrlSearchState.ts b/src/hooks/useUrlSearchState.ts new file mode 100644 index 00000000..4a39d729 --- /dev/null +++ b/src/hooks/useUrlSearchState.ts @@ -0,0 +1,106 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { parse, stringify } from 'query-string'; +import type { ParseOptions, StringifyOptions } from 'query-string'; +import { useMemo, useRef, useCallback, SetStateAction, useState, useEffect } from 'react'; +import { useLocation } from 'react-use'; +import { useRouter } from 'next/router'; + +function getDefaultSearch(searchKeys?: string[]) { + if (!searchKeys) return; + + const obj: any = {}; + searchKeys.forEach((i) => { + obj[i] = undefined; + }); + return obj; +} +function getSearchObj(searchKeys?: string[], searchObj?: any) { + if (!searchKeys) return searchObj; + + const obj: any = {}; + searchKeys.forEach((i) => { + if (searchObj[i]) obj[i] = searchObj[i]; + }); + return obj; +} + +export interface Options { + navigateMode?: 'push' | 'replace'; + parseOptions?: ParseOptions; + stringifyOptions?: StringifyOptions; + searchKeys?: string[]; +} + +const baseParseConfig: ParseOptions = { + parseNumbers: false, + parseBooleans: false, + arrayFormat: 'index', +}; + +const baseStringifyConfig: StringifyOptions = { + skipNull: false, + skipEmptyString: false, + arrayFormat: 'index', +}; + +type UrlState = Record; + +export default function useUrlSearchState( + initialState?: S | (() => S), + options?: Options, +) { + type State = Partial<{ [key in keyof S]: any }>; + const { navigateMode = 'push', parseOptions, stringifyOptions, searchKeys } = options || {}; + + const mergedParseOptions = { ...baseParseConfig, ...parseOptions }; + const mergedStringifyOptions = { ...baseStringifyConfig, ...stringifyOptions }; + + const router = useRouter(); + const [, update] = useState({}); + const { search, hash } = useLocation(); + const hashRef = useRef(); + useEffect(() => { + hashRef.current = hash; + }); + const initialStateRef = useRef(typeof initialState === 'function' ? (initialState as () => S)() : initialState || {}); + + const queryFromUrl = useMemo(() => { + return parse(search ?? '', mergedParseOptions); + }, [search]); + const defaultSearch = useMemo(() => getDefaultSearch(searchKeys), [searchKeys]); + + const targetQuery: State = useMemo( + () => + getSearchObj(searchKeys, { + ...initialStateRef.current, + ...queryFromUrl, + }), + [queryFromUrl], + ); + + const setSearch = useCallback( + (search: string) => { + if (navigateMode === 'replace') { + router.replace({ hash: hashRef.current, search }); + } else { + router.push({ hash: hashRef.current, search }, undefined, { scroll: false }); + } + }, + [router], + ); + + const setState = useCallback( + (s: SetStateAction) => { + const newQuery = typeof s === 'function' ? s(targetQuery) : s; + setSearch(stringify({ ...queryFromUrl, ...(defaultSearch ?? {}), ...newQuery }, mergedStringifyOptions) || '?'); + update({}); + }, + [setSearch, queryFromUrl, targetQuery, defaultSearch], + ); + + const clear = useCallback(() => { + !defaultSearch ? setSearch('') : setState(defaultSearch); + }, [defaultSearch, setState]); + + return [targetQuery, setState, clear] as const; +} diff --git a/src/hooks/wallet.ts b/src/hooks/wallet.ts new file mode 100644 index 00000000..ec373914 --- /dev/null +++ b/src/hooks/wallet.ts @@ -0,0 +1,282 @@ +import { WalletTypeEnum as AelfWalletTypeEnum, TChainId, WalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { did } from '@portkey/did'; +import { useConnectWallet } from '@aelf-web-login/wallet-adapter-react'; +import { ChainId, MethodsWallet } from '@portkey/provider-types'; +import { GetCAHolderByManagerParams } from '@portkey/services'; +import CommonMessage from 'components/CommonMessage'; +import { MAIN_SIDE_CHAIN_ID } from 'constants/index'; +import { useChainDispatch } from 'contexts/useChain'; +import { setSelectELFWallet, setSelectERCWallet } from 'contexts/useChain/actions'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { ExtraInfoForDiscover, TAelfAccounts, WebLoginWalletInfo } from 'types/wallet'; +import { handleWebLoginErrorMessage } from 'utils/error'; +import { clearWCStorageByDisconnect } from 'utils/localStorage'; +import { useAelfAuthToken } from './aelfAuthToken'; +import { AuthTokenSource, removeOneLocalJWT, resetLocalJWT } from 'utils/aelfAuthToken'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; +import { pubKeyToAddress } from 'utils/aelfUtils'; +import { eBridgeInstance } from 'utils/eBridgeInstance'; +import useGlobalLoading from 'hooks/useGlobalLoading'; +import { ROUTE_PATHS } from 'constants/link'; +import { useRouter } from 'next/router'; +import { getPortkeyWebWalletInfo } from 'utils/portkey'; +import { AElfConnectorId } from 'types'; + +export function useInitWallet() { + const chainDispatch = useChainDispatch(); + const { connectWallet, walletType } = useConnectWallet(); + const connectWalletRef = useRef(connectWallet); + connectWalletRef.current = connectWallet; + const isLogin = useIsAelfLogin(); + + const init = useCallback(async () => { + switch (walletType) { + case AelfWalletTypeEnum.elf: + chainDispatch(setSelectELFWallet(AElfConnectorId.NIGHTELF)); + break; + case AelfWalletTypeEnum.web: + case AelfWalletTypeEnum.discover: + chainDispatch(setSelectELFWallet(AElfConnectorId.PORTKEY)); + break; + case AelfWalletTypeEnum.fairyVault: + chainDispatch(setSelectELFWallet(AElfConnectorId.FAIRY_VAULT)); + break; + default: + chainDispatch(setSelectELFWallet(undefined)); + break; + } + }, [chainDispatch, walletType]); + + const onLogoutClearData = useCallback(async () => { + chainDispatch(setSelectERCWallet(undefined)); + clearWCStorageByDisconnect(); + // await sleep(500); + // connectWalletRef.current(); + }, [chainDispatch]); + + useEffect(() => { + if (!isLogin) { + if (localStorage.getItem('connectedWallet') !== AelfWalletTypeEnum.web) { + onLogoutClearData(); + } + } else { + init(); + } + }, [init, isLogin, onLogoutClearData, walletType]); +} + +export function useAelfAuthListener() { + const { setGlobalLoading } = useGlobalLoading(); + const { isConnected, walletInfo, walletType } = useConnectWallet(); + + const { queryAuth } = useAelfAuthToken(); + + const onAuthorizationExpired = useCallback(async () => { + if (!isConnected) { + console.warn('AuthorizationExpired: Not Logined'); + eBridgeInstance.setUnauthorized(false); + return; + } else if (isConnected && walletInfo) { + const { caHash } = await getCaHashAndOriginChainIdByWallet(walletInfo as WebLoginWalletInfo, walletType); + const managerAddress = await getManagerAddressByWallet(walletInfo as WebLoginWalletInfo, walletType); + const source = walletType === AelfWalletTypeEnum.elf ? AuthTokenSource.NightElf : AuthTokenSource.Portkey; + const key = (caHash || source) + managerAddress; + removeOneLocalJWT(key); + + console.log('AuthorizationExpired'); + eBridgeInstance.setUnauthorized(true); + await queryAuth(false, true); + } else { + eBridgeInstance.setUnauthorized(false); + } + setGlobalLoading(false); + }, [isConnected, queryAuth, setGlobalLoading, walletInfo, walletType]); + const onAuthorizationExpiredRef = useRef(onAuthorizationExpired); + onAuthorizationExpiredRef.current = onAuthorizationExpired; + + useEffect(() => { + const { remove } = eBridgeEventBus.Unauthorized.addListener(() => { + console.log('Unauthorized listener', eBridgeInstance.unauthorized); + if (eBridgeInstance.unauthorized) return; + eBridgeInstance.setUnauthorized(true); + onAuthorizationExpiredRef.current?.(); + }); + return () => { + remove(); + }; + }, []); +} + +export function useIsAelfLogin() { + const { isConnected, walletInfo } = useConnectWallet(); + return useMemo(() => isConnected && !!walletInfo, [isConnected, walletInfo]); +} + +export function useAelfLogin() { + const { setGlobalLoading } = useGlobalLoading(); + const router = useRouter(); + const { connectWallet } = useConnectWallet(); + const isLogin = useIsAelfLogin(); + const isLoginRef = useRef(isLogin); + isLoginRef.current = isLogin; + + const { getAuth } = useAelfAuthToken(); + const getAuthRef = useRef(getAuth); + getAuthRef.current = getAuth; + + return useCallback( + async (isNeedGetJWT = false, handleConnectedCallback?: () => Promise | void, isStopLoading = false) => { + try { + const _isNeedGetJWT = + router.asPath?.includes(ROUTE_PATHS.LISTING_APPLICATION) || + router.asPath?.includes(ROUTE_PATHS.MY_APPLICATIONS) || + isNeedGetJWT; + + if (isLoginRef.current) { + if (_isNeedGetJWT) { + await getAuthRef.current(true, false); + } + await handleConnectedCallback?.(); + return; + } + + if (isStopLoading) { + setGlobalLoading(false); + } + await connectWallet(); + if (_isNeedGetJWT) { + setGlobalLoading(true); + await getAuthRef.current(true, false); + } + await handleConnectedCallback?.(); + } catch (error) { + CommonMessage.error(handleWebLoginErrorMessage(error)); + } finally { + setGlobalLoading(false); + } + }, + [connectWallet, router.asPath, setGlobalLoading], + ); +} + +export function useGetAccount() { + const { walletInfo } = useConnectWallet(); + const isLogin = useIsAelfLogin(); + + // WebLoginWalletInfo TAelfAccounts ExtraInfoForDiscover | ExtraInfoForPortkeyAA | ExtraInfoForNightElf; + return useMemo(() => { + if (!isLogin) return undefined; + + const accounts: TAelfAccounts = { + [MAIN_SIDE_CHAIN_ID.mainChain]: 'ELF_' + walletInfo?.address + '_' + MAIN_SIDE_CHAIN_ID.mainChain, + [MAIN_SIDE_CHAIN_ID.sideChain]: 'ELF_' + walletInfo?.address + '_' + MAIN_SIDE_CHAIN_ID.sideChain, + }; + + return accounts; + }, [isLogin, walletInfo]); +} + +export function useAelfLogout() { + const chainDispatch = useChainDispatch(); + const { disConnectWallet, walletType } = useConnectWallet(); + return useCallback(async () => { + try { + const req = await disConnectWallet(); + if (walletType === WalletTypeEnum.web && !req) return; + eBridgeEventBus.AelfLogoutSuccess.emit(); + chainDispatch(setSelectERCWallet(undefined)); + clearWCStorageByDisconnect(); + resetLocalJWT(); // only remove aelf token + } catch (error) { + console.log(error, '====error-disConnectWallet'); + } + }, [chainDispatch, disConnectWallet, walletType]); +} + +export function useGetWalletManagerStatus() { + const { walletType, walletInfo } = useConnectWallet(); + + return useCallback( + async (chainId: ChainId) => { + if (walletType === AelfWalletTypeEnum.elf) return true; + if (walletType === AelfWalletTypeEnum.discover) { + const discoverInfo = walletInfo?.extraInfo as ExtraInfoForDiscover; + const provider = discoverInfo.provider; + if (!provider) { + return false; + } + return provider.request({ + method: MethodsWallet.GET_WALLET_MANAGER_SYNC_STATUS, + payload: { chainId }, + }); + } + // portkey sdk + const portkeyWebWalletInfo = getPortkeyWebWalletInfo(); + + return did.checkManagerIsExist({ + chainId, + caHash: portkeyWebWalletInfo?.caHash || '', + managementAddress: portkeyWebWalletInfo?.managerAddress || '', + }); + }, + [walletInfo?.extraInfo, walletType], + ); +} + +export const getManagerAddressByWallet = async ( + walletInfo: WebLoginWalletInfo, + walletType: AelfWalletTypeEnum, + pubkey?: string, +): Promise => { + if (walletType === AelfWalletTypeEnum.unknown) return ''; + + let managerAddress; + if (walletType === AelfWalletTypeEnum.discover) { + const discoverInfo = walletInfo?.extraInfo as ExtraInfoForDiscover; + managerAddress = await discoverInfo?.provider?.request({ + method: 'wallet_getCurrentManagerAddress', + }); + } else if (walletType === AelfWalletTypeEnum.web) { + const portkeyWebWalletInfo = getPortkeyWebWalletInfo(); + managerAddress = portkeyWebWalletInfo?.managerAddress; + } else { + // AelfWalletTypeEnum.elf + managerAddress = walletInfo.address; + } + + if (!managerAddress && pubkey) { + managerAddress = pubKeyToAddress(pubkey); + } + + return managerAddress || ''; +}; + +export const getCaHashAndOriginChainIdByWallet = async ( + walletInfo: WebLoginWalletInfo, + walletType: AelfWalletTypeEnum, +): Promise<{ caHash: string; originChainId: TChainId }> => { + if (walletType === AelfWalletTypeEnum.unknown) + return { + caHash: '', + originChainId: MAIN_SIDE_CHAIN_ID.sideChain, + }; + + let caHash, originChainId; + if (walletType === AelfWalletTypeEnum.discover) { + const res = await did.services.getHolderInfoByManager({ + caAddresses: [walletInfo?.address], + } as unknown as GetCAHolderByManagerParams); + const caInfo = res[0]; + caHash = caInfo?.caHash; + originChainId = caInfo?.chainId as TChainId; + } else if (walletType === AelfWalletTypeEnum.web) { + const portkeyWebWalletInfo = getPortkeyWebWalletInfo(); + caHash = portkeyWebWalletInfo?.caHash; + originChainId = portkeyWebWalletInfo?.originChainId as TChainId; + } + + return { + caHash: caHash || '', + originChainId: originChainId || MAIN_SIDE_CHAIN_ID.sideChain, + }; +}; diff --git a/src/hooks/web3.ts b/src/hooks/web3.ts new file mode 100644 index 00000000..40f6be53 --- /dev/null +++ b/src/hooks/web3.ts @@ -0,0 +1,241 @@ +import { useAccount, useConnect, useDisconnect } from 'wagmi'; +import { ZERO } from 'constants/misc'; +import { useCallback, useMemo } from 'react'; +import { getProvider } from 'utils/provider'; +import { AElfConnectorId, ChainId, EVMConnectorId, TONConnectorId, Web3Type } from 'types'; +import { useChain, useChainDispatch } from 'contexts/useChain'; +import { ACTIVE_CHAIN, DEFAULT_ERC_CHAIN, IS_MAINNET } from 'constants/index'; +import { setSelectELFWallet } from 'contexts/useChain/actions'; +import { useConnectWallet } from '@aelf-web-login/wallet-adapter-react'; +import { ExtraInfoForDiscover, ExtraInfoForNightElf, ExtraInfoForPortkeyAA } from 'types/wallet'; +import { useAelfLogin } from './wallet'; +import { WalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { getPortkeySDKAccount } from 'utils/wallet'; +import { useTonWallet } from '@tonconnect/ui-react'; +import { toUserFriendlyAddress } from '@tonconnect/sdk'; +import { SupportedChainId, SupportedTONChainId } from 'constants/chain'; +import { isELFChain } from 'utils/aelfUtils'; +import { isTonChain } from 'utils'; +import { getNetworkInfo, switchChain } from 'utils/network'; +import { useLatestRef } from 'hooks'; + +export function useAElfConnect() { + const login = useAelfLogin(); + const chainDispatch = useChainDispatch(); + + return useCallback(async () => { + await login(); + chainDispatch(setSelectELFWallet('NIGHTELF')); + }, [chainDispatch, login]); +} + +export function usePortkeyConnect() { + const login = useAelfLogin(); + const chainDispatch = useChainDispatch(); + return useCallback(async () => { + await login(); + chainDispatch(setSelectELFWallet('PORTKEY')); + }, [chainDispatch, login]); +} +// useActiveWeb3React contains all attributes of useWeb3React and aelf combination +export function useWeb3(): Web3Type { + const accountInfo = useAccount(); + const { disconnectAsync } = useDisconnect(); + const [{ userERCChainId }] = useChain(); + + const tmpContext = useMemo(() => { + const contextNetwork: Web3Type = { + ...accountInfo, + connector: accountInfo.connector, + connectorId: accountInfo.connector?.id as EVMConnectorId, + chainId: accountInfo.chainId, + isActive: accountInfo.isConnected, + isPortkey: false, + walletType: 'ERC', + loginWalletType: WalletTypeEnum.unknown, + account: accountInfo.address, + accounts: accountInfo.addresses, + deactivate: disconnectAsync, + }; + if (!accountInfo.address) { + if (typeof window === 'object') { + contextNetwork.chainId = DEFAULT_ERC_CHAIN; + const chainId = ZERO.plus(window.ethereum?.chainId ?? ''); + if (!chainId.isNaN() && contextNetwork.account) { + contextNetwork.chainId = chainId.toNumber(); + } else if (userERCChainId) { + contextNetwork.chainId = userERCChainId; + } + } + + // Keep the previous logic and delete it if it is useless after observing it for a while + contextNetwork.getProvider = async (chainId?: SupportedChainId) => { + const _chainId = typeof chainId !== 'undefined' ? chainId : contextNetwork.chainId; + + const provider = getProvider(_chainId); + return provider; + }; + return contextNetwork; + } else { + contextNetwork.getProvider = async (chainId?: SupportedChainId) => { + const _chainId = typeof chainId !== 'undefined' ? chainId : accountInfo.chainId; + + const provider = await accountInfo.connector?.getProvider?.({ chainId: _chainId }); + return provider; + }; + } + + return contextNetwork; + }, [accountInfo, disconnectAsync, userERCChainId]); + + return tmpContext; +} + +const PORTKEY_TYPE = ['PortkeyDiscover', 'PortkeyAA', 'PortkeyWebWallet']; + +// useActiveWeb3React contains all attributes of useWeb3React and aelf combination +export function useAElf(): Web3Type { + const { walletInfo, walletType, isConnected } = useConnectWallet(); + const [{ userELFChainId }] = useChain(); + const chainId = userELFChainId; + const tmpContext = useMemo(() => { + const aelfWalletInfo = walletInfo?.extraInfo as ExtraInfoForNightElf; + const aelfBridges = aelfWalletInfo?.nightElfInfo?.aelfBridges as Web3Type['aelfInstances']; + + const contextNetwork: any = { + ...walletInfo, + aelfInstance: aelfWalletInfo?.nightElfInfo?.defaultAElfBridge, + aelfInstances: aelfBridges, + }; + if (chainId && ACTIVE_CHAIN[chainId] && aelfBridges) { + contextNetwork.aelfInstance = aelfBridges[chainId as keyof typeof aelfBridges]; + } + const isPortkey = PORTKEY_TYPE.includes(walletType) ? true : false; + const isFairyVault = walletType === WalletTypeEnum.fairyVault; + + const _walletType = isPortkey ? 'PORTKEY' : isFairyVault ? 'FAIRY_VAULT' : 'NIGHTELF'; + return { + ...contextNetwork, + account: walletInfo?.address, + isActive: isConnected && !!walletInfo, + chainId, + loginWalletType: walletType, + walletType: _walletType, + connector: walletInfo?.address ? _walletType : undefined, + connectorId: isPortkey + ? AElfConnectorId.PORTKEY + : isFairyVault + ? AElfConnectorId.FAIRY_VAULT + : AElfConnectorId.NIGHTELF, + isPortkey, + }; + }, [chainId, isConnected, walletInfo, walletType]); + return tmpContext; +} + +export function usePortkey(): Web3Type { + const { walletInfo, walletType, isConnected } = useConnectWallet(); + const tmpContext = useMemo(() => { + const contextNetwork: any = { + ...walletInfo, + }; + + const _walletAAInfo = walletInfo?.extraInfo as ExtraInfoForPortkeyAA; + const _walletDiscoverInfo = walletInfo?.extraInfo as ExtraInfoForDiscover; + + let _accounts; + + switch (walletType) { + case WalletTypeEnum.web: + // sdk login + _accounts = (_walletAAInfo as any)?.accounts; + break; + case WalletTypeEnum.discover: + _accounts = _walletDiscoverInfo.accounts; + break; + default: + break; + } + return { + ...contextNetwork, + accounts: _accounts, + wallet: { ...contextNetwork }, + isActive: isConnected && !!walletInfo, + loginWalletType: walletType, + walletType: 'PORTKEY', + connector: 'PORTKEY', + connectorId: AElfConnectorId.PORTKEY, + isPortkey: true, + }; + }, [isConnected, walletInfo, walletType]); + + return tmpContext; +} + +export function useTon(): Web3Type { + const wallet = useTonWallet(); + return useMemo(() => { + return { + ...wallet, + account: wallet?.account.address ? toUserFriendlyAddress(wallet?.account.address, !IS_MAINNET) : undefined, + wallet: { ...wallet }, + isActive: !!wallet?.account, + loginWalletType: undefined, + walletType: 'TON', + connector: 'TON', + connectorId: TONConnectorId.TON, + isTON: true, + chainId: IS_MAINNET ? SupportedTONChainId.MAINNET : SupportedTONChainId.TESTNET, + baseAccount: wallet?.account, + } as any; + }, [wallet]); +} + +export function useWeb3Wallet(chainId?: ChainId) { + const tonWallet = useTon(); + const aelfWallet = useAElf(); + const web3Wallet = useWeb3(); + return useMemo(() => { + if (isELFChain(chainId)) return aelfWallet; + if (isTonChain(chainId)) return tonWallet; + return web3Wallet; + }, [aelfWallet, chainId, tonWallet, web3Wallet]); +} + +export function useEVMSwitchChain() { + const web3 = useWeb3(); + const latestWeb3Ref = useLatestRef(web3); + return useCallback( + (chainId: ChainId) => { + if (latestWeb3Ref.current.chainId === chainId) return; + // Whether the switch is successful or not does not affect the link status + const info = getNetworkInfo(chainId); + if (!info) throw new Error('Invalid chainId'); + return switchChain(info.info, latestWeb3Ref.current.connector, true); + }, + [latestWeb3Ref], + ); +} + +export function useActiveAddresses() { + // const { account: tonAccount } = useTon(); + const { account: aelfAccount } = useAElf(); + const { account: evmAccount } = useWeb3(); + return useMemo(() => { + return [evmAccount, aelfAccount].filter(Boolean).join(','); + }, [aelfAccount, evmAccount]); +} + +export function useEVMConnectWallet() { + const { connectAsync, connectors } = useConnect(); + + return useCallback( + async (connectorId: EVMConnectorId, chainId?: number) => { + const connector = connectors.find((item) => item.id === connectorId); + if (!connector) return; + + await connectAsync({ connector: connector, chainId }); + }, + [connectAsync, connectors], + ); +} diff --git a/src/hooks/whitelist.ts b/src/hooks/whitelist.ts new file mode 100644 index 00000000..df282f3c --- /dev/null +++ b/src/hooks/whitelist.ts @@ -0,0 +1,61 @@ +import { useWhitelist } from 'contexts/useWhitelist'; +import { WhitelistItem } from 'contexts/useWhitelist/actions'; +import { useMemo } from 'react'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { isSymbol } from 'utils/reg'; +export type CurrentWhitelistItem = { + symbol: string; +} & WhitelistItem; + +export function useActiveWhitelist() { + const [{ whitelistMap, defaultWhitelistMap }] = useWhitelist(); + return useMemo(() => { + return Object.assign({}, defaultWhitelistMap, whitelistMap); + }, [defaultWhitelistMap, whitelistMap]); +} + +export function useAllWhitelist() { + const whitelistMap = useActiveWhitelist(); + return useMemo(() => { + const list: CurrentWhitelistItem[] = Object.entries(whitelistMap).map(([k, v]) => ({ + symbol: k, + ...v, + })); + return list; + }, [whitelistMap]); +} + +export function useCurrentWhitelist() { + const allList = useAllWhitelist(); + const { fromWallet, toWallet } = useWallet(); + const fromChainId = fromWallet?.chainId; + const toChainId = toWallet?.chainId; + return useMemo(() => { + if (!allList || !toChainId || !fromChainId) return []; + return allList?.filter((item) => { + const fromItem = item?.[fromChainId]; + const toItem = item?.[toChainId]; + const canForm = fromItem && !fromItem?.onlyTo; + const canTo = toItem && !toItem?.onlyFrom; + return canForm && canTo; + }); + }, [allList, fromChainId, toChainId]); +} + +export function useUserAddedToken(symbol?: string): CurrentWhitelistItem | false { + const activeWhitelist = useActiveWhitelist(); + const { fromWallet, toWallet } = useWallet(); + const fromChainId = fromWallet?.chainId; + const toChainId = toWallet?.chainId; + return useMemo(() => { + if (!symbol || !isSymbol(symbol) || !toChainId || !fromChainId) return false; + const info = activeWhitelist?.[symbol.toUpperCase()]; + const tokenSymbol = info?.[fromChainId]?.symbol; + if (tokenSymbol && info?.[toChainId]?.symbol) + return { + symbol: tokenSymbol, + ...info, + }; + return false; + }, [activeWhitelist, fromChainId, symbol, toChainId]); +} diff --git a/src/i18n/config.ts b/src/i18n/config.ts new file mode 100644 index 00000000..1832c972 --- /dev/null +++ b/src/i18n/config.ts @@ -0,0 +1,21 @@ +import enUS from 'antd/lib/locale/en_US'; +import zh_HK from 'antd/lib/locale/zh_HK'; +const LANGUAGE = 'I18N_LANGUAGE'; +const LOCAL_LANGUAGE = [ + { language: 'en', title: 'English' }, + { language: 'zh', title: '繁體中文' }, +]; +const LOCAL_LANGUAGE_LIST = LOCAL_LANGUAGE.map((i) => i.language); +const DEFAULT_LANGUAGE = LOCAL_LANGUAGE_LIST[0]; + +const getLocalLanguage = () => { + let lang = navigator.language; + if (typeof lang === 'string') lang = lang.slice(0, 2); + + return lang; +}; +const ANTD_LOCAL: { [key: string]: any } = { + zh: zh_HK, + en: enUS, +}; +export { LANGUAGE, LOCAL_LANGUAGE, LOCAL_LANGUAGE_LIST, getLocalLanguage, ANTD_LOCAL, DEFAULT_LANGUAGE }; diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 00000000..45fc7d9a --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,58 @@ +import i18n from 'i18next'; +import { useCallback, useMemo } from 'react'; +import { initReactI18next, useTranslation } from 'react-i18next'; + +import en from './languages/en.json'; +import zh from './languages/zh.json'; +import { LANGUAGE, LOCAL_LANGUAGE_LIST, DEFAULT_LANGUAGE } from './config'; +import moment from 'moment'; +// import 'moment/locale/zh-cn'; +import 'moment/locale/zh-hk'; +const resources = { en, zh }; +export function initLanguage(localStorage?: Storage) { + let lng = DEFAULT_LANGUAGE; + + // Get whether the language has been set locally + const v = localStorage?.getItem(LANGUAGE); + if (v && LOCAL_LANGUAGE_LIST.includes(v)) { + lng = v; + } + // TODO: browser language + // //Get browser language + // else if (getLocalLanguage() === 'zh') { + // lng = LOCAL_LANGUAGE_LIST[1]; + // } + + i18n + .use(initReactI18next) // passes i18n down to react-i18next + .init({ + resources, + lng, + + keySeparator: false, // we do not use keys in form messages.welcome + + interpolation: { + escapeValue: false, // react already safes from xss + }, + }); + moment.locale(lng.replace('_', '-')); +} +export function useLanguage() { + const { i18n, t } = useTranslation(); + const changeLanguage = useCallback( + (value: string) => { + if (i18n.language !== value && LOCAL_LANGUAGE_LIST.includes(value)) { + if (value === 'zh') { + moment.locale('zh-hk'); + } else { + moment.locale(value); + } + i18n.changeLanguage(value); + localStorage.setItem(LANGUAGE, value); + } + }, + [i18n], + ); + return useMemo(() => ({ language: i18n.language, changeLanguage, t }), [changeLanguage, i18n.language, t]); +} +export default i18n; diff --git a/src/i18n/languages/en.json b/src/i18n/languages/en.json new file mode 100644 index 00000000..a21dd44a --- /dev/null +++ b/src/i18n/languages/en.json @@ -0,0 +1,226 @@ +{ + "translation": { + "eBridge: Cross-chain Bridge": "eBridge: Cross-chain Bridge", + "Meta description": "Web3 cross-chain bridge that facilitates seamless token transfer between the aelf blockchain and Ethereum Virtual Machine (EVM) compatible networks.", + "Token Bridge": "Token Bridge", + "Connect": "Connect", + "Connect Wallet": "Connect Wallet", + "Connect aelf wallet to view transaction history": "Connect aelf wallet to view transaction history", + "Heterogeneous Chain Cross-Chain History": "External", + "Homogeneous Chain Cross-Chain History": "Internal", + "Select a Receipt ID": "Select a Receipt ID", + "From": "From", + "To": "To", + "From Chain": "From", + "To Chain": "To", + "Transaction Status": "Transaction Status", + "Amount Sent": "Amount Sent", + "Amount": "Amount", + "Amount Received": "Amount Received", + "From Address": "From", + "To Address": "To", + "Sent At": "Sent At", + "Receive At": "Receive At", + "Sending TXID": "Sending TXID", + "Receiving TXID": "Receiving TXID", + "Transferred": "Processing", + "Indexed": "Pending", + "Received": "Completed", + "Estimated transaction fee": "Estimated transaction fee", + "Grant permission to access your funds?": "Grant permission to access your funds?", + "Confirm": "Confirm", + "No cross-chain records found.": "No cross-chain records found.", + "Transfer": "Transfer", + "Heterogeneous chain cross-chain": "External: ", + "cross-chain transfer between aelf and other public chains (e.g. aelf to Ethereum)": "cross-chain transfer between aelf and other public chains (e.g. MainChain AELF to Ethereum).", + "Homogeneous chain cross-chain": "Internal:", + "Please note": "Please note:", + "When the network is not congested": "When the network is not congested:", + "The minimum crosschain transaction fee is around": "The minimum crosschain transaction fee is around", + "The minimum crosschain amount is": "The minimum crosschain amount is {{amount}} {{symbol}}.", + "The minimum crosschain amount is2": "The minimum crosschain amount is ", + "Estimated time of heterogenous cross-chain arrival is": "Estimated time of heterogenous cross-chain arrival is {{time}}", + "Estimated time of homogeneous cross-chain arrival is": "The estimated time of internal cross-chain transaction is {{time}}", + "Estimated time of homogeneous cross-chain arrival is2": "The estimated time of internal cross-chain transaction is {{time}} minutes.", + "Your transfer will take about a few minutes to complete": "Your transfer will take about {{time}} minutes to complete.", + "Cross Chain time": "Estimated time of heterogenous cross-chain arrival is {{heterogeneous}}", + "You pay fees for the fromChain transaction. We're covering the fees on aelf for you": "You'll only pay fees for the {{fromChain}} part of the transaction. We're covering the fees on aelf for you.", + "Tokens will arrive automatically after being sent": "Please allow some time for tokens to be shown automatically in your wallet.", + "Once finished, You can verify this on the blockchain explorer and wallet": "Once finished, the tokens will appear automatically in the recipient's wallet. You can verify this on the blockchain explorer.", + "Estimated time of sending tokens is time. Please select Receipt ID and receive the token(s) transferred": "Estimated time of arrival is {{time}} minutes.", + "Once the token is sent cross-chain": "Once the token is sent cross-chain, the recipient needs to visit eBridge, connect the \"To\" wallet, and select the \"Receipt ID\" to receive the token. This will incur transaction fees on {{toChain}} so the recipient should ensure there's a sufficient balance to pay for the fees.", + "Token not received?": "Token not received?", + "Initiate cross-chain transfer to the recipient's address": "Enter recipient address and send (no need to connect the \"To\" wallet)", + "Tips": "Tips", + "Connect the recipient wallet": "Connect the recipient wallet", + "Select a Receipt ID2": "Select a Receipt ID", + "Click the button to receive the token(s) transferred": "Click the button to receive the token(s) transferred", + "Cross-chain transaction initiated": "Cross-chain transaction initiated", + "Receive": "Receive", + "Receiving request submitted": "Receiving request submitted", + "Can't find your token": "Can't find your token? ", + "Import custom tokens": "Import custom tokens", + "Import a custom token": "Import a custom token", + "Please enter": "Please enter", + "Confirm2": "Confirm", + "Import": "Import", + "Custom token imported": "Custom token imported", + "Cross-chain transaction initiated2": "Cross-chain transaction initiated", + "Balance": "Balance: ", + "Added": "Added", + "records in total": "Total {{total}} items", + "Select a token": "Select a token", + "Insufficient balance": "Insufficient balance", + "Synchronization request has been submitted": "Synchronization request has been submitted", + "Transaction is being processed, please don't close this window": "Transaction is being processed, please don't close this window.", + "Create a token": "Create a token", + "To add a new token, you need to initiate 2 transactions here to create the token on": "To add a new token, you need to initiate 2 transactions here to create the token on {{chain}}", + "Transaction": "Transaction", + "This token does not exist on": "This token does not exist on {{chain}}", + "Create Tip": "Estimated duration: {{time}}, estimated transaction fee: {{fee}}.", + "Click the button below to proceed": "Click the button below to proceed.", + "Initiate Synchronization": "Initiate Synchronization", + "Synchronize token info": "Synchronize token info", + "Copy Success": "Copy Success", + "Transaction Id": "Transaction ID", + "Verify info": "Verify info", + "Transaction request submitted": "Transaction request submitted", + "Transaction request failed": "Transaction request failed", + "Custom token does not exist": "Custom token does not exist.", + "Connect2": "Connect ", + "to view transaction history": " to view transaction history", + "aelf Wallet": "aelf wallet", + "wallet": "wallet", + "Request submitted. Please synchronize the token info": "Request submitted. Please synchronize the token info.", + "The amount to be transferred should be smaller than": "Transaction request failed. The amount to be transferred should be smaller than {{amount}} {{symbol}}.", + "record does not exist": "record does not exist", + "Approve": "Approve", + "Approve symbol": "Approve {{symbol}}", + "Invalid to chain": "Invalid to chain", + "Invalid from chain": "Invalid from chain", + "Please confirm the recipient's address": "Please confirm the recipient's address", + "Confirm3": "Confirm", + "Cancel": "Cancel", + "Notification Title": "eBridge Alpha", + "Notification Content Section 1": "This is an ALPHA version of eBridge. As such, delays may occur, and catastrophic bugs may lurk. Thanks, for trying it at this early stage.", + "Notification Content Section 2": "For any issues, join our Telegram group for support: ", + "Upgrade Title": "Service Upgrade Notice", + "Upgrade Notice": { + "section 1": "Dear eBridge User,", + "section 2": "We are currently upgrading our system to serve you better. During this period, the eBridge service is temporarily unavailable. We kindly advise you to refrain from using the service at this moment to avoid any potential inconveniences or losses.", + "section 3":"The service is expected to resume by May 15, 2025, at 12:00 PM (UTC).", + "section 4": "We appreciate your understanding and patience and look forward to providing you with an improved eBridge experience soon.", + "section 5": ["Thank you for your support!", "The eBridge Team"] + }, + "Please Notice": "Notice", + "have reached the daily limit": "{{tokenSymbol}} transfers from {{fromChain}} to {{toChain}} have reached the daily limit, which will reset at 00:00 (UTC) each day. Please try again after the reset.", + "have a daily limit and your current transaction": "{{tokenSymbol}} transfers from {{fromChain}} to {{toChain}} have a daily limit and your current transaction exceeds the remaining quota of {{amount}} {{tokenSymbol}}. The limit will reset at 00:00 (UTC) each day. Please try again after the reset or consider transferring a smaller amount.", + "have a maximum capacity and your current transaction exceeds the available capacity": "{{tokenSymbol}} transfers from {{fromChain}} to {{toChain}} have a maximum capacity and your current transaction exceeds the available capacity of {{amount}} {{tokenSymbol}}. The capacity will be refilled in {{time}} minutes. Please try again after that.", + "Your current transaction exceeds the capacity and can't be processed": "{{tokenSymbol}} transfers from {{fromChain}} to {{toChain}} have a maximum capacity of {{amount}} {{tokenSymbol}}. Your current transaction exceeds the capacity and can't be processed. Please consider transferring a smaller amount.", + "Insufficient liquidity tip": "{{tokenSymbol}} liquidity on {{toChain}} is only {{amount}} {{tokenSymbol}}. Please lower the transfer amount or try again later.", + "eBridge limit rules": "About eBridge's transfer/rate limits", + "eBridge limit rules title": "eBridge's Transfer/Rate Limits", + "eBridge daily limit specification": "Daily Transfer Limits", + "eBridge daily limit specification desc": "To enhance security and protect our users' assets, eBridge imposes daily transfer limits for each token in each direction.", + "eBridge rate limits": "Rate Limits", + "eBridge rate limits desc": { + "section 1": "To keep eBridge secure, we use rate limits for token transfers. Here's how they work:", + "section 2": "We apply these limits on both the sending and receiving blockchains for extra security.", + "section 3": "Let's use USDT transfers from Ethereum to aelf as an example:", + "section 4": "Maximum capacity: 50,000 USDT", + "section 5": "Refill rate: 500 USDT per second (up to 50,000 USD every 2 minutes)", + "Section 6": "If someone transfers 50,000 USDT, the bucket is completely empty. To transfer another 5,000 USDT, you'd need to wait 10 seconds for the bucket to refill enough." + }, + "eBridge rate limits desc section 1 list": { + "section 1": "1. Each token has a 'bucket' with a maximum capacity.", + "section 2": "2. This bucket slowly refills over time.", + "section 3": "3. Every transfer uses some of the bucket's capacity.", + "section 4": "4. If the bucket is empty, you need to wait for it to refill before making another transfer." + }, + "Supported tokens": "Tokens:", + "OK": "OK", + "eBridge limit rules Token": "Token", + "eBridge limit rules Allowance": "Daily Transfer Limit", + "About allowance": "{{allowance}} {{tokenSymbol}}", + "Rate limit Token": "Token", + "Rate limit Attributes": "Rate Limit", + "Rate limit capacity": "Maximum capacity: {{capacity}} {{token}}", + "Rate limit refill rate": "Refill rate: {{refillRate}} {{token}}/second ", + "Rate limit refill rate desc": "(up to {{capacity}} {{token}} every {{maximumTimeConsumed}} minutes)", + "input address error": "Please enter a correct address that matches the \"To\" chain.", + "Synchronising data on the chain 1": "Synchronising data on the {{chainType}}...", + "Synchronising data on the chain 2": "Please wait 5-10 minutes before initiating a new transaction.", + "main chain": "MainChain", + "side chain": "SideChain", + "Request rejected. eBridge needs your permission to continue": "Request rejected. eBridge needs your permission to continue.", + "Learn more about transfer and limits": "Learn more about eBridge's transfer and rate limits ", + "here": "here", + "period": ".", + "Bridge": "Bridge", + "Transactions": "Transactions", + "Language": "Language", + "Community": "Community", + "Legal": "Legal", + "Terms of Service": "Terms of Service", + "Privacy Policy": "Privacy Policy", + "Connect Wallets": "Connect Wallets", + "Connect aelf Wallet": "Connect aelf Wallet", + "Connect External Wallet": "Connect External Wallet", + "Select your wallet": "Select your wallet", + "Connect aelf wallet": "Connect aelf wallet", + "By connecting a wallet, you agree to our": "By connecting a wallet, you agree to our ", + "Let's connect your aelf wallet to get started.": "Let's connect your aelf wallet to get started.", + "Nice! Now let's connect your aelf wallet.": "Nice! Now let's connect your aelf wallet.", + "Skip and enter address manually": "Skip and enter address manually", + "Bridge to aelf": "Bridge to aelf", + "Bridge from aelf": "Bridge from aelf", + "Bridge within aelf": "Bridge within aelf", + "Transfer limits": "Transfer limits", + "MAX": "MAX", + "Send to another address manually": "Send to another address manually", + "Choose this option if you wish to send to an address without connecting wallet.": "Choose this option if you wish to send to an address without connecting wallet.", + "You'll receive": "You'll receive", + "Expected time": "Expected time", + "minutes": "minutes", + "Enter destination address placeholder": "Enter destination {{networkName}} address", + "Enter destination address": "Enter destination address", + "Enter an amount": "Enter an amount", + "Wait for wallet confirmation...": "Wait for wallet confirmation...", + "The Transaction fee also needs to be approved. Don't refresh or close the page until the operation is complete.": "The Transaction fee also needs to be approved. Don't refresh or close the page until the operation is complete.", + "Transaction approved": "Transaction approved", + "Transaction rejected": "Transaction rejected", + "The transaction has been approved. You can check it's status in the “Transactions” page.": "The transaction has been approved. You can check it's status in the “Transactions” page.", + "The transaction was not signed. It was rejected.": "The transaction was not signed. It was rejected.", + "Close": "Close", + "Retry": "Retry", + "Can't find your token?": "Can't find your token?", + "Check the transaction status from the transaction records.": "Check the transaction status from the transaction records.", + "For more details, click on the From/To TXID to track its progress on the Explorer.": "For more details, click on the From/To TXID to track its progress on the Explorer.", + "All Transactions": "All Transactions", + "Pool": "Pool", + "Total TVL": "Total TVL", + "Your Total Liquidity": "Your Total Liquidity", + "Pools": "Pools", + "Tokens": "Tokens", + "Only Show My Position": "Only Show My Position", + "Token": "Token", + "Network": "Network", + "Your Liquidity": "Your Liquidity", + "Total Liquidity": "Total Liquidity", + "The total value of all assets locked in liquidity pools across eBridge.": "The total value of all assets locked in liquidity pools across eBridge.", + "The total value of the liquidity you have added to all pools on eBridge.": "The total value of the liquidity you have added to all pools on eBridge.", + "The number of liquidity pools currently active on eBridge.": "The number of liquidity pools currently active on eBridge.", + "The number of different tokens available across all liquidity pools on eBridge.": "The number of different tokens available across all liquidity pools on eBridge.", + "Add": "Add", + "Remove": "Remove", + "Token Amount": "Token Amount", + "Max": "Max", + "Estimated value": "Estimated value", + "Share of Pool": "Share of Pool", + "Liquidity": "Liquidity", + "Enter Amount": "Enter Amount", + "Search token": "Search token", + "Insufficient amount": "Insufficient amount", + "You currently don’t have any positions in pools.": "You currently don’t have any positions in pools." + } +} diff --git a/src/i18n/languages/zh.json b/src/i18n/languages/zh.json new file mode 100644 index 00000000..e040bdfe --- /dev/null +++ b/src/i18n/languages/zh.json @@ -0,0 +1,227 @@ +{ + "translation": { + "eBridge: Cross-chain Bridge": "eBridge: Cross-chain Bridge", + "Meta description": "Web3 cross-chain bridge that facilitates seamless token transfer between the aelf blockchain and Ethereum Virtual Machine (EVM) compatible networks.", + "Token Bridge": "代幣跨鏈", + "Connect": "連接錢包", + "Connect Wallet": "連接錢包", + "Connect aelf wallet to view transaction history": "連接AELF錢包查看操作記錄", + "Heterogeneous Chain Cross-Chain History": "外部交易", + "Homogeneous Chain Cross-Chain History": "內部交易", + "Select a Receipt ID": "選擇Receipt ID", + "From": "從", + "To": "到", + "From Chain": "來源鏈", + "To Chain": "目標鏈", + "Transaction Status": "交易狀態", + "Amount Sent": "發送金額", + "Amount": "金額", + "Amount Received": "接收金額", + "From Address": "發送地址", + "To Address": "接收地址", + "Sent At": "發送時間", + "Receive At": "接收時間", + "Sending TXID": "發送交易 ID ", + "Receiving TXID": "接收交易 ID", + "Transferred": "進行中", + "Indexed": "待接收", + "Received": "已完成", + "Estimated transaction fee": "預計交易手續費", + "Grant permission to access your funds?": "允許換幣合約使用您的代幣", + "Confirm": "授權", + "No cross-chain records found.": "未査詢到跨鏈記錄", + "Transfer": "發送", + "Heterogeneous chain cross-chain": "異構跨鏈:", + "cross-chain transfer between aelf and other public chains (e.g. aelf to Ethereum)": "AELF與其他公鏈(如MainChain AELF到Ethereum)之間的跨鏈轉帳", + "Homogeneous chain cross-chain": "同構跨鏈:", + "Please note": "請注意:", + "When the network is not congested": "在沒有出現擁堵的情況下", + "The minimum crosschain transaction fee is around": "最小跨鏈手續費約", + "The minimum crosschain amount is": "最小跨鏈金額{{amount}}{{symbol}}。", + "The minimum crosschain amount is2": "最小跨鏈金額 ", + "Estimated time of heterogenous cross-chain arrival is": "異構跨鏈耗時約為{{time}}", + "Estimated time of homogeneous cross-chain arrival is": "内部跨鏈耗時約為{{time}}", + "Estimated time of homogeneous cross-chain arrival is2": "内部跨鏈耗時約為{{time}}分鐘完成;", + "Your transfer will take about a few minutes to complete": "您的轉帳大約需要{{time}}分鐘完成。", + "Cross Chain time": "異構跨鏈耗時約為{{heterogeneous}},同構跨鏈的耗時約為{{homogeneous}}。", + "You pay fees for the fromChain transaction. We're covering the fees on aelf for you": "您只需支付{{fromChain}}部分的交易費用。我們為您承擔aelf上的費用。", + "Tokens will arrive automatically after being sent": "請耐心等待,您的代幣將自動顯示在您的錢包中。", + "Once finished, You can verify this on the blockchain explorer and wallet": "完成後,代幣會自動顯示在接收者的錢包中。您可以在區塊鏈瀏覽器上進行驗證。", + "Estimated time of sending tokens is time. Please select Receipt ID and receive the token(s) transferred": "預計到賬時間為{{time}}分鐘。", + "Once the token is sent cross-chain": "代幣跨鏈後,接收方需要訪問eBridge、鏈接至“To”錢包、選擇“Receipt ID”以接收代幣。此步驟會在{{toChain}}網絡產生交易費,因此接收方需確保錢包有充足的餘額支付費用。", + "Token not received?": "發送了代幣但沒有接收?", + "Initiate cross-chain transfer to the recipient's address": "輸入收款地址並轉賬 (無需連接“To”錢包)", + "Tips": "提示", + "Connect the recipient wallet": "連接接收錢包。", + "Select a Receipt ID2": "選擇Receipt ID。", + "Click the button to receive the token(s) transferred": "點擊接收按鈕,接收已發送的代幣。", + "Cross-chain transaction initiated": "異構跨鏈已發起", + "Receive": "接收", + "Receiving request submitted": "接收請求已發送", + "Can't find your token": "找不到您的代幣?", + "Import custom tokens": "添加新幣種", + "Import a custom token": "添加跨鏈新幣種", + "Please enter": "請輸入", + "Confirm2": "驗證", + "Import": "添加幣種", + "Custom token imported": "新幣種添加成功", + "Cross-chain transaction initiated2": "同構跨鏈已發起", + "Balance": "餘額:", + "Added": "已添加", + "records in total": "共 {{total}} 項目", + "Select a token": "選擇幣種", + "Insufficient balance": "餘額不足", + "Synchronization request has been submitted": "同步代幣資訊請求已發送", + "Transaction is being processed, please don't close this window": "交易進行中,請不要關閉頁面", + "Create a token": "創建代幣", + "To add a new token, you need to initiate 2 transactions here to create the token on": "添加新代幣的同時,您需要在本彈窗內發起兩筆交易,在 {{chain}} 同步代幣資訊 ", + "Transaction": "交易", + "This token does not exist on": "{{chain}} 上沒有該代幣資訊 ", + "Create Tip": "交易大約耗時 {{time}},費用約為 {{fee}}。", + "Click the button below to proceed": "如仍要繼續,請點擊下方按鈕開始", + "Initiate Synchronization": "發起同步請求", + "Synchronize token info": "同步代幣資訊", + "Copy Success": "複製成功", + "Transaction Id": "交易 ID", + "Verify info": "驗證資訊", + "Transaction request submitted": "交易請求發送成功", + "Transaction request failed": "交易請求發送失敗", + "Custom token does not exist": "該幣種不存在", + "Connect2": "連接", + "to view transaction history": "查看操作記錄", + "aelf Wallet": "aelf錢包", + "wallet": "錢包", + "Request submitted. Please synchronize the token info": "請求發送成功,請同步代幣信息", + "The amount to be transferred should be smaller than": "交易請求發送失敗,本次轉帳金額需小於 {{amount}} {{symbol}}.", + "record does not exist": "記錄不存在", + "Approve": "授權", + "Approve symbol": "授權 {{symbol}}", + "Invalid to chain": "無效的目標鏈", + "Invalid from chain": "無效的來源鏈", + "Please confirm the recipient's address": "請確認跨鏈目標地址", + "Confirm3": "確認", + "Cancel": "返回", + "Notification Title": "eBridge Alpha", + "Notification Content Section 1": "This is an ALPHA version of eBridge. As such, delays may occur, and catastrophic bugs may lurk. Thanks, for trying it at this early stage.", + "Notification Content Section 2": "For any issues, join our Telegram group for support: ", + "Upgrade Title": "Service Upgrade Notice", + "Upgrade Notice": { + "section 1": "Dear eBridge User,", + "section 2": "We are currently upgrading our system to serve you better. During this period, the eBridge service is temporarily unavailable. We kindly advise you to refrain from using the service at this moment to avoid any potential inconveniences or losses.", + "section 3":"The service is expected to resume by May 15, 2025, at 12:00 PM (UTC).", + "section 4": "We appreciate your understanding and patience and look forward to providing you with an improved eBridge experience soon.", + "section 5": ["Thank you for your support!", "The eBridge Team"] + }, + "Please Notice": "請注意", + "have reached the daily limit": "{{fromChain}}-->{{toChain}}方向的{{tokenSymbol}}每日跨鏈額度已達到上限。額度將在00:00 (UTC)刷新,請在刷新後重試。", + "have a daily limit and your current transaction": "{{fromChain}}-->{{toChain}}方向的{{tokenSymbol}}每日跨鏈額度當前剩餘{{amount}} {{tokenSymbol}}。您的跨鏈數量超過剩餘額度,因此無法完成。額度將在00:00 (UTC)刷新,請在刷新後重試或減少本次跨鏈的數量。", + "have a maximum capacity and your current transaction exceeds the available capacity": "{{fromChain}}-->{{toChain}}方向的{{tokenSymbol}}跨鏈容量當前可用{{amount}} {{tokenSymbol}},您的跨鏈數量超過可用容量,因此無法完成。容量將在{{time}}分鐘后填充,請在填充後重試。", + "Your current transaction exceeds the capacity and can't be processed": "{{fromChain}}-->{{toChain}}方向的{{tokenSymbol}}跨鏈最大容量為{{amount}} {{tokenSymbol}},您的跨鏈數量超過最大容量,因此無法完成。請減少本次跨鏈的數量。", + "Insufficient liquidity tip": "{{toChain}} 上的 {{tokenSymbol}} 流動性僅剩 {{amount}} {{tokenSymbol}},請降低轉帳金額或稍後再試。", + "eBridge limit rules": "關於eBridge限額/限速規則", + "eBridge limit rules title": "eBridge限額/限速規則", + "eBridge daily limit specification": "日限額說明", + "eBridge daily limit specification desc": "為了提升安全性並保護我們用戶的資產,eBridge 對每個代幣都設定了每日轉帳限額。", + "eBridge rate limits": "限速說明", + "eBridge rate limits desc": { + "section 1": "為了保持 eBridge 的安全性,我們對代幣轉帳設置了速率限制。以下是其工作原理:", + "section 2": "我們在發送和接收的區塊鏈上都應用這些限制,以加強安全性。", + "section 3": "以 USDT 從以太坊轉到 aelf 為例:", + "section 4": "最大容量:50,000 USDT", + "section 5": "補滿速率:每秒 500 USDT(每 2 分鐘最多補滿 50,000 USDT)", + "section 6": "如果有人轉帳 50,000 USDT,桶會完全空掉。若想再轉帳 5,000 USDT,您需要等待 10 秒鐘讓桶補滿足夠的容量。" + }, + "eBridge rate limits desc section 1 list": { + "section 1": "1. 每個代幣都有一個「桶」,具有最大容量。", + "section 2": "2. 這個桶會隨著時間慢慢補滿。", + "section 3": "3. 每次轉帳都會使用一部分桶的容量。", + "section 4": "4. 如果桶是空的,您需要等待它重新補滿後才能進行下一次轉帳。" + }, + "Supported tokens": "代幣:", + "OK": "好的", + "eBridge limit rules Token": "代幣", + "eBridge limit rules Allowance": "日限額", + "About allowance": "{{allowance}} {{tokenSymbol}}", + "Rate limit Token": "代幣", + "Rate limit Attributes": "限速", + "Rate limit capacity": "最大容量: {{capacity}} {{token}}", + "Rate limit refill rate": "填充速率:每秒{{refillRate}} {{token}}", + "Rate limit refill rate desc": "(每{{maximumTimeConsumed}}分鐘最多{{capacity}} {{token}})", + "input address error": "請輸入與“To”鏈匹配的錢包地址。", + "Synchronising data on the chain 1": "{{chainType}}同步數據中...", + "Synchronising data on the chain 2": "請等待5-10分鐘後再發起跨鏈。", + "main chain": "主鏈", + "side chain": "側鏈", + "Request rejected. eBridge needs your permission to continue": "請求被拒絕。eBridge需要您的授權才能繼續。", + "Learn more about transfer and limits": "關於", + "here": "eBridge限額/限速規則", + "period": "。", + "Bridge": "跨鏈轉賬", + "Transactions": "交易記錄", + "Language": "語言", + "Community": "社群", + "Legal": "法律聲明", + "Terms of Service": "服務條款", + "Privacy Policy": "隱私政策", + "Connect Wallets": "連接錢包", + "Connect aelf Wallet": "連接 aelf 錢包", + "Connect External Wallet": "連接外部錢包", + "Select your wallet": "選擇您的錢包", + "Connect aelf wallet": "連接 aelf 錢包", + "By connecting a wallet, you agree to our": "連接錢包即表示您同意我們的", + "Let's connect your aelf wallet to get started.": "連接您的 aelf 錢包以開始", + "Nice! Now let's connect your aelf wallet.": "很好!現在讓我們連接您的 aelf 錢包。", + "Skip and enter address manually": "跳過並手動輸入地址", + "Bridge to aelf": "跨鏈至 aelf", + "Bridge from aelf": "從 aelf 跨鏈", + "Bridge within aelf": "在 aelf 內跨鏈", + "Transfer limits": "轉帳限額", + "MAX": "最大值", + "Send to another address manually": "手動輸入另一個地址並轉帳", + "Choose this option if you wish to send to an address without connecting wallet.": "如果您希望在不連接錢包的情況下發送至地址,請選擇此選項。", + "You'll receive": "您將收到​", + "Expected time": "預計時間", + "minutes": "分鐘", + "Enter destination address placeholder": "輸入目標 {{networkName}} 地址", + "Enter destination address": "輸入地址", + "Enter an amount": "輸入金額", + "Wait for wallet confirmation...": "等待錢包確認中...", + "The Transaction fee also needs to be approved. Don't refresh or close the page until the operation is complete.": "需授权交易手續費。操作完成前,請勿刷新或關閉此頁面。", + "Do not refresh or close the page while waiting for the operation to be completed.": "等待操作完成時,請勿刷新或關閉頁面。", + "Transaction approved": "交易已批准", + "Transaction rejected": "交易被拒絕", + "The transaction has been approved. You can check it's status in the “Transactions” page.": "交易已獲批准。您可以在“交易”頁面查看其狀態。", + "The transaction was not signed. It was rejected.": "交易未簽署,已被拒絕。", + "Close": "關閉", + "Retry": "重試", + "Can't find your token?": "找不到您的代幣?", + "Check the transaction status from the transaction records.": "請從交易記錄中查看交易狀態。", + "For more details, click on the From/To TXID to track its progress on the Explorer.": "欲了解更多詳情,請點擊發送/接收的交易編號 (TXID) 在區塊瀏覽器上追蹤其進度。", + "All Transactions": "所有交易", + "Pool": "流動池", + "Total TVL": "總體鎖倉價值", + "Your Total Liquidity": "您的總流動性", + "Pools": "流動池", + "Tokens": "代幣", + "Only Show My Position": "僅顯示我的倉位", + "Token": "代幣", + "Network": "網絡", + "Your Liquidity": "您的流動性", + "Total Liquidity": "總流動性", + "The total value of all assets locked in liquidity pools across eBridge.": "eBridge 上所有資產鎖定在流動性池中的總價值。", + "The total value of the liquidity you have added to all pools on eBridge.": "您在 eBridge 上所有流動性池中所添加的流動性總價值。", + "The number of liquidity pools currently active on eBridge.": "目前在 eBridge 上活躍的流動性池數量。", + "The number of different tokens available across all liquidity pools on eBridge.": "eBridge 上所有流動性池中可用的不同代幣數量。", + "Add": "添加", + "Remove": "移除", + "Token Amount": "代幣數量", + "Max": "Max", + "Estimated value": "預估價值", + "Share of Pool": "流動池份額", + "Liquidity": "總流動性", + "Enter Amount": "輸入數量", + "Search token": "搜尋代幣", + "Insufficient amount": "金额不足", + "You currently don’t have any positions in pools.": "您目前在池中沒有任何持倉。" + } +} diff --git a/src/i18n/moment/zh-cn.js b/src/i18n/moment/zh-cn.js new file mode 100644 index 00000000..4507109a --- /dev/null +++ b/src/i18n/moment/zh-cn.js @@ -0,0 +1,125 @@ +/* eslint-disable no-undef */ +//! moment.js locale configuration +//! locale : Chinese (China) [zh-cn] +//! author : suupic : https://github.com/suupic +//! author : Zeno Zeng : https://github.com/zenozeng +//! author : uu109 : https://github.com/uu109 +import moment from 'moment/moment'; +(function (global, factory) { + factory(moment); +})(this, function (moment) { + 'use strict'; + + //! moment.js locale configuration + + var zhCn = moment.defineLocale('zh-cn', { + months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), + monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), + weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), + weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'), + weekdaysMin: '日_一_二_三_四_五_六'.split('_'), + longDateFormat: { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L: 'YYYY/MM/DD', + LL: 'YYYY年M月D日', + LLL: 'YYYY年M月D日Ah点mm分', + LLLL: 'YYYY年M月D日ddddAh点mm分', + l: 'YYYY/M/D', + ll: 'YYYY-MM-DD', + lll: 'YYYY年M月D日 HH:mm', + llll: 'YYYY年M月D日dddd HH:mm', + }, + meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, + meridiemHour: function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') { + return hour; + } else if (meridiem === '下午' || meridiem === '晚上') { + return hour + 12; + } else { + // '中午' + return hour >= 11 ? hour : hour + 12; + } + }, + meridiem: function (hour, minute) { + var hm = hour * 100 + minute; + if (hm < 600) { + return '凌晨'; + } else if (hm < 900) { + return '早上'; + } else if (hm < 1130) { + return '上午'; + } else if (hm < 1230) { + return '中午'; + } else if (hm < 1800) { + return '下午'; + } else { + return '晚上'; + } + }, + calendar: { + sameDay: '[今天]LT', + nextDay: '[明天]LT', + nextWeek: function (now) { + if (now.week() !== this.week()) { + return '[下]dddLT'; + } else { + return '[本]dddLT'; + } + }, + lastDay: '[昨天]LT', + lastWeek: function (now) { + if (this.week() !== now.week()) { + return '[上]dddLT'; + } else { + return '[本]dddLT'; + } + }, + sameElse: 'L', + }, + dayOfMonthOrdinalParse: /\d{1,2}(日|月|周)/, + ordinal: function (number, period) { + switch (period) { + case 'd': + case 'D': + case 'DDD': + return number + '日'; + case 'M': + return number + '月'; + case 'w': + case 'W': + return number + '周'; + default: + return number; + } + }, + relativeTime: { + future: '%s后', + past: '%s前', + s: '几秒', + ss: '%d 秒', + m: '1 分钟', + mm: '%d 分钟', + h: '1 小时', + hh: '%d 小时', + d: '1 天', + dd: '%d 天', + w: '1 周', + ww: '%d 周', + M: '1 个月', + MM: '%d 个月', + y: '1 年', + yy: '%d 年', + }, + week: { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow: 1, // Monday is the first day of the week. + doy: 4, // The week that contains Jan 4th is the first week of the year. + }, + }); + + return zhCn; +}); diff --git a/src/modals/AccountModal/AccountCard.tsx b/src/modals/AccountModal/AccountCard.tsx new file mode 100644 index 00000000..bedb8132 --- /dev/null +++ b/src/modals/AccountModal/AccountCard.tsx @@ -0,0 +1,191 @@ +import { Button, Card, Col, Row } from 'antd'; +import { useCallback, useMemo } from 'react'; +import { getExploreLink, shortenString } from '../../utils'; +import Copy from '../../components/Copy'; +import CommonLink from '../../components/CommonLink'; +import { useModal } from 'contexts/useModal'; +import { basicModalView } from 'contexts/useModal/actions'; +import { isELFChain } from 'utils/aelfUtils'; +import WalletIcon from 'components/WalletIcon'; +import { SUPPORTED_WALLETS } from 'constants/wallets'; +import { useChainDispatch } from 'contexts/useChain'; +import { setSelectERCWallet } from 'contexts/useChain/actions'; +import { clearWCStorageByDisconnect } from 'utils/localStorage'; +import { formatAddress } from 'utils/chain'; +import CommonMessage from 'components/CommonMessage'; +import { WalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import IconFont from 'components/IconFont'; +import { useAelfLogout } from 'hooks/wallet'; +import { TelegramPlatform } from 'utils/telegram/telegram'; +import { useTonConnectUI } from '@tonconnect/ui-react'; +import { useConnect } from 'hooks/useConnect'; +import { useWeb3 } from 'hooks/web3'; +import { TWalletConnectorId } from 'types'; +import { handleErrorMessage } from 'utils/error'; +import { useConnectWallet } from '@aelf-web-login/wallet-adapter-react'; + +function AccountCard() { + const { goAssets } = useConnectWallet(); + const [{ accountWallet, accountChainId }, { dispatch }] = useModal(); + const chainDispatch = useChainDispatch(); + const logoutWebLogin = useAelfLogout(); + const connect = useConnect(); + const [tonConnectUI] = useTonConnectUI(); + const { deactivate } = useWeb3(); + + const { connector, connectorId, account, aelfInstance, walletType, loginWalletType } = accountWallet || {}; + const filter = useCallback( + (k: TWalletConnectorId) => { + return SUPPORTED_WALLETS[k].connectorId === connectorId; + }, + [connectorId], + ); + + const formatConnectorName = useMemo(() => { + const keys = Object.keys(SUPPORTED_WALLETS) as TWalletConnectorId[]; + const name = keys.filter((k) => filter(k)).map((k) => SUPPORTED_WALLETS[k].name)[0]; + return `Connected with ${name}`; + }, [filter]); + + const showAelfDisconnectButton = useMemo(() => { + return !(TelegramPlatform.isTelegramPlatform() && walletType === 'PORTKEY'); + }, [walletType]); + + const onDisconnect = useCallback(async () => { + if (typeof connector !== 'string') { + // WEB3 + try { + await deactivate?.(); + } catch (error) { + console.log('error: ', error); + } finally { + chainDispatch(setSelectERCWallet(undefined)); + clearWCStorageByDisconnect(); + } + } else if (connector === 'TON') { + tonConnectUI.disconnect?.(); + connect('TON'); + } else { + // Aelf + await logoutWebLogin?.(); + } + if (walletType !== 'ERC') { + dispatch(basicModalView.modalDestroy()); + return; + } + + dispatch( + basicModalView.setWalletModal(true, { + walletWalletType: walletType, + walletChainType: walletType === 'ERC' ? 'ERC' : 'ELF', + walletChainId: accountChainId, + }), + ); + }, [ + connector, + walletType, + dispatch, + accountChainId, + deactivate, + chainDispatch, + tonConnectUI, + connect, + logoutWebLogin, + ]); + + const changeWallet = useCallback(async () => { + if (walletType !== 'ERC') { + onDisconnect(); + return; + } + + try { + return dispatch( + basicModalView.setWalletModal(true, { + walletWalletType: walletType, + walletChainType: walletType === 'ERC' ? 'ERC' : 'ELF', + walletChainId: accountChainId, + }), + ); + } catch (error: any) { + console.debug(`connection error: ${error}`); + CommonMessage.error(`connection error: ${handleErrorMessage(error.message)}`); + } + }, [accountChainId, dispatch, onDisconnect, walletType]); + + const isELF = isELFChain(accountChainId); + + const jumpAssets = useCallback(() => { + dispatch(basicModalView.modalDestroy()); + goAssets(); + }, [dispatch, goAssets]); + return ( + <> +
+
{formatConnectorName}
+ {loginWalletType === WalletTypeEnum.web && ( +
+ View Assets + +
+ )} +
+ + +
+ {account && } +
+ +
+ + {account ? ( + + {shortenString(isELF ? formatAddress(accountChainId, account) : account, 8, 8)} + + ) : null} + {account ? ( + + + + ) : null} + + + + {accountChainId && account ? ( + + {`View on ${new URL(getExploreLink(account, 'address', accountChainId)).host}`} + + ) : null} + + + + + + {aelfInstance?.connect ? null : ( + + + {showAelfDisconnectButton && ( + <> + + {walletType !== 'TON' && ( + + )} + + )} + + + )} + + ); +} + +export default AccountCard; diff --git a/src/modals/AccountModal/index.tsx b/src/modals/AccountModal/index.tsx new file mode 100644 index 00000000..8917cd57 --- /dev/null +++ b/src/modals/AccountModal/index.tsx @@ -0,0 +1,21 @@ +import CommonModal from '../../components/CommonModal'; +import { useModal } from 'contexts/useModal'; +import { basicModalView } from 'contexts/useModal/actions'; +import clsx from 'clsx'; +import AccountCard from './AccountCard'; + +function AccountModal() { + const [{ accountModal }, { dispatch }] = useModal(); + return ( + dispatch(basicModalView.setAccountModal(false))}> + + + ); +} + +export default AccountModal; diff --git a/src/modals/PortkeyNotConnectModal/index.tsx b/src/modals/PortkeyNotConnectModal/index.tsx new file mode 100644 index 00000000..af9c9968 --- /dev/null +++ b/src/modals/PortkeyNotConnectModal/index.tsx @@ -0,0 +1,49 @@ +import { Col, Row } from 'antd'; +import CommonButton from 'components/CommonButton'; +import CommonModal from 'components/CommonModal'; + +import styles from './styles.module.less'; +import { useLanguage } from 'i18n'; +import { SupportedELFChainId } from 'constants/chain'; +import { basicModalView } from 'contexts/useModal/actions'; +import { useModal } from 'contexts/useModal'; + +export default function PortkeyNotConnectModal() { + const { t } = useLanguage(); + const [{ portketNotConnectModal }, { dispatch }] = useModal(); + + const closeModal = () => + dispatch( + basicModalView.setPortketNotConnectModal({ + visible: false, + chainId: undefined, + }), + ); + return ( + + + + + + {t('Synchronising data on the chain 1', { + chainType: t( + portketNotConnectModal?.chainId === SupportedELFChainId.AELF ? 'main chain' : 'side chain', + ), + })} + + {t('Synchronising data on the chain 2')} + + + + + {t('OK')} + + + + + ); +} diff --git a/src/modals/PortkeyNotConnectModal/styles.module.less b/src/modals/PortkeyNotConnectModal/styles.module.less new file mode 100644 index 00000000..ae7fe857 --- /dev/null +++ b/src/modals/PortkeyNotConnectModal/styles.module.less @@ -0,0 +1,40 @@ + +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + + +.portkey-not-connect-modal { + max-width: 492px; + :global { + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-body { + padding: 24px; + width: unset; + min-width: 492px; + } + } + .text { + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22px; + color: @font-5 + } + + .confirm-btn-box { + display: flex; + justify-content: center; + } + + .confirm-btn { + font-size: 16px; + height: 48px; + width: 322px; + font-weight: 600; + } +} \ No newline at end of file diff --git a/src/modals/WalletModal/index.tsx b/src/modals/WalletModal/index.tsx new file mode 100644 index 00000000..fab46fd9 --- /dev/null +++ b/src/modals/WalletModal/index.tsx @@ -0,0 +1,20 @@ +import { useLanguage } from 'i18n'; +import { useModal } from 'contexts/useModal'; +import { setWalletModal } from 'contexts/useModal/actions'; +import CommonModal from '../../components/CommonModal'; +import WalletList from '../../components/WalletList'; +export default function WalletModal() { + const { t } = useLanguage(); + const [{ walletModal }, { dispatch }] = useModal(); + const handleCloseModal = () => dispatch(setWalletModal(false)); + return ( + + + + ); +} diff --git a/src/modals/WalletsModal/index.tsx b/src/modals/WalletsModal/index.tsx new file mode 100644 index 00000000..e0f8efb7 --- /dev/null +++ b/src/modals/WalletsModal/index.tsx @@ -0,0 +1,184 @@ +import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'; +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import Link from 'next/link'; +import { useAelfLogin, useIsAelfLogin } from 'hooks/wallet'; +import { useModal } from 'contexts/useModal'; +import { setWalletsModal } from 'contexts/useModal/actions'; +import { useWalletContext } from 'contexts/useWallet'; +import { Button } from 'antd'; +import CommonModal from '../../components/CommonModal'; +import CommonImage from 'components/CommonImage'; +import WalletList from '../../components/WalletList'; +import { ethereumLogo, groupIcon, aelfChainLogo, checkFilledIcon, tonLogo } from 'assets/images'; +import { TERMS_OF_SERVICE_URL } from 'constants/link'; +import styles from './styles.module.less'; +import { ChainType } from 'types'; + +const WALLET_STEP = { + FROM: 0, + TO: 1, +}; + +const STEP_ITEM_CONFIG = { + ERC: { + chainType: 'ERC', + iconList: [ethereumLogo, groupIcon], + text: 'External Chain', + }, + ELF: { + chainType: 'ELF', + iconList: [aelfChainLogo], + text: 'aelf', + }, + TON: { + chainType: 'TON', + iconList: [tonLogo], + text: 'External Chain', + }, +}; + +export default function WalletsModal() { + const { t } = useLanguage(); + const login = useAelfLogin(); + const isLogin = useIsAelfLogin(); + const [{ walletsModal }, { dispatch }] = useModal(); + const [{ fromOptions, toOptions }] = useWalletContext(); + const [walletStep, setWalletStep] = useState(WALLET_STEP.FROM); + + const isFromELF = useMemo(() => fromOptions?.chainType === 'ELF', [fromOptions?.chainType]); + + const externalWallet = useMemo( + () => + isFromELF + ? toOptions?.chainType === 'ERC' + ? STEP_ITEM_CONFIG.ERC + : STEP_ITEM_CONFIG.TON + : fromOptions?.chainType === 'ERC' + ? STEP_ITEM_CONFIG.ERC + : STEP_ITEM_CONFIG.TON, + [fromOptions?.chainType, isFromELF, toOptions?.chainType], + ); + + const stepConfig = useMemo(() => { + if (!isFromELF) { + return [externalWallet, STEP_ITEM_CONFIG.ELF]; + } else { + return [STEP_ITEM_CONFIG.ELF, externalWallet]; + } + }, [externalWallet, isFromELF]); + + useEffect(() => { + if (!walletsModal) { + setWalletStep(WALLET_STEP.FROM); + } + }, [walletsModal]); + + const handleCloseModal = useCallback(() => { + dispatch(setWalletsModal(false)); + }, [dispatch]); + + const handleConnectExternalWalletFinish = () => { + if (!isFromELF) { + setWalletStep(WALLET_STEP.TO); + } else { + handleCloseModal(); + } + }; + + const handleConnectAELFWallet = useCallback(async () => { + await login(); + }, [login]); + + const handleConnectAELFWalletFinish = useCallback(() => { + if (!isFromELF) { + handleCloseModal(); + } else { + setWalletStep(WALLET_STEP.TO); + } + }, [handleCloseModal, isFromELF]); + + useEffect(() => { + if (isLogin && walletsModal) { + handleConnectAELFWalletFinish(); + } + }, [handleConnectAELFWalletFinish, isLogin, walletsModal]); + + return ( + +
+
+ {stepConfig.map((config, index) => { + const isNotCurrentStep = walletStep !== index; + return ( + + {index !== 0 &&
} +
+ {isNotCurrentStep && index < walletStep ? ( + + ) : ( +
+ {config.iconList.map((icon, idx) => ( + + ))} +
+ )} + {config.text} +
+ + ); + })} +
+ {stepConfig[walletStep].chainType !== 'ELF' ? ( + + ) : ( + <> +
+ + + {t( + !isFromELF + ? 'Nice! Now let’s connect your aelf wallet.' + : 'Let’s connect your aelf wallet to get started.', + )} + +
+ + + )} + {walletStep === WALLET_STEP.FROM && ( +

+ {t('By connecting a wallet, you agree to our')} + + {t('Terms of Service')} + + . +

+ )} + {walletStep === WALLET_STEP.TO && ( + + )} +
+ + ); +} diff --git a/src/modals/WalletsModal/styles.module.less b/src/modals/WalletsModal/styles.module.less new file mode 100644 index 00000000..a0ecc04c --- /dev/null +++ b/src/modals/WalletsModal/styles.module.less @@ -0,0 +1,76 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/font.less'; + +.wallets-modal { + :global { + .@{app-prefix}-modal-body { + min-width: 100%; + max-width: 100%; + } + } +} + +.modal-content { + gap: 16px; +} + +.steps-wrap { + gap: 16px; + .step-item { + gap: 8px; + } + .step-item-not-current { + .chian-icon, + .chain-text { + opacity: 0.3; + } + } + .chian-icon { + width: 24px; + height: 24px; + &:not(:first-child) { + margin-left: -4px; + } + } + .check-icon { + width: 24px; + height: 24px; + } + .chain-text { + color: @font-13; + font-size: 12px; + line-height: 22px; + } + .step-line { + flex: 1; + border-bottom: 1px solid @border-5; + } +} + +.tip { + color: @font-13; + font-size: 12px; + line-height: 16px; +} + +.link-button { + margin-top: -8px; + color: @font-14; + font-size: 14px; + font-weight: 500; + line-height: 22px; + .font-family-medium(); +} + +.aelf-tip-wrap { + gap: 10px; + padding: 16px 0; + color: @font-13; + line-height: 20px; +} + +.aelf-chain-logo { + width: 64px; + height: 64px; +} diff --git a/src/modals/index.tsx b/src/modals/index.tsx new file mode 100644 index 00000000..f3085972 --- /dev/null +++ b/src/modals/index.tsx @@ -0,0 +1,15 @@ +import dynamic from 'next/dynamic'; +import WalletModal from './WalletModal'; +import WalletsModal from './WalletsModal'; +import PortkeyNotConnectModal from './PortkeyNotConnectModal'; +const AccountModal = dynamic(import('./AccountModal'), { ssr: false }); +export default function Modals() { + return ( + <> + + + + + + ); +} diff --git a/src/page-components/Assets/images/assetsBack.svg b/src/page-components/Assets/images/assetsBack.svg new file mode 100644 index 00000000..a94f86a6 --- /dev/null +++ b/src/page-components/Assets/images/assetsBack.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/page-components/Assets/index.tsx b/src/page-components/Assets/index.tsx new file mode 100644 index 00000000..693a38ca --- /dev/null +++ b/src/page-components/Assets/index.tsx @@ -0,0 +1,68 @@ +import { useConnectWallet } from '@aelf-web-login/wallet-adapter-react'; +import { WalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { Asset, PortkeyAssetProvider } from '@portkey/did-ui-react'; +import { useCallback, useEffect, useMemo } from 'react'; +import { useRouter } from 'next/router'; +import assetsBackImg from './images/assetsBack.svg'; +import styles from './styles.module.less'; +import CommonImage from 'components/CommonImage'; +import { ROUTE_PATHS } from 'constants/link'; +import { useAElf } from 'hooks/web3'; +import { WalletTypeEnum as AelfWalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { getPortkeyWebWalletInfo } from 'utils/portkey'; +import { ChainId } from '@portkey/types'; + +export default function Assets() { + const router = useRouter(); + const { connector } = useAElf(); + + const { walletType } = useConnectWallet(); + + const portkeyWebInfo = useMemo(() => { + let chainId, pin; + const sdkWalletInfoString = getPortkeyWebWalletInfo(); + if (sdkWalletInfoString) { + chainId = sdkWalletInfoString.originChainId; + } + return { + chainId, + pin, + }; + }, []); + + const handleDeleteAccount = useCallback(() => { + localStorage.clear(); + }, []); + + useEffect(() => { + if (walletType !== WalletTypeEnum.web) { + router.replace(ROUTE_PATHS.HOME); + } + }, [walletType, router]); + + const onOverviewBack = useCallback(() => { + router.back(); + }, [router]); + + if (connector !== AelfWalletTypeEnum.web || !portkeyWebInfo?.pin || !portkeyWebInfo?.chainId) { + return null; + } + + return ( +
+ + } + onLifeCycleChange={(lifeCycle: any) => { + console.log(lifeCycle, 'onLifeCycleChange'); + }} + onOverviewBack={onOverviewBack} + onDeleteAccount={handleDeleteAccount} + /> + +
+ ); +} diff --git a/src/page-components/Assets/styles.module.less b/src/page-components/Assets/styles.module.less new file mode 100644 index 00000000..0669f973 --- /dev/null +++ b/src/page-components/Assets/styles.module.less @@ -0,0 +1,10 @@ +.assets-page { + width: 100%; + height: 100%; + + .assets-back-wrap { + width: 24px; + height: 24px; + cursor: pointer; + } +} diff --git a/src/page-components/Bridge/ActionButton/CheckToFillAddressModal/index.tsx b/src/page-components/Bridge/ActionButton/CheckToFillAddressModal/index.tsx new file mode 100644 index 00000000..8ecfa3ad --- /dev/null +++ b/src/page-components/Bridge/ActionButton/CheckToFillAddressModal/index.tsx @@ -0,0 +1,80 @@ +import { useMemo } from 'react'; +import { Col, Row, Button } from 'antd'; +import CommonModal from 'components/CommonModal'; +import { useLanguage } from 'i18n'; +import { useHomeContext } from '../../HomeContext'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { getNameByChainId, getIconByChainId } from 'utils/chain'; +import { useWallet } from 'contexts/useWallet/hooks'; +import IconFont from 'components/IconFont'; +import styles from './styles.module.less'; + +export default function CheckToFillAddressModal({ + visible: propsVisible, + setVisible: propsSetVisible, + onSuccess, +}: { + visible: boolean; + setVisible: (v: boolean) => void; + onSuccess?: () => void; +}) { + const { t } = useLanguage(); + const { toWallet } = useWallet(); + const [{ toAddress }] = useHomeContext(); + const { chainId } = toWallet || {}; + const isMD = useMediaQueries('md'); + const name = getNameByChainId(chainId); + + const visible = useMemo(() => propsVisible, [propsVisible]); + const iconProps = useMemo(() => { + if (!chainId) return undefined; + return getIconByChainId(chainId); + }, [chainId]); + + const IconName = useMemo(() => { + const props = { nameSize: 16, marginRight: 12 }; + if (isMD) { + props.nameSize = 14; + props.marginRight = 8; + } + return ( + + + {name || 'Wrong Network'} + + ); + }, [iconProps?.type, isMD, name]); + + const onCancel = () => propsSetVisible(false); + + const onConfirm = () => onSuccess?.(); + + return ( + propsSetVisible(false)}> +
+
+ {IconName} +
{toAddress}
+
+
+ + + + + + + + ); +} diff --git a/src/page-components/Bridge/ActionButton/CheckToFillAddressModal/styles.module.less b/src/page-components/Bridge/ActionButton/CheckToFillAddressModal/styles.module.less new file mode 100644 index 00000000..5e6246ec --- /dev/null +++ b/src/page-components/Bridge/ActionButton/CheckToFillAddressModal/styles.module.less @@ -0,0 +1,42 @@ +@import '/src/assets/theme/color.less'; + +.confirm-modal { + :global { + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + } + + .confirm-modal-content { + width: 100%; + height: 144px; + border-radius: 8px; + background-color: #f6f7fa; + margin-bottom: 32px; + padding: 24px; + box-sizing: border-box; + } + + .confirm-modal-address { + width: 100%; + height: 44px; + line-height: 22px; + color: #364153; + font-size: 14px; + text-align: left; + margin-top: 24px; + } + + .confirm-modal-button { + :global{ + .@{app-prefix}-btn{ + width: calc(49% - 8px); + height: 40px; + font-size: 14px; + line-height: 22px; + } + } + } +} \ No newline at end of file diff --git a/src/page-components/Bridge/ActionButton/index.tsx b/src/page-components/Bridge/ActionButton/index.tsx new file mode 100644 index 00000000..1d52f4c5 --- /dev/null +++ b/src/page-components/Bridge/ActionButton/index.tsx @@ -0,0 +1,401 @@ +import clsx from 'clsx'; +import lodash from 'lodash'; +import CommonButton from 'components/CommonButton'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { useBridgeContract, useTokenContract } from 'hooks/useContract'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import styles from './styles.module.less'; +import { CrossChainTransfer, CreateReceipt, LockToken } from 'utils/crossChain'; +import { useHomeContext } from '../HomeContext'; +import { timesDecimals } from 'utils/calculate'; +import { setActionLoading, setFrom } from '../HomeContext/actions'; +import { Trans } from 'react-i18next'; +import { ZERO } from 'constants/misc'; +import { useLanguage } from 'i18n'; +import useLockCallback from 'hooks/useLockCallback'; +import { isELFChain } from 'utils/aelfUtils'; +import { ACTIVE_CHAIN } from 'constants/index'; +import { formatAddress, isAddress, isChainAddress } from 'utils'; +import CheckToFillAddressModal from './CheckToFillAddressModal'; +import useLimitAmountModal from '../useLimitAmountModal'; +import useCheckPortkeyStatus from 'hooks/useCheckPortkeyStatus'; +import { arrowRightWhiteIcon } from 'assets/images'; +import CommonImage from 'components/CommonImage'; +import { useModalDispatch } from 'contexts/useModal/hooks'; +import { setWalletsModal } from 'contexts/useModal/actions'; +import { useAelfLogin } from 'hooks/wallet'; +import { getMaxAmount } from 'utils/input'; +import { useCheckTxnFeeEnough } from 'hooks/checkTxnFee'; +import { useConnect } from 'hooks/useConnect'; +import useLoadingModal from 'hooks/useLoadingModal'; +import { ResultType } from 'components/Loading/ResultModal'; + +export default function ActionButton() { + const { fromWallet, toWallet, fromOptions, toOptions, isHomogeneous } = useWallet(); + const login = useAelfLogin(); + const connect = useConnect(); + const [toConfirmModal, setToConfirmModal] = useState(false); + const [ + { selectToken, fromInput, fromBalance, actionLoading, crossMin, toChecked, toAddress, crossFee }, + { dispatch }, + ] = useHomeContext(); + const { chainId: fromChainId, account: fromAccount } = fromWallet || {}; + const { chainId: toChainId, account: toAccount } = toWallet || {}; + const fromTokenInfo = useMemo(() => { + if (!fromChainId) return; + const token = lodash.cloneDeep(selectToken?.[fromChainId]); + if (token?.isNativeToken) token.address = ''; + return token; + }, [fromChainId, selectToken]); + const { t } = useLanguage(); + const modalDispatch = useModalDispatch(); + + const { modal, setLoadingModal, setResultModal } = useLoadingModal( + useMemo( + () => ({ + resultModalProps: { + onClose: () => setIsBridgeButtonLoading(false), + approvedDescription: + "The transaction has been approved. You can check it's status in the “Transactions” page.", + }, + }), + [], + ), + ); + + useEffect(() => { + setLoadingModal({ open: actionLoading }); + }, [actionLoading, setLoadingModal]); + + const [isBridgeButtonLoading, setIsBridgeButtonLoading] = useState(false); + + const tokenContract = useTokenContract(fromChainId, fromTokenInfo?.address, fromWallet?.isPortkey); + const bridgeContract = useBridgeContract(fromChainId, fromWallet?.isPortkey); + + const [limitAmountModal, checkLimitAndRate] = useLimitAmountModal(); + + const { checkPortkeyConnect } = useCheckPortkeyStatus(); + + const onCrossChainTransfer = useLockCallback(async () => { + if (!(fromChainId && toChainId)) return; + + const token = selectToken?.[fromChainId] || selectToken?.[toChainId]; + if (!(tokenContract && fromAccount && toAccount && token && fromInput)) return; + dispatch(setActionLoading(true)); + setIsBridgeButtonLoading(true); + + if (!(await checkPortkeyConnect(isELFChain(fromChainId) ? fromChainId : toChainId))) { + setIsBridgeButtonLoading(false); + dispatch(setActionLoading(false)); + return; + } + try { + const req = await CrossChainTransfer({ + contract: tokenContract, + account: fromAccount, + to: toAccount, + token, + toChainId: toChainId, + amount: timesDecimals(fromInput, token.decimals).toFixed(0), + }); + if (!req.error) { + dispatch(setFrom('')); + setResultModal({ open: true, type: ResultType.APPROVED }); + } else { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onCrossChainTransfer }); + } + } catch (error: any) { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onCrossChainTransfer }); + } + dispatch(setActionLoading(false)); + }, [ + checkPortkeyConnect, + dispatch, + fromAccount, + fromChainId, + fromInput, + selectToken, + setResultModal, + toAccount, + toChainId, + tokenContract, + ]); + + const onCreateReceipt = useCallback(async () => { + console.log( + fromTokenInfo, + fromAccount, + bridgeContract, + toChainId, + fromChainId, + (toChecked && (toAccount || isAddress(toAddress, toChainId))) || toAccount, + ); + + if ( + !( + fromTokenInfo && + fromAccount && + bridgeContract && + toChainId && + fromChainId && + ((toChecked && (toAccount || isAddress(toAddress, toChainId))) || toAccount) + ) + ) + return; + dispatch(setActionLoading(true)); + setIsBridgeButtonLoading(true); + + const params: any = { + fromChainId, + fromToken: isELFChain(fromChainId) ? fromTokenInfo?.symbol : fromTokenInfo?.address, + account: fromAccount, + bridgeContract, + amount: timesDecimals(fromInput, fromTokenInfo.decimals).toFixed(0), + toChainId, + to: toChecked && toAddress ? toAddress : (toAccount as string), + crossFee, + }; + + if (!(await checkPortkeyConnect(isELFChain(fromChainId) ? fromChainId : toChainId))) { + setIsBridgeButtonLoading(false); + dispatch(setActionLoading(false)); + return; + } + + if (await checkLimitAndRate('transfer', fromInput)) { + setIsBridgeButtonLoading(false); + dispatch(setActionLoading(false)); + + return; + } + + if (tokenContract) { + params.tokenContract = tokenContract; + } + + try { + const req = await (fromTokenInfo.isNativeToken ? LockToken : CreateReceipt)(params); + if (!req?.error) { + dispatch(setFrom('')); + setResultModal({ + open: true, + type: ResultType.APPROVED, + }); + } else { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onCreateReceipt }); + } + } catch (error: any) { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onCreateReceipt }); + } + dispatch(setActionLoading(false)); + }, [ + fromTokenInfo, + fromAccount, + bridgeContract, + toChainId, + fromChainId, + toChecked, + toAccount, + toAddress, + dispatch, + fromInput, + crossFee, + checkPortkeyConnect, + checkLimitAndRate, + tokenContract, + setResultModal, + ]); + + const needConfirm = useMemo( + () => + !isHomogeneous && + toAccount && + toChecked && + isAddress(toAddress, toChainId) && + toAddress && + toAccount !== formatAddress(toAddress), + [isHomogeneous, toAccount, toChecked, toAddress, toChainId], + ); + + const getWalletBtnProps = useCallback( + ({ isFrom = false }: { isFrom?: boolean }) => { + const chainType = isFrom ? fromOptions?.chainType : toOptions?.chainType; + const wallet = isFrom ? fromWallet : toWallet; + const { chainId } = wallet || {}; + const isELF = chainType === 'ELF'; + const children = isELF ? 'Connect aelf Wallet' : 'Connect External Wallet'; + const disabled = false; + const onClick = () => { + connect(chainType, chainId); + }; + return { children, onClick, disabled }; + }, + [connect, fromOptions?.chainType, fromWallet, toOptions?.chainType, toWallet], + ); + + const { isShowTxnFeeEnoughTip, checkTxnFeeEnough } = useCheckTxnFeeEnough(); + const btnProps = useMemo(() => { + let children: React.ReactNode = ( +
+ {t('Bridge')} + +
+ ), + onClick: any, + disabled = true; + if ( + fromWallet && + fromWallet?.walletType !== 'ERC' && + fromAccount && + fromInput && + crossFee && + ZERO.plus(crossFee).gt(ZERO) && + tokenContract + ) { + checkTxnFeeEnough(); + } + + if (isBridgeButtonLoading) { + children = 'Bridge'; + disabled = true; + return { children, onClick, disabled, loading: true }; + } + if (!fromAccount && !toChecked && !toAccount) { + disabled = false; + if (isHomogeneous) { + children = 'Connect Wallet'; + onClick = () => login(); + } else { + children = 'Connect Wallets'; + onClick = () => modalDispatch(setWalletsModal(true)); + } + return { children, onClick, disabled }; + } + + if (!fromAccount) { + const props = getWalletBtnProps({ isFrom: true }); + return { children: props.children, onClick: props.onClick, disabled: props.disabled }; + } + + if (isHomogeneous) { + if (!toAccount) { + const props = getWalletBtnProps({ isFrom: false }); + return { children: props.children, onClick: props.onClick, disabled: props.disabled }; + } + } else { + if (!toChecked) { + if (!toAccount) { + const props = getWalletBtnProps({ isFrom: false }); + return { children: props.children, onClick: props.onClick, disabled: props.disabled }; + } + } else { + if ((toAddress && !isChainAddress(toAddress, toChainId)) || !toAddress) { + children = 'Enter destination address'; + return { children, onClick, disabled }; + } + } + } + + // invalid to chain + if (toChainId && !ACTIVE_CHAIN[toChainId]) { + children = 'Invalid to chain'; + return { children, onClick, disabled }; + } + if (!fromInput) { + children = 'Enter an amount'; + return { children, disabled, onClick }; + } + // invalid from chain + if (fromChainId && !ACTIVE_CHAIN[fromChainId]) { + children = 'Invalid from chain'; + return { children, onClick, disabled }; + } + + const max = getMaxAmount({ + chainId: fromWallet?.chainId, + symbol: fromBalance?.token?.symbol, + balance: fromBalance?.show, + crossFee, + }); + + if (max.lt(fromInput)) { + children = 'Insufficient balance'; + return { children, onClick, disabled }; + } else if (ZERO.lt(fromInput)) { + // crossMin + if (crossMin && ZERO.plus(crossMin).gt(fromInput)) { + children = `${t('The minimum crosschain amount is2')}${crossMin} ${fromTokenInfo?.symbol}`; + return { children, onClick, disabled }; + } else if (isShowTxnFeeEnoughTip) { + return { + disabled: true, + loading: false, + onClick: undefined, + children: 'Not enough ELF for Txn fee', + }; + } else { + disabled = false; + if (isHomogeneous) { + onClick = onCrossChainTransfer; + return { children, onClick, disabled }; + } else { + if (needConfirm) { + onClick = () => setToConfirmModal(true); + return { children, onClick, disabled }; + } else { + onClick = onCreateReceipt; + return { children, onClick, disabled }; + } + } + } + } + return { children, disabled, onClick }; + }, [ + fromInput, + t, + fromWallet, + fromAccount, + crossFee, + tokenContract, + isBridgeButtonLoading, + toChecked, + toAccount, + isHomogeneous, + toChainId, + fromChainId, + fromBalance?.token?.symbol, + fromBalance?.show, + isShowTxnFeeEnoughTip, + checkTxnFeeEnough, + login, + modalDispatch, + getWalletBtnProps, + toAddress, + crossMin, + fromTokenInfo?.symbol, + onCrossChainTransfer, + needConfirm, + onCreateReceipt, + ]); + + return ( + <> + + {btnProps.children} + + { + onCreateReceipt(); + setToConfirmModal(false); + }} + /> + {limitAmountModal} + {modal} + + ); +} diff --git a/src/page-components/Bridge/ActionButton/styles.module.less b/src/page-components/Bridge/ActionButton/styles.module.less new file mode 100644 index 00000000..eeda5489 --- /dev/null +++ b/src/page-components/Bridge/ActionButton/styles.module.less @@ -0,0 +1,38 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +.action-btn { + margin: auto; + width: 240px; + height: 48px; + font-size: 16px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; +} + +.action-btn-loading { + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: center; + gap: 8px; + &[disabled] { + cursor: not-allowed; + } +} + +.button-content { + gap: 8px; +} + +.arrow-icon { + width: 20px; + height: 20px; +} + +@media screen and (max-width: @md-width) { + .action-btn { + width: 100%; + } +} diff --git a/src/page-components/Bridge/AddTokenModal/components.tsx b/src/page-components/Bridge/AddTokenModal/components.tsx new file mode 100644 index 00000000..d9209166 --- /dev/null +++ b/src/page-components/Bridge/AddTokenModal/components.tsx @@ -0,0 +1,299 @@ +import { TransactionResult } from '@aelf-react/types'; +import { Row, Col, message, Alert, Steps, Progress } from 'antd'; +import { request } from 'api'; +import clsx from 'clsx'; +import CommonButton from 'components/CommonButton'; +import CommonLink from 'components/CommonLink'; +import CommonModal from 'components/CommonModal'; +import IconFont from 'components/IconFont'; +import TokenLogo from 'components/TokenLogo'; +import { ZERO } from 'constants/misc'; +import { useCrossChainContract } from 'hooks/useContract'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import type { ChainId, TokenInfo } from 'types'; +import { getExploreLink, shortenString } from 'utils'; +import { getChainIdToMap, getNameByChainId } from 'utils/chain'; +import { ContractBasic } from 'utils/contract'; +import { CrossChainCreateToken, ValidateTokenInfoExists } from 'utils/crossChain'; +import { txMessage } from 'utils/message'; +import writeText from 'copy-to-clipboard'; + +import styles from './styles.module.less'; +import { Trans } from 'react-i18next'; +import { useLanguage } from 'i18n'; + +const Hundred = ZERO.plus(100); +const InquireTime = 5000; + +export type StateTokenInfo = { [key in ChainId]?: TokenInfo }; + +export function TokenCard({ fromTokenInfo, chainId }: { fromTokenInfo: TokenInfo; chainId?: ChainId }) { + return ( +
+
+ Verify info +
+ +
+
Symbol
+ {fromTokenInfo.symbol} + +
+
Decimals
+ {fromTokenInfo.decimals} + +
+
+
Logo
+ +
+ + + + ); +} + +const maxProgress = 95; +export function TransactionModal({ + progress = 0, + transactionResult, + sendChainID, +}: { + sendChainID?: ChainId; + progress?: number; + transactionResult?: TransactionResult; +}) { + const { t } = useLanguage(); + const [autoProgress, setAutoProgress] = useState(1); + useEffect(() => { + let p = 1; + const timer = setInterval(() => { + p = Math.min(p + (p > 60 ? 1 : 2), maxProgress); + if (p === maxProgress) timer && clearInterval(timer); + setAutoProgress(p); + }, 1000); + return () => { + timer && clearInterval(timer); + }; + }, []); + const tmpProgress = useMemo( + () => Math.floor(autoProgress > progress ? autoProgress : progress), + [autoProgress, progress], + ); + return ( + <> +
+ {`Transaction is being processed, please don't close this window`} +
+
+ {tmpProgress ?? 0}% +
+ +
+
+
+
+ Transaction Id +
+
+ + {shortenString(transactionResult?.TransactionId || '', 6)} + + { + writeText(transactionResult?.TransactionId || ''); + message.success(t('Copy Success')); + }} + /> +
+
+ + ); +} + +export function SyncToken({ + account, + tokenOptions, + fromTokenContract, + toTokenContract, + addToken, +}: { + account?: string; + fromTokenContract?: ContractBasic; + toTokenContract?: ContractBasic; + addToken: (symbol: string, tokenInfo: StateTokenInfo) => void; + tokenOptions: { + toChainId?: ChainId; + fromChainId?: ChainId; + fromTokenInfo: any; + toTokenInfo: any; + tokenNotExist: boolean; + currentTokenInfo?: TokenInfo; + isSync: boolean; + }; +}) { + const { t, language } = useLanguage(); + const [loading, setLoading] = useState(); + const [visibleModal, setVisibleModal] = useState(); + const [crossChainProgress, setCrossChainProgress] = useState(); + const timer = useRef(); + const [transactionResult, setTransactionResult] = useState(); + + const { currentTokenInfo, fromTokenInfo, toChainId, fromChainId } = tokenOptions; + const { sendChainID, receiveId, sendTokenContract, receiveTokenContract } = useMemo(() => { + if (!fromTokenInfo) + return { + receiveTokenContract: fromTokenContract, + sendTokenContract: toTokenContract, + sendChainID: toChainId, + receiveId: fromChainId, + }; + return { + receiveTokenContract: toTokenContract, + sendTokenContract: fromTokenContract, + sendChainID: fromChainId, + receiveId: toChainId, + }; + }, [fromChainId, fromTokenContract, fromTokenInfo, toChainId, toTokenContract]); + const sendCrossChainContract = useCrossChainContract(sendChainID); + const onGetCrossChainIndexing = useCallback( + async (sendChainID: ChainId, receiveId: ChainId, height: number) => { + const progress = await request.cross.getCrossChainIndexing({ + params: { + fromChainId: getChainIdToMap(sendChainID), + toChainId: getChainIdToMap(receiveId), + height: height, + }, + }); + if (!isNaN(progress)) { + setCrossChainProgress(progress); + if (Hundred.lte(progress)) { + message.success(t('Request submitted. Please synchronize the token info')); + setVisibleModal(false); + timer.current && clearInterval(timer.current); + } + } + }, + [t], + ); + const onValidateTokenInfoExists = useCallback(async () => { + if (loading || !(sendTokenContract && account && currentTokenInfo && sendChainID && receiveId)) return; + setLoading(true); + try { + const req = await ValidateTokenInfoExists({ + contract: sendTokenContract, + account, + tokenInfo: currentTokenInfo, + }); + if (!req?.error) { + setTransactionResult(req); + setCrossChainProgress(0); + setVisibleModal(true); + timer.current && clearInterval(timer.current); + timer.current = setInterval(() => { + onGetCrossChainIndexing(sendChainID, receiveId, req.BlockNumber); + }, InquireTime); + } else { + txMessage({ req, chainId: sendChainID }); + } + } catch (error) { + console.debug(error, '======ValidateTokenInfoExists'); + } + setLoading(false); + }, [loading, sendTokenContract, account, currentTokenInfo, sendChainID, receiveId, onGetCrossChainIndexing]); + + const onCrossChainCreateToken = useCallback(async () => { + if ( + loading || + !( + receiveId && + sendCrossChainContract && + receiveTokenContract && + sendTokenContract && + sendChainID && + transactionResult + ) + ) + return; + setLoading(true); + try { + const req = await CrossChainCreateToken({ + transactionId: transactionResult.TransactionId, + sendChainID, + sendCrossChainContract, + receiveTokenContract, + sendTokenContract, + }); + txMessage({ req, chainId: receiveId }); + if (!req.error && currentTokenInfo?.symbol) { + addToken(currentTokenInfo?.symbol, { + [sendChainID]: currentTokenInfo, + [receiveId]: currentTokenInfo, + }); + } + } catch (error) { + console.debug(error, '======ValidateTokenInfoExists'); + } + setLoading(false); + }, [ + addToken, + currentTokenInfo, + loading, + receiveId, + receiveTokenContract, + sendChainID, + sendCrossChainContract, + sendTokenContract, + transactionResult, + ]); + useEffect(() => { + return () => { + timer.current && clearInterval(timer.current); + }; + }, []); + + const isCreateToken = transactionResult && Hundred.lte(crossChainProgress ?? 0); + return ( +
+
+ Synchronize token info +
+
+ +
+ + + + +
* {t('Create Tip', { fee: '1 ELF', time: '150 S' })}
+ + + + + {isCreateToken ? t('Synchronize token info') : t('Initiate Synchronization')} + +
+ ); +} diff --git a/src/page-components/Bridge/AddTokenModal/index.tsx b/src/page-components/Bridge/AddTokenModal/index.tsx new file mode 100644 index 00000000..f8f6be93 --- /dev/null +++ b/src/page-components/Bridge/AddTokenModal/index.tsx @@ -0,0 +1,140 @@ +import { Input, Button, Col, message } from 'antd'; +import CommonButton from 'components/CommonButton'; +import CommonModal from 'components/CommonModal'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { addWhitelist } from 'contexts/useWhitelist/actions'; +import { useWhitelistActions } from 'contexts/useWhitelist/hooks'; +import { useTokenContract } from 'hooks/useContract'; +import { useUserAddedToken } from 'hooks/whitelist'; +import { useLanguage } from 'i18n'; +import React, { useCallback, useMemo, useState } from 'react'; +import { Trans } from 'react-i18next'; +import { unityTokenInfo } from 'utils'; +import { isSymbol } from 'utils/reg'; +import { useHomeContext } from '../HomeContext'; +import { setAddModal, setSelectToken } from '../HomeContext/actions'; +import { useHomeActions } from '../HomeContext/hooks'; +import { StateTokenInfo, SyncToken, TokenCard } from './components'; +import styles from './styles.module.less'; +function AddToken() { + const [loading, setLoading] = useState(); + const [symbol, setSymbol] = useState(); + const { fromWallet, toWallet } = useWallet(); + const { dispatch: homoDispatch } = useHomeActions(); + const fromChainId = fromWallet?.chainId; + const toChainId = toWallet?.chainId; + const fromTokenContract = useTokenContract(fromChainId); + const toTokenContract = useTokenContract(toChainId); + const [tokenInfo, setTokenInfo] = useState(); + const { dispatch } = useWhitelistActions(); + const getTokenInfo = useCallback(async () => { + if (!symbol || !isSymbol(symbol) || loading || !fromChainId || !toChainId) return; + setLoading(true); + try { + const [from, to] = await Promise.all([ + fromTokenContract?.callViewMethod('GetTokenInfo', [symbol]), + toTokenContract?.callViewMethod('GetTokenInfo', [symbol]), + ]); + setTokenInfo({ + [fromChainId]: unityTokenInfo(from), + [toChainId]: unityTokenInfo(to), + }); + } catch (error) { + console.debug(error, '====error'); + } + setLoading(false); + }, [fromChainId, fromTokenContract, loading, symbol, toChainId, toTokenContract]); + + const tokenOptions = useMemo(() => { + const fromTokenInfo = fromChainId ? tokenInfo?.[fromChainId] : undefined; + const toTokenInfo = toChainId ? tokenInfo?.[toChainId] : undefined; + const tokenNotExist = !!(tokenInfo && fromChainId && toChainId && !fromTokenInfo && !toTokenInfo); + const currentTokenInfo = fromTokenInfo || toTokenInfo; + const isSync = !fromTokenInfo || !toTokenInfo; + return { fromChainId, toChainId, fromTokenInfo, toTokenInfo, tokenNotExist, currentTokenInfo, isSync }; + }, [fromChainId, toChainId, tokenInfo]); + const { currentTokenInfo, tokenNotExist, isSync } = tokenOptions; + const isAdded = useUserAddedToken(currentTokenInfo?.symbol || symbol); + const { t } = useLanguage(); + const addToken = useCallback( + (symbol: string, tokenInfo: StateTokenInfo) => { + dispatch(addWhitelist({ [symbol]: { ...tokenInfo } })); + homoDispatch(setAddModal(false)); + homoDispatch( + setSelectToken({ + symbol, + ...tokenInfo, + }), + ); + message.success(t('Custom token imported')); + }, + [dispatch, homoDispatch, t], + ); + return ( + <> +
+
Token Symbol
+
+
+ { + setSymbol(e.target.value); + tokenInfo && setTokenInfo(undefined); + }} + /> + + + + + + {tokenNotExist ? ( +
+ Custom token does not exist +
+ ) : ( + !!currentTokenInfo && ( + <> + + {isSync ? ( + + ) : ( + { + if (!currentTokenInfo?.symbol || !tokenInfo) return; + addToken(currentTokenInfo?.symbol, tokenInfo); + }} + type="primary"> + {t('Import')} + + )} + + ) + )} + + + ); +} +export default function AddTokenModal() { + const [{ addModal }, { dispatch }] = useHomeContext(); + const { t } = useLanguage(); + return ( + dispatch(setAddModal(false))} + open={addModal} + title={t('Import custom tokens')} + width="auto"> + + + ); +} diff --git a/src/page-components/Bridge/AddTokenModal/styles.module.less b/src/page-components/Bridge/AddTokenModal/styles.module.less new file mode 100644 index 00000000..722cd6bf --- /dev/null +++ b/src/page-components/Bridge/AddTokenModal/styles.module.less @@ -0,0 +1,183 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; +.add-modal { + :global { + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-body { + min-width: 448px; + max-width: 448px; + } + } + .input-row { + :global { + .@{app-prefix}-input { + height: 40px; + border-radius: 6px; + } + .@{app-prefix}-btn-default { + width: 112px; + } + } + } + .error-tip { + margin-top: 6px; + font-size: 12px; + color: @font-6; + } + .title { + color: @font-5; + margin-bottom: 8px; + } + .add-btn { + margin-top: 32px; + height: 48px; + width: 100%; + } +} + +.token-row { + margin-top: 24px; + .token-card { + background: @bg-7; + border-radius: 8px; + padding: 18px 16px; + .logo { + width: 24px; + height: 24px; + } + .card-title { + color: @font-3; + margin-bottom: 8px; + } + } +} +.notice-alert { + background: @bg-10; + color: @font-7; + border: 0; + padding: 15px 20px; + border-radius: 6px; + :global { + .@{app-prefix}-alert-icon { + margin-top: 2px; + font-size: 14px; + line-height: 24px; + margin-right: 10px; + } + .@{app-prefix}-alert-message { + font-size: 14px; + color: @font-7; + } + .@{app-prefix}-alert-description { + font-size: 12px; + } + } +} + +.add-steps { + margin: 18px 10px 24px; + width: auto; + + :global { + .@{app-prefix}-steps-item-description { + margin-top: 2px; + font-size: 12px; + } + .@{app-prefix}-steps-item-icon { + width: 28px; + height: 28px; + line-height: 28px; + } + .@{app-prefix}-steps-item-wait .@{app-prefix}-steps-item-container { + .@{app-prefix}-steps-item-icon { + border-color: @font-3; + .@{app-prefix}-steps-icon{ + color: @font-3; + } + } + .@{app-prefix}-steps-item-title, + .@{app-prefix}-steps-item-description { + color: @font-3; + } + } + } +} +.add-tip { + color: @font-3; +} + +.indexing-modal { + :global { + .@{app-prefix}-modal-content { + border-radius: 8px; + } + .@{app-prefix}-modal-close, + .@{app-prefix}-modal-header { + display: none; + } + .@{app-prefix}-modal-body { + padding-top: 24px; + min-width: 400px; + max-width: 400px; + } + } + .transaction-card { + margin-top: 10px; + background: @bg-7; + padding: 16px; + padding-right: 30px; + border-radius: 4px; + div:first-child { + color: @font-3; + } + a { + margin-right: 16px; + } + } +} +.progress-row { + margin-top: 10px; + width: 100%; + position: relative; + padding-top: 18px; + .progress { + :global { + .@{app-prefix}-progress-success-bg, + .@{app-prefix}-progress-bg { + background: linear-gradient(90deg, #fdd62d 0%, #fa8814 100%); + } + } + } + .progress-text { + top: 0; + position: absolute; + color: @font-7; + transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s; + } +} + +@media screen and (max-width: @md-width) { + .indexing-modal { + :global { + .@{app-prefix}-modal-content { + margin-left: 20px; + max-width: calc(100% - 40px); + } + } + .transaction-card { + font-size: 12px; + flex-direction: column; + :global { + .flex-row-center { + margin-top: 2px; + justify-content: space-between; + } + } + } + } +} diff --git a/src/page-components/Bridge/AmountRow/TokenSelect/index.tsx b/src/page-components/Bridge/AmountRow/TokenSelect/index.tsx new file mode 100644 index 00000000..23a8cbe2 --- /dev/null +++ b/src/page-components/Bridge/AmountRow/TokenSelect/index.tsx @@ -0,0 +1,30 @@ +import clsx from 'clsx'; +import { ButtonProps } from 'antd'; +import CommonImage from 'components/CommonImage'; +import TokenLogo from 'components/TokenLogo'; +import { formatSymbol } from 'utils/token'; +import { ChainId } from 'types'; +import { arrowIcon } from 'assets/images'; +import styles from './styles.module.less'; + +export default function TokenSelect({ + title, + chainId, + symbol, + onClick, +}: { + title?: string; + chainId?: ChainId; + symbol?: string; + onClick?: ButtonProps['onClick']; +}) { + return ( +
+ +
+
{formatSymbol(title)}
+ +
+
+ ); +} diff --git a/src/page-components/Bridge/AmountRow/TokenSelect/styles.module.less b/src/page-components/Bridge/AmountRow/TokenSelect/styles.module.less new file mode 100644 index 00000000..3644d2be --- /dev/null +++ b/src/page-components/Bridge/AmountRow/TokenSelect/styles.module.less @@ -0,0 +1,30 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/font.less'; + +.token-select { + .token-img { + width: 24px; + height: 24px; + margin-right: 8px; + box-sizing: border-box; + border-radius: 50%; + border: 1px solid @border-3; + } + .token-title-row { + gap: 8px; + } + .token-title { + color: @font-15; + font-size: 20px; + font-weight: 500; + line-height: 24px; + .text-overflow(1); + + white-space: nowrap; + } + .arrow-icon { + width: 16px; + height: 16px; + } +} diff --git a/src/page-components/Bridge/AmountRow/index.tsx b/src/page-components/Bridge/AmountRow/index.tsx new file mode 100644 index 00000000..3cf18fe0 --- /dev/null +++ b/src/page-components/Bridge/AmountRow/index.tsx @@ -0,0 +1,58 @@ +import { useMemo } from 'react'; +import { useLanguage } from 'i18n'; +import TokenSelect from './TokenSelect'; +import { useHomeContext } from '../HomeContext'; +import { setFrom, setSelectModal } from '../HomeContext/actions'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { unitConverter } from 'utils/converter'; +import { formatSymbol } from 'utils/token'; +import { getMaxAmount, parseInputChange } from 'utils/input'; +import { divDecimals } from 'utils/calculate'; +import { TBridgeChainId } from 'constants/chain'; +import { ZERO } from 'constants/misc'; +import CommonAmountRow from 'components/CommonAmountRow'; + +export default function AmountRow() { + const { t } = useLanguage(); + const [{ selectToken, fromInput, fromBalance, crossMin, crossFee }, { dispatch }] = useHomeContext(); + const { fromWallet, changing } = useWallet(); + const { token, show } = fromBalance || {}; + const { chainId, account } = fromWallet || {}; + + const min = useMemo(() => divDecimals(1, token?.decimals), [token?.decimals]); + const max = getMaxAmount({ chainId, symbol: token?.symbol, balance: show, crossFee }); + + const showError = useMemo( + () => fromInput && account && (max.lt(fromInput) || (crossMin && ZERO.plus(crossMin).gt(fromInput))), + [account, crossMin, fromInput, max], + ); + + return ( + dispatch(setFrom(parseInputChange(max.toFixed(), min, token?.decimals)))} + onAmountInputChange={(e) => dispatch(setFrom(parseInputChange(e.target.value, min, token?.decimals)))} + leftHeaderTitle={t('Amount')} + rightHeaderTitle={`${unitConverter(show)} ${formatSymbol( + selectToken && selectToken[chainId as TBridgeChainId]?.symbol, + )}`} + rightInputEle={ + + dispatch( + setSelectModal({ + open: true, + type: 'to', + }), + ) + } + /> + } + /> + ); +} diff --git a/src/page-components/Bridge/BridgeHeader/index.tsx b/src/page-components/Bridge/BridgeHeader/index.tsx new file mode 100644 index 00000000..4da5a327 --- /dev/null +++ b/src/page-components/Bridge/BridgeHeader/index.tsx @@ -0,0 +1,32 @@ +import { useMemo } from 'react'; +import { useLanguage } from 'i18n'; +import MainContentHeader from 'components/MainContentHeader'; +import { useWalletContext } from 'contexts/useWallet'; +import { useHomeContext } from '../HomeContext'; +import { setLimitAmountDescModal } from '../HomeContext/actions'; + +export default function BridgeHeader() { + const { t } = useLanguage(); + const [, { dispatch }] = useHomeContext(); + const [{ fromOptions, toOptions }] = useWalletContext(); + const isFromELF = fromOptions?.chainType === 'ELF'; + const isToELF = toOptions?.chainType === 'ELF'; + const title = useMemo(() => { + if (isFromELF && isToELF) { + return 'Bridge within aelf'; + } else if (isFromELF) { + return 'Bridge from aelf'; + } else { + return 'Bridge to aelf'; + } + }, [isFromELF, isToELF]); + return ( + dispatch(setLimitAmountDescModal(true)), + }} + /> + ); +} diff --git a/src/page-components/Bridge/ChangeIcon/index.tsx b/src/page-components/Bridge/ChangeIcon/index.tsx new file mode 100644 index 00000000..e065f1d5 --- /dev/null +++ b/src/page-components/Bridge/ChangeIcon/index.tsx @@ -0,0 +1,21 @@ +import clsx from 'clsx'; +import CommonImage from 'components/CommonImage'; +import { changeEnd, changeWallet } from 'contexts/useWallet/actions'; +import { useWalletActions } from 'contexts/useWallet/hooks'; +import { useThrottleCallback } from 'hooks'; +import { bridgeIcon } from 'assets/images'; +import styles from './styles.module.less'; + +export default function ChangeIcon({ className }: { className?: string }) { + const { dispatch } = useWalletActions(); + const onChange = useThrottleCallback(() => { + dispatch(changeWallet()); + dispatch(changeEnd()); + }, [dispatch, changeWallet]); + + return ( +
onChange()}> + +
+ ); +} diff --git a/src/page-components/Bridge/ChangeIcon/styles.module.less b/src/page-components/Bridge/ChangeIcon/styles.module.less new file mode 100644 index 00000000..e866eac4 --- /dev/null +++ b/src/page-components/Bridge/ChangeIcon/styles.module.less @@ -0,0 +1,21 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; + +.change-body { + width: 40px; + height: 40px; + border-radius: 6px; + border: 1px solid @border-5; +} + +.change-icon { + width: 16px; + height: 16px; + transform: rotate(90deg); +} + +@media screen and (max-width: @md-width) { + .change-icon { + transform: rotate(0); + } +} diff --git a/src/page-components/Bridge/CheckBoxInputRow/index.tsx b/src/page-components/Bridge/CheckBoxInputRow/index.tsx new file mode 100644 index 00000000..6bac17d4 --- /dev/null +++ b/src/page-components/Bridge/CheckBoxInputRow/index.tsx @@ -0,0 +1,89 @@ +import { useCallback, useMemo, useState } from 'react'; +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import { Checkbox, Input, Row } from 'antd'; +import CommonImage from 'components/CommonImage'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { useHomeContext } from '../HomeContext'; +import { setToChecked, setToAddress } from '../HomeContext/actions'; +import { isChainAddress } from 'utils'; +import { getNameByChainId } from 'utils/chain'; +import { clearIcon, questionFilledIcon } from 'assets/images'; +import styles from './styles.module.less'; +import CommonTooltipSwitchModal from 'components/CommonTooltipSwitchModal'; + +const TextArea = Input.TextArea; + +export default function CheckBoxInputRow() { + const { toWallet, isHomogeneous } = useWallet(); + const [{ toChecked, toAddress }, { dispatch }] = useHomeContext(); + const { chainId } = toWallet || {}; + + const [checked, setChecked] = useState(!!toChecked); + const [value, setValue] = useState(toAddress || ''); + const { t } = useLanguage(); + const isMD = useMediaQueries('md'); + const InputEle = useMemo(() => (isMD ? TextArea : Input), [isMD]); + + const checkBoxInputRowStatus = useMemo( + () => !!(toChecked && toAddress && !isChainAddress(toAddress, chainId)), + [chainId, toAddress, toChecked], + ); + + const status = useMemo(() => (checkBoxInputRowStatus ? 'error' : undefined), [checkBoxInputRowStatus]); + + const onInputChange = useCallback((value: string) => dispatch(setToAddress(value)), [dispatch]); + + const clearValue = useCallback(() => { + setValue(''); + onInputChange(''); + }, [onInputChange]); + + const networkName = chainId ? getNameByChainId(chainId) : ''; + + if (isHomogeneous) { + return null; + } + + return ( +
+ + { + setChecked(e.target.checked); + dispatch(setToChecked(e.target.checked)); + }}> + + {t('Send to another address manually')} + + + + + + + {checked && ( + + { + setValue(e.target.value); + onInputChange(e.target.value); + }} + maxLength={100} + status={status} + /> + {value && } + {status === 'error' && {t('input address error')}} + + )} +
+ ); +} diff --git a/src/page-components/Bridge/CheckBoxInputRow/styles.module.less b/src/page-components/Bridge/CheckBoxInputRow/styles.module.less new file mode 100644 index 00000000..3c75237c --- /dev/null +++ b/src/page-components/Bridge/CheckBoxInputRow/styles.module.less @@ -0,0 +1,84 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; + +.check-box { + :global { + .bridge-checkbox { + .bridge-checkbox-inner { + width: 16px; + height: 16px; + &::after { + left: 22%; + } + } + } + } +} + +.check-box-label-wrap { + gap: 8px; +} + +.check-box-label { + color: @font-13; + font-size: 16px; + line-height: 22px; + text-align: left; +} + +.question-icon { + width: 22px; + height: 22px; +} + +.input-box { + position: relative; +} + +.address-input { + margin-top: 10px; + width: 100%; + height: 48px; + border-radius: 6px; + font-size: 16px; + text-align: left; + padding-right: 36px; + + /* stylelint-disable-next-line selector-no-vendor-prefix */ + &::-webkit-input-placeholder { + height: 24px; + line-height: 24px; + font-size: 16px; + color: @font-18; + } +} + +input.address-input { + .text-overflow(1); +} + +textarea.address-input { + resize: none; +} + +.clear-icon { + position: absolute; + right: 12px; + top: 26px; + width: 16px; + height: 16px; + cursor: pointer; +} + +.input-error { + font-size: 12px; + line-height: 20px; + color: @font-6; + margin-top: 8px; +} + +@media screen and (max-width: @md-width) { + .clear-icon { + top: 32px; + } +} diff --git a/src/page-components/Bridge/HomeContext/actions.ts b/src/page-components/Bridge/HomeContext/actions.ts new file mode 100644 index 00000000..86265b01 --- /dev/null +++ b/src/page-components/Bridge/HomeContext/actions.ts @@ -0,0 +1,119 @@ +import { basicActions } from 'contexts/utils'; +import { CurrentWhitelistItem } from 'hooks/whitelist'; +import { CrossChainItem } from 'types/api'; +import BigNumber from 'bignumber.js'; +import { TokenInfo } from 'types'; + +export enum HomeActionsEnum { + setSelectModal = 'SET_SELECT_TOKEN_MODAL', + setAddModal = 'SET_ACCOUNT_MODAL', + setNetWorkDrawer = 'SET_NETWORK_DRAWER', + setSelectToken = 'SET_SELECT_TOKEN', + setFrom = 'SET_FROM', + setTo = 'SET_TO', + setToChecked = 'SET_TO_CHECKED', + setToAddress = 'SET_TO_ADDRESS', + setReceiveList = 'SET_RECEIVE_LIST', + setReceiveId = 'SET_RECEIVE_ID', + setActionLoading = 'SET_ACTION_LOADING', + destroy = 'DESTROY', + default = 'DEFAULT', + destroyModal = 'DESTROY_MODAL', + destroyState = 'DESTROY_STATE', + setLimitAmountModal = 'SET_LIMIT_AMOUNT_MODAL', + setLimitAmountDescModal = 'SET_LIMIT_AMOUNT_DESC_MODAL', +} + +export type HomeState = { + selectModal?: { + open: boolean; + type?: 'from' | 'to'; + }; + addModal?: boolean; + selectToken?: CurrentWhitelistItem; + fromInput?: string; + toInput?: string; + toChecked?: boolean; + toAddress?: string; + receiveList?: CrossChainItem[]; + receiveId?: string; + receiveItem?: CrossChainItem; + receivedList?: string[]; + fromBalance?: { balance: BigNumber; show: BigNumber; token: TokenInfo }; + actionLoading?: boolean; + crossMin?: number; + crossFee?: string; + limitAmountModal?: boolean; + limitAmountDescModal?: boolean; +}; + +export const DestroyModal = { + selectModal: { + open: false, + }, + addModal: undefined, +}; +export const DestroyState = { + selectModal: { + open: false, + }, + addModal: undefined, + fromInput: '', + toInput: '', + receiveList: undefined, + receiveId: undefined, + receiveItem: undefined, + actionLoading: undefined, +}; + +export const HomeActions = { + setSelectModal: (selectModal: HomeState['selectModal']) => { + const obj: any = { selectModal }; + if (selectModal?.open) { + obj.destroyModal = true; + } + return basicActions(HomeActionsEnum['setSelectModal'], obj); + }, + setAddModal: (addModal: boolean) => { + const obj: any = { addModal }; + if (addModal) { + obj.destroyModal = true; + } + return basicActions(HomeActionsEnum['setAddModal'], obj); + }, + setSelectToken: (selectToken?: CurrentWhitelistItem) => + basicActions(HomeActionsEnum['setSelectToken'], { selectToken }), + homeModalDestroy: () => basicActions(HomeActionsEnum['destroyModal']), + homeStateDestroy: () => basicActions(HomeActionsEnum['destroyState']), + setFrom: (input: string) => basicActions(HomeActionsEnum['setFrom'], { fromInput: input }), + setTo: (input: string) => basicActions(HomeActionsEnum['setTo'], { toInput: input }), + setToChecked: (checked: boolean) => basicActions(HomeActionsEnum['setToChecked'], { toChecked: checked }), + setToAddress: (address: string) => basicActions(HomeActionsEnum['setToAddress'], { toAddress: address }), + setActionLoading: (actionLoading?: boolean) => basicActions(HomeActionsEnum['setActionLoading'], { actionLoading }), + setReceiveList: (receiveList: CrossChainItem[]) => basicActions(HomeActionsEnum['setReceiveList'], { receiveList }), + setReceiveId: (receiveId?: string) => basicActions(HomeActionsEnum['setReceiveId'], { receiveId }), + setHomeState: (state: HomeState) => basicActions(HomeActionsEnum['default'], state), + homeDestroy: () => basicActions(HomeActionsEnum['destroy']), + setLimitAmountModal: (limitAmountModal: boolean) => + basicActions(HomeActionsEnum['setLimitAmountModal'], { limitAmountModal }), + setLimitAmountDescModal: (limitAmountDescModal: boolean) => + basicActions(HomeActionsEnum['setLimitAmountDescModal'], { limitAmountDescModal }), +}; + +export const { + setActionLoading, + setReceiveId, + setReceiveList, + setTo, + setToChecked, + setToAddress, + setFrom, + setSelectModal, + setSelectToken, + homeModalDestroy, + setAddModal, + homeDestroy, + setHomeState, + setLimitAmountModal, + setLimitAmountDescModal, +} = HomeActions; diff --git a/src/page-components/Bridge/HomeContext/hooks.ts b/src/page-components/Bridge/HomeContext/hooks.ts new file mode 100644 index 00000000..9581c17e --- /dev/null +++ b/src/page-components/Bridge/HomeContext/hooks.ts @@ -0,0 +1,7 @@ +import { useMemo } from 'react'; +import { useHomeContext } from '.'; + +export function useHomeActions() { + const [, actions] = useHomeContext(); + return useMemo(() => actions, [actions]); +} diff --git a/src/page-components/Bridge/HomeContext/index.tsx b/src/page-components/Bridge/HomeContext/index.tsx new file mode 100644 index 00000000..26198ae2 --- /dev/null +++ b/src/page-components/Bridge/HomeContext/index.tsx @@ -0,0 +1,187 @@ +import storages from 'constants/storages'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { BasicActions } from 'contexts/utils'; +import { useBalances } from 'hooks/useBalances'; +import { useCurrentWhitelist, useUserAddedToken } from 'hooks/whitelist'; +import React, { createContext, useCallback, useContext, useEffect, useMemo, useReducer } from 'react'; +import { useCookie, usePrevious } from 'react-use'; +import { isELFChain } from 'utils/aelfUtils'; +import { divDecimals } from 'utils/calculate'; +import { + DestroyModal, + DestroyState, + HomeActionsEnum, + HomeState, + setFrom, + setHomeState, + setReceiveId, + setSelectToken, + setTo, +} from './actions'; +import { DefaultWhitelistMap } from 'constants/index'; +import { crossTokenMin } from 'constants/misc'; +import { sliceDecimals } from 'utils/input'; +import { useCrossFee } from 'hooks/useCrossFee'; +import { useLatestRef } from 'hooks'; + +const defaultSelectToken = { + symbol: 'ELF', + ...DefaultWhitelistMap.ELF, +}; +const INITIAL_STATE = { + selectToken: defaultSelectToken, + toChecked: false, + toAddress: '', +}; +const HomeContext = createContext(INITIAL_STATE); +const ExpirationTime = 1000 * 60 * 5; +export function useHomeContext(): [ + HomeState, + BasicActions & { + addReceivedList: (s?: string) => void; + }, +] { + return useContext(HomeContext); +} + +//reducer +function reducer(state: HomeState, { type, payload }: { type: HomeActionsEnum; payload: any }) { + switch (type) { + case HomeActionsEnum.destroy: { + return {}; + } + case HomeActionsEnum.destroyState: { + return Object.assign({}, state, DestroyState); + } + case HomeActionsEnum.destroyModal: { + return Object.assign({}, state, DestroyModal); + } + default: { + const { destroyModal } = payload; + if (destroyModal) return Object.assign({}, state, DestroyModal, payload); + return Object.assign({}, state, payload); + } + } +} + +export default function Provider({ children }: { children: React.ReactNode }) { + const [state, dispatch]: [HomeState, BasicActions['dispatch']] = useReducer(reducer, INITIAL_STATE); + const [receivedValue, updateReceivedList] = useCookie(storages.crossChainReceived); + const { selectToken, fromInput, toInput, receiveList, receiveId } = state; + const { fromWallet, toWallet } = useWallet(); + const { chainId: fromChainId, account: fromAccount } = fromWallet || {}; + const preFromAccount = usePrevious(fromAccount); + const { chainId: toChainId, account: toAccount } = toWallet || {}; + const preToAccount = usePrevious(toAccount); + const tokenInfo = useUserAddedToken(selectToken?.symbol); + const tokens = useMemo(() => { + if (!fromChainId) return; + const { symbol, address, isNativeToken } = selectToken?.[fromChainId] || {}; + if (isELFChain(fromChainId)) return [symbol]; + if (isNativeToken) return [symbol]; + return [address]; + }, [fromChainId, selectToken]); + const crossFee = useCrossFee(); + const allWhitelist = useCurrentWhitelist(); + const latestAllWhitelist = useLatestRef(allWhitelist); + + const [[balance]] = useBalances(fromWallet, tokens); + useEffect(() => { + if (fromChainId && toChainId && (!selectToken?.[fromChainId] || !selectToken?.[toChainId])) { + if (tokenInfo) { + dispatch(setSelectToken(tokenInfo)); + } else { + if (latestAllWhitelist.current.some((i: any) => i[fromChainId]?.symbol === defaultSelectToken)) { + dispatch(setSelectToken(defaultSelectToken)); + } else { + dispatch(setSelectToken(allWhitelist[0])); + } + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fromChainId, toChainId, tokenInfo]); + + // auto fix token + useEffect(() => { + if (fromChainId && toChainId) { + const fromItem = selectToken?.[fromChainId]; + const toItem = selectToken?.[toChainId]; + const canForm = fromItem && !fromItem?.onlyTo; + const canTo = toItem && !toItem?.onlyFrom; + if (!tokenInfo || !(canTo && canForm)) { + if (latestAllWhitelist.current.some((i: any) => i[fromChainId]?.symbol === defaultSelectToken)) { + dispatch(setSelectToken(defaultSelectToken)); + } else { + dispatch(setSelectToken(allWhitelist[0])); + } + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fromChainId, selectToken, toChainId, tokenInfo]); + + useEffect(() => { + dispatch(setTo(fromInput || '')); + }, [fromInput]); + useEffect(() => { + const token = fromChainId ? selectToken?.[fromChainId] : undefined; + if (token) { + dispatch(setFrom(sliceDecimals(toInput, token?.decimals ?? 6))); + } else { + dispatch(setFrom(toInput || '')); + } + }, [fromChainId, selectToken, toInput]); + useEffect(() => { + if (preFromAccount !== fromAccount && preToAccount !== fromAccount) { + dispatch(setHomeState({ fromInput: '', toInput: '' })); + } + if (preToAccount !== fromAccount) dispatch(setReceiveId(undefined)); + }, [fromAccount, preFromAccount, preToAccount]); + useEffect(() => { + dispatch(setReceiveId(undefined)); + }, [toChainId]); + const receivedList = useMemo(() => { + let list = []; + try { + if (receivedValue) { + const tmpList = JSON.parse(receivedValue); + if (Array.isArray(tmpList)) list = tmpList; + } + } catch (error) { + console.debug(error, '=====error'); + } + return list; + }, [receivedValue]); + const addReceivedList = useCallback( + (id: string) => { + let list: string[] = []; + list = list.concat(receivedList); + list.push(id); + updateReceivedList(JSON.stringify(list), { + expires: new Date(new Date().getTime() + ExpirationTime), + }); + }, + [receivedList, updateReceivedList], + ); + const actions = useMemo(() => ({ dispatch, addReceivedList }), [addReceivedList]); + const receiveItem = useMemo(() => receiveList?.find((i) => i.id === receiveId), [receiveId, receiveList]); + const tmpList = useMemo(() => receiveList?.filter((i) => !receivedList.includes(i.id)), [receiveList, receivedList]); + const fromBalance = useMemo(() => { + if (!fromChainId) return; + const token = selectToken?.[fromChainId]; + if (!token) return; + return { balance, show: divDecimals(balance, token.decimals), token }; + }, [balance, fromChainId, selectToken]); + const crossMin = useMemo(() => { + if (!isELFChain(fromChainId) && isELFChain(toChainId) && selectToken) return crossTokenMin[selectToken.symbol]; + }, [fromChainId, selectToken, toChainId]); + + return ( + [{ ...state, receiveItem, receivedList, receiveList: tmpList, fromBalance, crossMin, crossFee }, actions], + [state, receiveItem, receivedList, tmpList, fromBalance, crossMin, crossFee, actions], + )}> + {children} + + ); +} diff --git a/src/page-components/Bridge/Info/index.tsx b/src/page-components/Bridge/Info/index.tsx new file mode 100644 index 00000000..384ca95b --- /dev/null +++ b/src/page-components/Bridge/Info/index.tsx @@ -0,0 +1,53 @@ +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { useHomeContext } from '../HomeContext'; +import { formatSymbol } from 'utils/token'; +import { sliceDecimals } from 'utils/input'; +import styles from './styles.module.less'; +import { useCheckTxnFeeEnoughAuto } from 'hooks/checkTxnFee'; +import { getCrossChainTime } from 'utils/time'; +import { useMemo } from 'react'; + +function InfoRow({ label, value, valueClassName }: { label: string; value?: string; valueClassName?: string }) { + return ( +
+
{label}
+
{value || '--'}
+
+ ); +} + +export default function Info() { + const { t } = useLanguage(); + const [{ crossFee, fromInput, toInput, selectToken }] = useHomeContext(); + const { isHomogeneous, toWallet, fromWallet } = useWallet(); + const { chainId: toChainId } = toWallet || {}; + const { chainId: fromChainId } = fromWallet || {}; + const token = toChainId ? selectToken?.[toChainId] : undefined; + + const time = useMemo(() => getCrossChainTime(fromChainId, toChainId), [toChainId, fromChainId]); + + const isShowTxnFeeEnoughTip = useCheckTxnFeeEnoughAuto(); + + if (!fromInput) { + return null; + } + + return ( +
+ + {isHomogeneous || !crossFee ? null : ( + + )} + +
+ ); +} diff --git a/src/page-components/Bridge/Info/styles.module.less b/src/page-components/Bridge/Info/styles.module.less new file mode 100644 index 00000000..e4997847 --- /dev/null +++ b/src/page-components/Bridge/Info/styles.module.less @@ -0,0 +1,12 @@ +@import '/src/assets/theme/color.less'; + +.info { + gap: 16px; + color: @font-13; + font-size: 16px; + line-height: 22px; +} + +.error-display { + color: @font-6; +} diff --git a/src/page-components/Bridge/LimitAmountDescModal/columns.tsx b/src/page-components/Bridge/LimitAmountDescModal/columns.tsx new file mode 100644 index 00000000..d217363a --- /dev/null +++ b/src/page-components/Bridge/LimitAmountDescModal/columns.tsx @@ -0,0 +1,82 @@ +import { useMemo } from 'react'; +import { useLanguage } from 'i18n'; +import { ReceiptRateLimitsInfo } from './contants'; +import { ChainId } from 'types'; + +import styles from './styles.module.less'; +import BigNumber from 'bignumber.js'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { formatSymbol } from 'utils/token'; + +export function useReceiptColumns() { + const { t } = useLanguage(); + const isMd = useMediaQueries('md'); + + return useMemo( + () => [ + { + title: t('eBridge limit rules Token'), + width: isMd ? 86 : 140, + key: 'token', + dataIndex: 'token', + render: (token: string) => {formatSymbol(token)}, + }, + { + title: t('eBridge limit rules Allowance'), + key: 'allowance', + dataIndex: 'allowance', + render: (allowance: number, item: { token: string; allowance: number }) => ( + + {t('About allowance', { + allowance: new BigNumber(allowance).toFormat(), + tokenSymbol: formatSymbol(item.token), + })} + + ), + }, + ], + [isMd, t], + ); +} + +export function useSwapColumns() { + const { t } = useLanguage(); + const isMd = useMediaQueries('md'); + + return useMemo( + () => [ + { + title: t('Rate limit Token'), + width: isMd ? 86 : 140, + key: 'token', + dataIndex: 'token', + render: (token: string) => {formatSymbol(token)}, + }, + { + title: t('Rate limit Attributes'), + key: 'allowance', + dataIndex: 'allowance', + render: (fromChain: ChainId, { capacity, token, refillRate, maximumTimeConsumed }: ReceiptRateLimitsInfo) => { + const bigCapacity = new BigNumber(capacity).toFormat(); + const bigRefillRate = new BigNumber(refillRate).toFormat(); + token = formatSymbol(token); + return ( + + {t('Rate limit capacity', { capacity: bigCapacity, token })} +
+ {t('Rate limit refill rate', { refillRate: bigRefillRate, token })} + + {t('Rate limit refill rate desc', { + token, + maximumTimeConsumed, + capacity: bigCapacity, + })} + +
+ ); + }, + }, + ], + [isMd, t], + ); +} diff --git a/src/page-components/Bridge/LimitAmountDescModal/contants.ts b/src/page-components/Bridge/LimitAmountDescModal/contants.ts new file mode 100644 index 00000000..36cbf2cf --- /dev/null +++ b/src/page-components/Bridge/LimitAmountDescModal/contants.ts @@ -0,0 +1,13 @@ +export interface ReceiptRateLimitsInfo { + token: string; + capacity: number; + refillRate: number; + maximumTimeConsumed: number; +} + +export interface RateLimitItem { + fromChain: string; + toChain: string; + receiptRateLimitsInfo: Array; + swapRateLimitsInfo: Array; +} diff --git a/src/page-components/Bridge/LimitAmountDescModal/index.tsx b/src/page-components/Bridge/LimitAmountDescModal/index.tsx new file mode 100644 index 00000000..d1c72520 --- /dev/null +++ b/src/page-components/Bridge/LimitAmountDescModal/index.tsx @@ -0,0 +1,124 @@ +import { Row, Col } from 'antd'; +import { useAsync } from 'react-use'; +import { request } from 'api'; +import CommonModal from 'components/CommonModal'; +import { useLanguage } from 'i18n'; +import CommonTable from 'components/CommonTable'; +import CommonButton from 'components/CommonButton'; +import { useHomeContext } from '../HomeContext'; +import { setLimitAmountDescModal } from '../HomeContext/actions'; +import { RateLimitItem } from './contants'; +import { useReceiptColumns, useSwapColumns } from './columns'; +import styles from './styles.module.less'; +import { formatNetworkName } from 'utils/chain'; + +export default function LimitAmountDescModal() { + const { t } = useLanguage(); + const [{ limitAmountDescModal }, { dispatch }] = useHomeContext(); + + const swapColumns = useSwapColumns(); + const receiptColumns = useReceiptColumns(); + + const dailyLimitData = useAsync(async () => { + const res = await request.cross.getDailyLimits(); + if (res.error) { + return []; + } + + return res.items; + }); + + const rateLimitData = useAsync(async () => { + const res = await request.cross.getRateLimits(); + if (res.error) { + return []; + } + + return res.items; + }); + + const closeModal = () => dispatch(setLimitAmountDescModal(false)); + + return ( + + +
+ + + {t('eBridge daily limit specification')} + + {t('eBridge daily limit specification desc')} + + + + + + + + + {t('eBridge rate limits')} + + + + {Object.values(t('eBridge rate limits desc', { returnObjects: true })).map((ele: any, idx: number) => ( +
+
+ {ele} + + {idx === 0 && + Object.values(t('eBridge rate limits desc section 1 list', { returnObjects: true })).map( + (ele: any, innerIdx: number) => ( + + {ele} + + ), + )} + + ))} + + + + {t('Supported tokens')} + + + + {rateLimitData?.value?.map((symoblItem: RateLimitItem) => ( + + + + {`${formatNetworkName(symoblItem.fromChain)} -> ${formatNetworkName(symoblItem.toChain)}`} + + + + + + + ))} + + + + + +
+ + {t('OK')} + +
+ + ); +} diff --git a/src/page-components/Bridge/LimitAmountDescModal/styles.module.less b/src/page-components/Bridge/LimitAmountDescModal/styles.module.less new file mode 100644 index 00000000..f7e49f80 --- /dev/null +++ b/src/page-components/Bridge/LimitAmountDescModal/styles.module.less @@ -0,0 +1,74 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + +.select-amount-desc-modal { + :global { + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-body { + padding: 0 24px 24px; + width: unset; + max-width: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + .@{app-prefix}-table { + & thead > tr > th, & tbody > tr > td { + padding: 9px 12px; + } + + & thead > tr > th { + background-color: @bg-13; + color: @font-15; + } + } + } + + .item-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + color: @font-2; + } + .item-text { + font-size: 14px; + line-height: 22px; + color: @font-5; + } + + .confirm-btn-box { + width: 100%; + display: flex; + justify-content: center; + } + + .confirm-btn { + flex: 1; + font-size: 16px; + height: 48px; + max-width: 322px; + font-weight: 600; + margin-top: 24px; + } + .table-data { + font-size: 14px; + line-height: 22px; + color: @font-5; + vertical-align: middle; + display: block; + &-sub { + color: @font-3; + } + } + .scroll-box { + padding-top: 24px; + overflow-y: scroll; + flex: 1; + max-height: 480px; + } +} \ No newline at end of file diff --git a/src/page-components/Bridge/SelectTokenModal/index.tsx b/src/page-components/Bridge/SelectTokenModal/index.tsx new file mode 100644 index 00000000..bde6f267 --- /dev/null +++ b/src/page-components/Bridge/SelectTokenModal/index.tsx @@ -0,0 +1,36 @@ +import { useMemo } from 'react'; +import CommonSelectTokenModal from 'components/CommonSelectTokenModal'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { useCurrentWhitelist } from 'hooks/whitelist'; +import { useHomeContext } from '../HomeContext'; +import { setSelectModal, setSelectToken, homeModalDestroy } from '../HomeContext/actions'; +import { TBridgeChainId } from 'constants/chain'; + +export default function SelectTokenModal() { + const [{ selectModal, selectToken }, { dispatch }] = useHomeContext(); + const { fromWallet, toWallet } = useWallet(); + const { chainId } = fromWallet || {}; + const { chainId: toChainId } = toWallet || {}; + const allWhitelist = useCurrentWhitelist(); + + const tokenList = useMemo(() => { + return allWhitelist.map((item) => ({ + ...item, + displaySymbol: item[(selectModal?.type === 'from' ? chainId : toChainId) as TBridgeChainId]?.symbol, + })); + }, [allWhitelist, chainId, selectModal?.type, toChainId]); + + return ( + { + dispatch(setSelectToken(item)); + dispatch(homeModalDestroy()); + }} + onClose={() => dispatch(setSelectModal({ open: false }))} + /> + ); +} diff --git a/src/page-components/Bridge/SelectTokenModal/styles.module.less b/src/page-components/Bridge/SelectTokenModal/styles.module.less new file mode 100644 index 00000000..406efbf4 --- /dev/null +++ b/src/page-components/Bridge/SelectTokenModal/styles.module.less @@ -0,0 +1,76 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +.select-modal { + :global { + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-body { + min-width: 448px; + max-width: 448px; + padding-left: 0 !important; + padding-right: 0 !important; + } + } + .input-search { + height: 40px; + border-radius: 8px; + width: calc(100% - 64px); + align-self: center; + :global { + .@{app-prefix}-input-suffix { + font-size: 16px; + } + } + } + .token-list { + margin-top: 8px; + height: 270px; + overflow: auto; + .token-item { + padding: 14px 32px; + line-height: 28px; + .token-logo { + margin-right: 16px; + } + &:hover:not(.token-item-selected) { + color: @font-1; + background: @bg-3; + } + } + .token-item-selected { + color: @font-1; + background: @bg-8; + } + } + .token-list-add { + margin-bottom: 60px; + } + .bottom-row { + padding: 16px 24px; + position: absolute; + left: 0; + right: 0; + bottom: 0; + border-top: 1px solid @border-3; + .row-body { + background: @bg-7; + height: 48px; + border-radius: 8px; + a { + color: @font-1; + } + } + } +} +@media screen and (max-width: @md-width) { + .select-modal { + :global { + .@{app-prefix}-modal-header { + border-width: 0; + } + } + } +} diff --git a/src/page-components/Bridge/Wallet/NetworkSelect/index.tsx b/src/page-components/Bridge/Wallet/NetworkSelect/index.tsx new file mode 100644 index 00000000..483160e5 --- /dev/null +++ b/src/page-components/Bridge/Wallet/NetworkSelect/index.tsx @@ -0,0 +1,95 @@ +import Network from 'components/Network'; +import { NetworkList } from 'constants/index'; +import { basicModalView } from 'contexts/useModal/actions'; +import { useModalDispatch } from 'contexts/useModal/hooks'; +import { setFromWallet, setToWallet } from 'contexts/useWallet/actions'; +import { useWalletActions } from 'contexts/useWallet/hooks'; +import { usePortkey, useWeb3 } from 'hooks/web3'; +import { memo, useCallback, useMemo } from 'react'; +import { NetworkType, Web3Type } from 'types'; +import { isELFChain } from 'utils/aelfUtils'; +import { switchChain } from 'utils/network'; +import { useChain } from 'contexts/useChain'; +import { isSelectPortkey } from 'utils/portkey'; +import { Accounts, ChainId } from '@portkey/provider-types'; +import CommonMessage from 'components/CommonMessage'; +import { useHomeContext } from '../../HomeContext'; +import { IS_ONLY_SIDE_CHAIN_LIST } from 'constants/misc'; +import { SupportedELFChainId } from 'constants/chain'; +import { isTonChain } from 'utils'; +import { handleErrorMessage } from 'utils/error'; + +function NetworkSelect({ wallet, isFrom }: { wallet?: Web3Type; isFrom?: boolean }) { + const { dispatch } = useWalletActions(); + const { connector: web3Connector, chainId: web3ChainId, account: web3Account } = useWeb3(); + const [{ selectToken }] = useHomeContext(); + + const { chainId, connector } = wallet || {}; + const portkeyWallet = usePortkey(); + const [{ selectELFWallet }] = useChain(); + const modalDispatch = useModalDispatch(); + + const onChange = useCallback( + async (info: NetworkType['info']) => { + const _wallet = portkeyWallet; + const selectPortkey = isSelectPortkey(selectELFWallet); + if (selectPortkey && _wallet?.isActive && isELFChain(info.chainId)) { + const accounts = (_wallet as { accounts: Accounts }).accounts; + + if (!accounts?.[info.chainId as ChainId]) { + modalDispatch( + basicModalView.setPortketNotConnectModal({ + visible: true, + chainId: info.chainId, + }), + ); + return; + } + } + const setWallet = isFrom ? setFromWallet : setToWallet; + if (typeof info.chainId === 'string') { + dispatch( + setWallet({ chainType: 'ELF', chainId: info.chainId, isPortkey: selectPortkey && portkeyWallet.isActive }), + ); + } else { + if (isTonChain(info.chainId)) { + dispatch(setWallet({ chainType: 'TON' })); + } else { + dispatch(setWallet({ chainType: 'ERC' })); + try { + await switchChain(info, !isELFChain(info.chainId) ? web3Connector : connector, !!web3Account, web3ChainId); + } catch (error: any) { + CommonMessage.error(handleErrorMessage(error)); + } + } + } + }, + [ + connector, + dispatch, + isFrom, + modalDispatch, + portkeyWallet, + selectELFWallet, + web3Account, + web3ChainId, + web3Connector, + ], + ); + + const networkList = useMemo(() => { + let _list = NetworkList.filter((item) => !isELFChain(item.info.chainId)); + const selectPortkey = isSelectPortkey(selectELFWallet) && portkeyWallet.isActive; + if (!selectPortkey || isELFChain(wallet?.chainId)) { + _list = NetworkList; + } else if (isELFChain(chainId)) { + _list = NetworkList.filter((item) => isELFChain(item.info.chainId)); + } + const isOnlySideChain = IS_ONLY_SIDE_CHAIN_LIST.includes(selectToken?.symbol || ''); + return _list.filter((i) => (isOnlySideChain ? i.info.chainId !== SupportedELFChainId.AELF : true)); + }, [chainId, portkeyWallet.isActive, selectELFWallet, selectToken, wallet?.chainId]); + + return ; +} + +export default memo(NetworkSelect); diff --git a/src/page-components/Bridge/Wallet/index.tsx b/src/page-components/Bridge/Wallet/index.tsx new file mode 100644 index 00000000..6018304b --- /dev/null +++ b/src/page-components/Bridge/Wallet/index.tsx @@ -0,0 +1,26 @@ +import { useLanguage } from 'i18n'; +import clsx from 'clsx'; +import ConnectWallet from 'components/ConnectWallet'; +import NetworkSelect from './NetworkSelect'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { useWalletContext } from 'contexts/useWallet'; +import styles from './styles.module.less'; + +export default function Wallet({ className, isFrom = false }: { className?: string; isFrom?: boolean }) { + const { t } = useLanguage(); + const [{ fromOptions, toOptions }] = useWalletContext(); + const { fromWallet, toWallet } = useWallet(); + + const wallet = isFrom ? fromWallet : toWallet; + const chainType = isFrom ? fromOptions?.chainType : toOptions?.chainType; + + return ( +
+
+ {t(isFrom ? 'From' : 'To')} + +
+ +
+ ); +} diff --git a/src/page-components/Bridge/Wallet/styles.module.less b/src/page-components/Bridge/Wallet/styles.module.less new file mode 100644 index 00000000..3a0589a7 --- /dev/null +++ b/src/page-components/Bridge/Wallet/styles.module.less @@ -0,0 +1,15 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/font.less'; + +.wallet { + gap: 8px; + .wallet-label { + font-size: 16px; + font-weight: 400; + line-height: 22px; + } + .wallet-label-text { + color: @font-13; + } +} diff --git a/src/page-components/Bridge/index.tsx b/src/page-components/Bridge/index.tsx new file mode 100644 index 00000000..b4f66bbe --- /dev/null +++ b/src/page-components/Bridge/index.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { memo } from 'react'; +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import PageHead from 'components/PageHead'; +import HomeProvider from './HomeContext'; +import BridgeHeader from './BridgeHeader'; +import Wallet from './Wallet'; +import ChangeIcon from './ChangeIcon'; +import AmountRow from './AmountRow'; +import CheckBoxInputRow from './CheckBoxInputRow'; +import Info from './Info'; +import ActionButton from './ActionButton'; +import LimitAmountDescModal from './LimitAmountDescModal'; +import SelectTokenModal from './SelectTokenModal'; +import AddTokenModal from './AddTokenModal'; +import styles from './styles.module.less'; + +function Bridge() { + const { t } = useLanguage(); + return ( + + +
+ +
+ + + +
+
+ + +
+ + +
+ + + +
+ ); +} + +export default memo(Bridge); diff --git a/src/page-components/Bridge/styles.module.less b/src/page-components/Bridge/styles.module.less new file mode 100644 index 00000000..151d32ae --- /dev/null +++ b/src/page-components/Bridge/styles.module.less @@ -0,0 +1,38 @@ +@import '/src/assets/theme/constants.less'; + +.bridge-content { + gap: 48px; +} + +.bridge-wallet-part { + gap: 24px; +} + +.bridge-wallet { + flex: 1; +} + +.change-icon { + flex-shrink: 0; + margin: 4px 0; +} + +.bridge-amount-part { + gap: 8px; +} + +@media screen and (max-width: @md-width) { + .bridge-content { + gap: 24px; + } + + .bridge-wallet-part { + flex-direction: column; + align-items: stretch; + gap: 8px; + } + + .change-icon { + margin: auto; + } +} diff --git a/src/page-components/Bridge/useLimitAmountModal/api.ts b/src/page-components/Bridge/useLimitAmountModal/api.ts new file mode 100644 index 00000000..c6452c55 --- /dev/null +++ b/src/page-components/Bridge/useLimitAmountModal/api.ts @@ -0,0 +1,140 @@ +import { gql } from '@apollo/client'; +import { ChainId } from 'types'; +import { LimitDataProps } from './constants'; +import BigNumber from 'bignumber.js'; +import moment from 'moment'; +import { getChainIdToMap, getShortNameByChainId } from 'utils/chain'; +import { isELFChain } from 'utils/aelfUtils'; +import { requestGql } from 'api'; +import CommonMessage from 'components/CommonMessage'; +import { INDEXER_URL } from 'constants/index'; + +interface LimitDataByGqlProps { + fromChainId: ChainId; + toChainId: ChainId; + symbol: string; + limitType: 'Receipt' | 'Swap'; + defaultDailyLimit: number; + refreshTime: string; + currentDailyLimit: string; + capacity: string; + refillRate: string; + isEnable: boolean; + currentBucketTokenAmount: string; + bucketUpdateTime: string; +} + +const limitQuery = gql` + query queryCrossChainLimitInfos($input: GetCrossChainLimitInfoInput!) { + queryCrossChainLimitInfos(input: $input) { + totalRecordCount + totalRecordCount + dataList: data { + id + fromChainId + toChainId + symbol + limitType + defaultDailyLimit + refreshTime + currentDailyLimit + capacity + refillRate + isEnable + currentBucketTokenAmount + bucketUpdateTime + } + } + } +`; + +export const getLimitData = async ({ + fromChainId, + toChainId, + fromSymbol, + toSymbol, +}: { + fromChainId?: ChainId; + fromSymbol?: string; + toChainId?: ChainId; + toSymbol?: string; +}): Promise => { + try { + const client = requestGql({ + uri: INDEXER_URL, + }); + const result = await client.query<{ + queryCrossChainLimitInfos: { + dataList: Array; + }; + }>({ + query: limitQuery, + variables: { + input: { + fromChainId: isELFChain(fromChainId) ? getShortNameByChainId(fromChainId) : getChainIdToMap(fromChainId), + toChainId: isELFChain(toChainId) ? getShortNameByChainId(toChainId) : getChainIdToMap(toChainId), + symbol: isELFChain(fromChainId) ? fromSymbol : toSymbol, + skipCount: 0, + }, + }, + }); + + if (result.error) { + throw new Error(result.error.message); + } + + if (!result?.data?.queryCrossChainLimitInfos?.dataList?.length) { + return; + } + + const { + refreshTime, + currentDailyLimit, + capacity, + currentBucketTokenAmount, + refillRate, + bucketUpdateTime, + defaultDailyLimit, + isEnable, + } = result.data.queryCrossChainLimitInfos.dataList[0]; + + let currentCapcity = new BigNumber(currentBucketTokenAmount); + let remain = new BigNumber(currentDailyLimit); + const fillRate = new BigNumber(refillRate); + const maxCapcity = new BigNumber(capacity); + + const utcRefreshTime = moment(refreshTime).utc(); + const utcBucketUpdateTime = moment(bucketUpdateTime).utc(); + const utcNow = moment.utc(); + const midnightToday = moment(utcNow.format('YYYY-MM-DD')); + + if (process.env.NEXT_PUBLIC_APP_ENV === 'test1') { + // test1 Updated hourly + if (utcRefreshTime.isBefore(utcNow.clone().subtract(1, 'h'))) { + remain = new BigNumber(defaultDailyLimit); + } + } else { + // Updated every day + if (utcRefreshTime.isBefore(midnightToday)) { + remain = new BigNumber(defaultDailyLimit); + } + } + + if (utcBucketUpdateTime.isBefore(utcNow)) { + const differenceTime = new BigNumber(utcNow.valueOf() - utcBucketUpdateTime.valueOf()).idiv(1000); + const newCurrentCapcity = fillRate.times(differenceTime).plus(currentCapcity); + currentCapcity = BigNumber.min(capacity, newCurrentCapcity); + } + + return { + fillRate, + maxCapcity, + remain, + currentCapcity, + isEnable, + }; + } catch (error: any) { + CommonMessage.error(error.message); + console.log('error: ', error); + } +}; diff --git a/src/page-components/Bridge/useLimitAmountModal/constants.ts b/src/page-components/Bridge/useLimitAmountModal/constants.ts new file mode 100644 index 00000000..66575d66 --- /dev/null +++ b/src/page-components/Bridge/useLimitAmountModal/constants.ts @@ -0,0 +1,31 @@ +import BigNumber from 'bignumber.js'; +import { ChainId } from 'types'; + +export const TokenFormat: { [key: string]: number } = { + ETH: 6, + BNB: 3, + USDC: 0, + USDT: 0, + DAI: 0, + ELF: 0, +}; + +export interface LimitDataProps { + remain: BigNumber; + maxCapcity: BigNumber; + currentCapcity: BigNumber; + fillRate: BigNumber; + isEnable?: boolean; + checkMaxCapcity?: boolean; + checkCurrentCapcity?: boolean; +} + +export interface ICrossInfo { + fromChainId?: ChainId; + toChainId?: ChainId; + fromDecimals?: number; + toDecimals?: number; + symbol?: string; + fromSymbol?: string; + toSymbol?: string; +} diff --git a/src/page-components/Bridge/useLimitAmountModal/index.tsx b/src/page-components/Bridge/useLimitAmountModal/index.tsx new file mode 100644 index 00000000..e3d0b0a9 --- /dev/null +++ b/src/page-components/Bridge/useLimitAmountModal/index.tsx @@ -0,0 +1,250 @@ +import React, { useCallback, useMemo, useState } from 'react'; +import { Row, Col } from 'antd'; +import CommonModal from 'components/CommonModal'; +import CommonButton from 'components/CommonButton'; +import { useLanguage } from 'i18n'; +import BigNumber from 'bignumber.js'; +import { ICrossInfo, LimitDataProps } from './constants'; +import { useBridgeOutContract, useLimitContract, usePoolContract } from 'hooks/useContract'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { isELFChain } from 'utils/aelfUtils'; +import { getNameByChainId } from 'utils/chain'; +import { getReceiptLimit, getSwapLimit, getSwapId } from 'utils/crossChain'; +import { useHomeContext } from '../HomeContext'; +import { divDecimals } from 'utils/calculate'; + +import styles from './styles.module.less'; +import { CrossChainItem } from 'types/api'; +import { ChainId, TokenInfo } from 'types'; +import { calculateMinValue, calculateTime, formatToken, getLimitDataByGQL } from './utils'; +import { getTotalLiquidity } from 'utils/pools'; + +export default function useLimitAmountModal() { + const { t } = useLanguage(); + + const [visible, setVisible] = useState(false); + const [modalTxt, setModalTxt] = useState(''); + + const { fromWallet, toWallet } = useWallet(); + const { chainId: fromChainId } = fromWallet || {}; + const { chainId: toChainId } = toWallet || {}; + + const [{ selectToken }] = useHomeContext(); + + const bridgeOutContract = useBridgeOutContract(toChainId, toWallet?.isPortkey); + + const getTokenInfo = useCallback( + (chainId?: ChainId) => { + if (!chainId) return; + return selectToken?.[chainId]; + }, + [selectToken], + ); + const limitContract = useLimitContract(fromChainId, toChainId, getTokenInfo(fromChainId)?.symbol); + + const poolContract = usePoolContract(toChainId, undefined, toWallet?.isPortkey, getTokenInfo(fromChainId)?.symbol); + + const getLimitDataByContract = useCallback( + async function ( + type: 'transfer' | 'swap', + crossInfo: ICrossInfo, + decimals?: number, + ): Promise { + let response: LimitDataProps | undefined; + + if (type === 'transfer') { + response = await getReceiptLimit({ + limitContract, + ...crossInfo, + }); + } else { + const swapId = await getSwapId({ bridgeOutContract, ...crossInfo }); + response = await getSwapLimit({ limitContract, ...crossInfo, swapId }); + } + + if (response) { + response = { + remain: divDecimals(response.remain, decimals), + maxCapcity: divDecimals(response.maxCapcity, decimals), + currentCapcity: divDecimals(response.currentCapcity, decimals), + fillRate: divDecimals(response.fillRate, decimals), + isEnable: response.isEnable, + }; + } + + return response; + }, + [bridgeOutContract, limitContract], + ); + + const checkDailyLimit = useCallback( + function (input: BigNumber, { remain }: LimitDataProps, { fromChainId, toChainId, symbol }: ICrossInfo): boolean { + if (remain.isZero()) { + setModalTxt( + t('have reached the daily limit', { + fromChain: getNameByChainId(fromChainId), + toChain: getNameByChainId(toChainId), + tokenSymbol: symbol, + }), + ); + return true; + } + + if (remain.lt(input)) { + const amount = formatToken(remain, symbol); + setModalTxt( + t('have a daily limit and your current transaction', { + fromChain: getNameByChainId(fromChainId), + toChain: getNameByChainId(toChainId), + tokenSymbol: symbol, + amount, + }), + ); + return true; + } + + return false; + }, + [t], + ); + + const checkCapacity = useCallback( + function ( + input: BigNumber, + { maxCapcity, currentCapcity, fillRate, checkMaxCapcity, checkCurrentCapcity }: LimitDataProps, + { fromChainId, toChainId, symbol }: ICrossInfo, + ): boolean { + if (maxCapcity.lt(input) && checkMaxCapcity) { + const amount = formatToken(maxCapcity, symbol); + setModalTxt( + t(`Your current transaction exceeds the capacity and can't be processed`, { + fromChain: getNameByChainId(fromChainId), + toChain: getNameByChainId(toChainId), + tokenSymbol: symbol, + amount, + }), + ); + return true; + } + + if (currentCapcity.lt(input) && checkCurrentCapcity) { + const amount = formatToken(currentCapcity, symbol); + const time = calculateTime(input, currentCapcity, fillRate); + setModalTxt( + t('have a maximum capacity and your current transaction exceeds the available capacity', { + fromChain: getNameByChainId(fromChainId), + toChain: getNameByChainId(toChainId), + tokenSymbol: symbol, + amount, + time, + }), + ); + return true; + } + + return false; + }, + [t], + ); + + const checkToLiquidity = useCallback( + async (input: BigNumber, { fromChainId, toChainId, symbol }: ICrossInfo, tokenInfo?: TokenInfo) => { + try { + const totalLiquidity = await getTotalLiquidity({ + poolContract, + tokenContract: { + address: tokenInfo?.address, + chainId: toChainId, + }, + symbol: tokenInfo?.symbol, + }); + + const toLiquidity = divDecimals(totalLiquidity, tokenInfo?.decimals); + + if (toLiquidity.lt(input)) { + const amount = formatToken(toLiquidity, symbol); + setModalTxt( + t(`Insufficient liquidity tip`, { + fromChain: getNameByChainId(fromChainId), + toChain: getNameByChainId(toChainId), + tokenSymbol: symbol, + amount, + }), + ); + return true; + } + } catch (error) { + console.log(error, '=======checkToLiquidity'); + } + }, + [poolContract, t], + ); + + const checkLimitAndRate = useCallback( + async function ( + type: 'transfer' | 'swap', + amount?: BigNumber | string | number | null, + receiveItem?: CrossChainItem, + ) { + if ((!amount && type === 'transfer') || (type === 'swap' && !receiveItem)) { + return true; + } + + const input = new BigNumber(amount || receiveItem?.transferAmount || 0); + const fromTokenInfo = getTokenInfo(fromChainId); + const toTokenInfo = getTokenInfo(toChainId); + const crossInfo: ICrossInfo = { + toChainId: toChainId, + toSymbol: toTokenInfo?.symbol, + toDecimals: toTokenInfo?.decimals, + fromChainId: fromChainId, + fromDecimals: fromTokenInfo?.decimals, + fromSymbol: fromTokenInfo?.symbol, + symbol: fromTokenInfo?.symbol, + }; + + const result = await (isELFChain(fromChainId) + ? getLimitDataByGQL(crossInfo, crossInfo?.fromDecimals) + : getLimitDataByContract(type, crossInfo, crossInfo?.fromDecimals)); + + const limitAndRateData = calculateMinValue(result); + + if (!limitAndRateData) return true; + + if ( + checkCapacity(input, limitAndRateData, crossInfo) || + checkDailyLimit(input, limitAndRateData, crossInfo) || + (await checkToLiquidity(input, crossInfo, toTokenInfo)) + ) { + setVisible(true); + return true; + } + }, + [getTokenInfo, fromChainId, toChainId, getLimitDataByContract, checkCapacity, checkDailyLimit, checkToLiquidity], + ); + + const closeModal = () => setVisible(false); + + const limitAmountModal = useMemo(() => { + return ( + + +
+ {modalTxt} + + + + {t('OK')} + + + + + ); + }, [visible, t, modalTxt]); + + return [limitAmountModal, checkLimitAndRate] as const; +} diff --git a/src/page-components/Bridge/useLimitAmountModal/styles.module.less b/src/page-components/Bridge/useLimitAmountModal/styles.module.less new file mode 100644 index 00000000..fe2fb86e --- /dev/null +++ b/src/page-components/Bridge/useLimitAmountModal/styles.module.less @@ -0,0 +1,40 @@ + +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + + +.limit-amount-modal { + max-width: 492px; + :global { + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-body { + padding: 24px; + width: unset; + min-width: 492px; + } + } + .text { + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22px; + color: @font-5 + } + + .confirm-btn-box { + display: flex; + justify-content: center; + } + + .confirm-btn { + font-size: 16px; + height: 48px; + width: 322px; + font-weight: 600; + } +} \ No newline at end of file diff --git a/src/page-components/Bridge/useLimitAmountModal/utils.ts b/src/page-components/Bridge/useLimitAmountModal/utils.ts new file mode 100644 index 00000000..0c254cd9 --- /dev/null +++ b/src/page-components/Bridge/useLimitAmountModal/utils.ts @@ -0,0 +1,71 @@ +import BigNumber from 'bignumber.js'; +import { ICrossInfo, LimitDataProps, TokenFormat } from './constants'; +import { getLimitData } from './api'; +import { divDecimals } from 'utils/calculate'; + +export const calculateMinValue = ( + input1: LimitDataProps | undefined, + input2?: LimitDataProps | undefined, +): LimitDataProps | undefined => { + if (!input1) { + return; + } + + if (!input2) { + if (input1.isEnable) { + input1.checkMaxCapcity = true; + input1.checkCurrentCapcity = true; + } + return input1; + } + + input1.checkMaxCapcity = true; + input1.checkCurrentCapcity = true; + + if (input1.remain.gt(input2.remain)) { + input1.remain = input2.remain; + } + + if (input1.isEnable && input2.isEnable) { + if (input1.maxCapcity.gt(input2.maxCapcity)) { + input1.maxCapcity = input2.maxCapcity; + } + } else if (input2.isEnable) { + input1.maxCapcity = input2.maxCapcity; + input1.checkCurrentCapcity = false; + } else if (!input1.isEnable) { + input1.checkMaxCapcity = false; + input1.checkCurrentCapcity = false; + } + + return input1; +}; + +export const formatToken = (input: BigNumber, symbol?: string): string => { + if (!symbol || !TokenFormat[symbol]) { + return input.dp(2, BigNumber.ROUND_DOWN).toFormat(); + } + return input.dp(TokenFormat[symbol], BigNumber.ROUND_DOWN).toFormat(); +}; + +export const calculateTime = (input: BigNumber, currentCapcity: BigNumber, fillRate: BigNumber): string => + input.minus(currentCapcity).div(fillRate).idiv(60).plus(1).toFormat(); + +export const getLimitDataByGQL = async ( + crossInfo: ICrossInfo, + decimals?: number, +): Promise => { + const response = await getLimitData(crossInfo); + + if (!response) { + return; + } + + return { + remain: divDecimals(response.remain, decimals), + maxCapcity: divDecimals(response.maxCapcity, decimals), + currentCapcity: divDecimals(response.currentCapcity, decimals), + fillRate: divDecimals(response.fillRate, decimals), + isEnable: response.isEnable, + }; +}; diff --git a/src/page-components/ListingApplication/InitializeTokenPool/index.tsx b/src/page-components/ListingApplication/InitializeTokenPool/index.tsx new file mode 100644 index 00000000..ccb34027 --- /dev/null +++ b/src/page-components/ListingApplication/InitializeTokenPool/index.tsx @@ -0,0 +1,58 @@ +import { ROUTE_PATHS } from 'constants/link'; +import { useRouter } from 'next/router'; +import { memo, useCallback, useMemo } from 'react'; +import styles from './styles.module.less'; +import { VIEW_PROGRESS } from 'constants/listingApplication'; +import { viewProgressIcon } from 'assets/images'; +import CommonImage from 'components/CommonImage'; +import CommonButton from 'components/CommonButton'; + +function InitializeTokenPool({ networks }: { networks: { name: string }[] }) { + const router = useRouter(); + + const networksString = useMemo(() => { + let str = ''; + if (networks.length === 1) { + str = networks[0].name + ' network'; + } else if (networks.length === 2) { + str = networks[0].name + ' and ' + networks[1].name + ' networks'; + } else { + networks?.forEach((item, index) => { + if (index === networks.length - 1) { + str += 'and ' + item.name + ' networks'; + } else if (index === networks.length - 2) { + str += item.name + ' '; + } else { + str += item.name + ', '; + } + }); + } + + return str; + }, [networks]); + + const handleGoMyApplications = useCallback(() => { + router.push(ROUTE_PATHS.MY_APPLICATIONS); + }, [router]); + + return ( +
+
Initialize Token Pool
+
+ +
+ {`The token is successfully created on ${networksString} and the initialization of the token pool is in progress.`} +
+ +
+ {`Initializing the token pool is expected to be completed in 1-2 business days.`} +
+ + {VIEW_PROGRESS} + +
+
+ ); +} + +export default memo(InitializeTokenPool); diff --git a/src/page-components/ListingApplication/InitializeTokenPool/styles.module.less b/src/page-components/ListingApplication/InitializeTokenPool/styles.module.less new file mode 100644 index 00000000..9bacf369 --- /dev/null +++ b/src/page-components/ListingApplication/InitializeTokenPool/styles.module.less @@ -0,0 +1,55 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/font.less'; + +.component-title { + font-size: 20px; + line-height: 28px; + .font-family-semibold(); + + margin-bottom: 32px; + @media screen and (max-width: @md-width) { + margin-bottom: 24px; + } +} + +.init-token-pool-body { + display: flex; + flex-direction: column; + align-items: center; + padding: 32px 24px; + @media screen and (max-width: @md-width) { + padding: 32px 0; + } +} + +.view-progress-icon { + width: 48px; + height: 48px; +} + +.init-token-pool-text { + font-size: 16px; + line-height: 24px; + .font-family-medium(); + + text-align: center; + margin: 12px auto 4px; +} + +.init-token-pool-desc { + font-size: 14px; + line-height: 22px; + color: @font-13; + margin-bottom: 48px; +} + +.view-progress-button { + width: 240px; + height: 48px; + font-size: 16px; + line-height: 24px; + color: @font-14; + border: 1px solid @font-14; + .font-family-medium(); +} diff --git a/src/page-components/ListingApplication/ListingTip/index.tsx b/src/page-components/ListingApplication/ListingTip/index.tsx new file mode 100644 index 00000000..b0106cf3 --- /dev/null +++ b/src/page-components/ListingApplication/ListingTip/index.tsx @@ -0,0 +1,39 @@ +import { infoCircleIcon } from 'assets/images'; +import CommonTooltipSwitchModal, { ICommonTooltipSwitchModalRef } from 'components/CommonTooltipSwitchModal'; +import { useRef } from 'react'; +import styles from './styles.module.less'; +import CommonImage from 'components/CommonImage'; + +export default function ListingTip({ + tip, + title, + modalTitle, + customChildren, +}: { + tip: React.ReactNode; + title?: string; + modalTitle?: string; + customChildren?: React.ReactNode; +}) { + const tooltipSwitchModalsRef = useRef(null); + + return ( + { + tooltipSwitchModalsRef.current = ref; + }} + tooltipProps={{ overlayClassName: styles['tip-tooltip'] }} + modalProps={{ title: modalTitle || title, zIndex: 1001 }} + modalWidth={377} + tip={tip}> + {customChildren ? ( +
tooltipSwitchModalsRef.current?.open()}>{customChildren}
+ ) : ( +
tooltipSwitchModalsRef.current?.open()}> + + {title} +
+ )} +
+ ); +} diff --git a/src/page-components/ListingApplication/ListingTip/styles.module.less b/src/page-components/ListingApplication/ListingTip/styles.module.less new file mode 100644 index 00000000..2f4dd236 --- /dev/null +++ b/src/page-components/ListingApplication/ListingTip/styles.module.less @@ -0,0 +1,27 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.tip-title { + display: flex; + align-items: center; + gap: 4px; + cursor: pointer; +} + +.tip-title-icon { + width: 16px; + height: 16px; + path { + fill: @font-13; + } +} + +.tip-title-text { + font-size: 14px; + line-height: 22px; + color: @font-13; +} + +div.tip-tooltip { + max-width: 268px; +} diff --git a/src/page-components/ListingApplication/SelectChain/CreationProgressModal/index.tsx b/src/page-components/ListingApplication/SelectChain/CreationProgressModal/index.tsx new file mode 100644 index 00000000..215942cb --- /dev/null +++ b/src/page-components/ListingApplication/SelectChain/CreationProgressModal/index.tsx @@ -0,0 +1,404 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Drawer } from 'antd'; +import CommonMessage from 'components/CommonMessage'; +import CommonModal from 'components/CommonModal'; +import CommonSteps, { ICommonStepsProps } from 'components/CommonSteps'; +import Remind, { RemindType } from 'components/Remind'; +import CommonButton from 'components/CommonButton'; +import ListingTip from '../../ListingTip'; +import { useMobile } from 'contexts/useStore/hooks'; +import { useWeb3 } from 'hooks/web3'; +import { getApplicationIssue, prepareBindIssue } from 'utils/api/application'; +import { getTransactionReceiptAutoRetry } from 'utils/wagmi'; +import { getBridgeChainInfo, getChainIdByAPI } from 'utils/chain'; +import { handleListingErrorMessage } from 'utils/error'; +import { ApplicationChainStatusEnum, TApplicationChainStatusItem, TPrepareBindIssueRequest } from 'types/api'; +import styles from './styles.module.less'; +import { useCallEVMCreateOfficialToken } from 'hooks/token'; +import useLockCallback from 'hooks/useLockCallback'; +import { useLatestRef } from 'hooks'; +import { DEFAULT_EVM_0_ADDRESS } from 'constants/evm'; + +export interface ICreationProgressModalProps { + open: boolean; + chains: TApplicationChainStatusItem[]; + supply: string; + isFirstTimeCreation: boolean; + isSelectAelfChains: boolean; + handleCreateFinish: (params?: { errorChainIds?: string[] }) => void; + handleClose: () => void; +} + +type TTxHash = `0x${string}`; + +type TChainItem = TApplicationChainStatusItem & { + txHash?: TTxHash; + isCreated?: boolean; +}; + +type TStepItem = ICommonStepsProps['stepItems'][number] & { + chain: TChainItem; +}; + +const POLLING_INTERVAL = 15000; + +export default function CreationProgressModal({ + open, + chains, + supply, + isFirstTimeCreation, + isSelectAelfChains, + handleCreateFinish, + handleClose, +}: ICreationProgressModalProps) { + const { account: evmAccount } = useWeb3(); + const isMobile = useMobile(); + const callEVMCreateOfficialToken = useCallEVMCreateOfficialToken(); + const callEVMCreateOfficialTokenRef = useLatestRef(callEVMCreateOfficialToken); + const poolingTimerForIssueResultRef = useRef>({}); + const [isCreateStart, setIsCreateStart] = useState(false); + const [isPollingStart, setIsPollingStart] = useState(false); + const [stepItems, setStepItems] = useState([]); + + const isWarning = useMemo(() => { + return ( + stepItems.length > 0 && + stepItems.some((item) => item.status === 'error') && + (stepItems[stepItems.length - 1].status === 'finish' || stepItems[stepItems.length - 1].status === 'error') + ); + }, [stepItems]); + + const isCreateFinished = useMemo(() => { + return stepItems.length > 0 && stepItems.every((item) => item.status === 'finish'); + }, [stepItems]); + + const isEveryChainError = useMemo(() => { + return stepItems.length > 0 && stepItems.every((item) => item.status === 'error'); + }, [stepItems]); + + const showCloseButton = useMemo(() => { + return isEveryChainError && (isFirstTimeCreation || !isSelectAelfChains); + }, [isEveryChainError, isSelectAelfChains, isFirstTimeCreation]); + + useEffect(() => { + const list: TStepItem[] = chains.map((chain) => ({ + chain, + title: `Creating token on ${chain.chainName}`, + })); + list.forEach((item) => { + item.status = 'process'; + item.isLoading = true; + }); + setStepItems(list); + }, [open, chains]); + + useEffect(() => { + if (open) { + setIsCreateStart(true); + } + }, [open]); + + useEffect(() => { + if (open && !isCreateStart && isCreateFinished) { + handleCreateFinish(); + } + }, [open, isCreateStart, isCreateFinished, handleCreateFinish]); + + useEffect(() => { + if (!open) { + setIsCreateStart(false); + setIsPollingStart(false); + } + }, [open]); + + const handleStepItemChange = useCallback( + ({ + step, + status, + params, + }: { + step: number; + status?: ICommonStepsProps['stepItems'][number]['status']; + params?: { + bindingId?: string; + thirdTokenId?: string; + txHash?: TTxHash; + isCreated?: boolean; + }; + }) => { + setStepItems((prev) => { + const newStepItems = prev.map((item) => ({ ...item, chain: { ...item.chain } })); + if (status) { + newStepItems[step].status = status; + newStepItems[step].isLoading = status === 'process'; + } + if (params) { + newStepItems[step].chain = { + ...newStepItems[step].chain, + ...params, + }; + } + return newStepItems; + }); + }, + [], + ); + + const handlePrepareBindIssue = useCallback( + async (chain: TChainItem) => { + try { + if (!evmAccount) { + throw new Error('No address found'); + } + const chainId = getChainIdByAPI(chain.chainId || ''); + const contractAddress = getBridgeChainInfo(chainId)?.CREATE_TOKEN_CONTRACT; + if (!contractAddress) { + throw new Error('No create token contract found'); + } + const params: TPrepareBindIssueRequest = { + address: evmAccount, + symbol: chain.symbol, + chainId: chain.chainId, + contractAddress, + supply, + }; + const res = await prepareBindIssue(params); + if (!res || !res.bindingId || !res.thirdTokenId) { + throw new Error('Failed to prepare bind issue'); + } + return res; + } catch (error: any) { + error.shouldShowMessage = true; + console.error(error); + throw error; + } + }, + [evmAccount, supply], + ); + + const handleIssue = useCallback( + async ({ chain }: { chain: TChainItem }) => { + try { + if (!evmAccount) { + throw new Error('No address found'); + } + const chainId = getChainIdByAPI(chain.chainId || ''); + const _officialAddress = getBridgeChainInfo(chainId)?.TOKEN_POOL || ''; + const res = await callEVMCreateOfficialTokenRef.current({ + chainId, + account: evmAccount, + name: chain.tokenName, + symbol: chain.symbol, + initialSupply: supply, + officialAddress: _officialAddress, + mintToAddress: DEFAULT_EVM_0_ADDRESS, + }); + if (!res || !res.TransactionId) { + throw new Error('Failed to create token'); + } + return res.TransactionId; + } catch (error) { + console.error(error); + throw error; + } + }, + [callEVMCreateOfficialTokenRef, evmAccount, supply], + ); + + const handlePollingForIssueResult = useCallback( + async ({ bindingId, thirdTokenId }: { bindingId?: string; thirdTokenId?: string }) => { + if (!bindingId || !thirdTokenId) { + return Promise.resolve(); + } + try { + const isFinished = await getApplicationIssue({ + bindingId, + thirdTokenId, + mintToAddress: DEFAULT_EVM_0_ADDRESS, + }); + if (!isFinished) { + if (poolingTimerForIssueResultRef.current[bindingId]) { + clearTimeout(poolingTimerForIssueResultRef.current[bindingId]); + } + return new Promise((resolve) => { + poolingTimerForIssueResultRef.current[bindingId] = setTimeout(async () => { + await handlePollingForIssueResult({ bindingId, thirdTokenId }); + resolve(); + }, POLLING_INTERVAL); + }); + } + } catch (error: any) { + error.shouldShowMessage = true; + console.error(error); + throw error; + } + }, + [], + ); + + const handlePolling = useCallback(async () => { + await Promise.all( + stepItems.map(async (item, index) => { + if (item.status === 'error' || item.status === 'finish') { + return; + } + try { + const chainId = getChainIdByAPI(item.chain.chainId || ''); + if (item.chain.txHash && chainId) { + await getTransactionReceiptAutoRetry({ + hash: item.chain.txHash, + chainId, + }); + } + await handlePollingForIssueResult({ + bindingId: item.chain.bindingId, + thirdTokenId: item.chain.thirdTokenId, + }); + handleStepItemChange({ step: index, status: 'finish' }); + } catch (error: any) { + if (error.shouldShowMessage) { + CommonMessage.error(handleListingErrorMessage(error)); + } + handleStepItemChange({ step: index, status: 'error' }); + } + }), + ); + }, [handlePollingForIssueResult, handleStepItemChange, stepItems]); + + const handleCreate = useLockCallback(async () => { + const create = async (step: number) => { + try { + const currentChain = stepItems[step].chain; + if (currentChain.status === ApplicationChainStatusEnum.Unissued) { + const txHash = await handleIssue({ chain: currentChain }); + const { bindingId, thirdTokenId } = await handlePrepareBindIssue(currentChain); + handleStepItemChange({ + step, + params: { bindingId, thirdTokenId, txHash, isCreated: true }, + }); + } + } catch (error: any) { + if (error.shouldShowMessage) { + CommonMessage.error(handleListingErrorMessage(error)); + } + handleStepItemChange({ step, status: 'error' }); + } + }; + + for (let index = 0; index < stepItems.length; index++) { + if (stepItems[index].status === 'finish' || stepItems[index].chain.isCreated) { + continue; + } + await create(index); + } + setIsPollingStart(true); + }, [stepItems, handlePrepareBindIssue, handleIssue, handleStepItemChange]); + + useEffect(() => { + if (open && isCreateStart && stepItems.length > 0) { + setIsCreateStart(false); + handleCreate(); + } + }, [handleCreate, isCreateStart, open, stepItems.length]); + + useEffect(() => { + if (isPollingStart) { + setIsPollingStart(false); + handlePolling(); + } + }, [isPollingStart, handlePolling]); + + const handleTryAgain = useCallback(() => { + setStepItems((prev) => { + const newStepItems = prev.map((item) => ({ ...item, chain: { ...item.chain } })); + newStepItems.forEach((item) => { + if (item.status === 'error') { + item.status = 'process'; + item.isLoading = true; + } + }); + return newStepItems; + }); + setIsCreateStart(true); + }, []); + + const handleSkip = useCallback(() => { + const errorChainIds = stepItems.filter((item) => item.status === 'error').map((item) => item.chain.chainId); + handleCreateFinish({ errorChainIds }); + }, [stepItems, handleCreateFinish]); + + const steps: ICommonStepsProps['stepItems'] = useMemo(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + return stepItems.map(({ chain, ...rest }) => rest); + }, [stepItems]); + + const renderContent = () => { + const networkList = stepItems.filter((item) => item.status === 'error').map((item) => item.chain.chainId); + + const formattedNetworkList = + networkList.length > 1 + ? `${networkList.slice(0, -1).join(', ')} and ${networkList[networkList.length - 1]}` + : networkList[0]; + + return ( + <> + + {isWarning && ( + <> + + {`Token creation on the ${formattedNetworkList} networks failed. Would you like to initiate the transaction again?`} + +
+ + {showCloseButton ? 'Close' : 'Skip'} + + + Try again + +
+ + )} + + ); + }; + + const commonProps = useMemo(() => { + return { + title: ( +
+ Token Creation in Progress + + {!isMobile &&

Tips:

} +
    +
  • Please approve the transaction in your wallet to create tokens on each chain.
  • +
  • If no wallet popup appears, please open your wallet manually to approve the transaction.
  • +
+ + } + /> +
+ ), + closable: false, + open, + }; + }, [open, isMobile]); + + if (isMobile) { + return ( + + {renderContent()} + + ); + } + + return ( + + {renderContent()} + + ); +} diff --git a/src/page-components/ListingApplication/SelectChain/CreationProgressModal/styles.module.less b/src/page-components/ListingApplication/SelectChain/CreationProgressModal/styles.module.less new file mode 100644 index 00000000..e8fd5ceb --- /dev/null +++ b/src/page-components/ListingApplication/SelectChain/CreationProgressModal/styles.module.less @@ -0,0 +1,84 @@ +@import 'src/assets/theme/constants.less'; +@import 'src/assets/theme/font.less'; + +.creation-progress-title-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.creation-progress-title { + .font-family-semibold(); + + font-size: 16px; + line-height: 24px; +} + +.creation-progress-steps { + padding-bottom: 16px; + :global { + .@{app-prefix}-steps-item-title { + font-size: 14px !important; + } + } +} + +.creation-progress-remind { + margin-top: 24px; +} + +.creation-progress-button-wrapper { + display: flex; + gap: 12px; + margin-top: 24px; + + .creation-progress-button { + flex: 1; + height: 48px; + font-size: 16px; + line-height: 24px; + } +} + +.creation-progress-drawer { + :global { + .@{app-prefix}-drawer-content-wrapper { + max-height: 80%; + } + .@{app-prefix}-drawer-header { + border-bottom: none; + } + .@{app-prefix}-drawer-body { + padding: 16px; + } + } + .creation-progress-title-wrapper { + margin-left: 28px; + } + .creation-progress-title { + flex: 1; + text-align: center; + .font-family-medium(); + + } +} + +.creation-progress-modal { + :global { + .@{app-prefix}-modal-header { + height: auto; + padding-top: 24px; + padding-bottom: 8px; + } + .@{app-prefix}-modal-title { + text-align: left; + font-size: 20px; + line-height: 28px; + } + .@{app-prefix}-modal-body { + width: 100%; + min-width: auto; + } + } +} diff --git a/src/page-components/ListingApplication/SelectChain/index.tsx b/src/page-components/ListingApplication/SelectChain/index.tsx new file mode 100644 index 00000000..5671b32b --- /dev/null +++ b/src/page-components/ListingApplication/SelectChain/index.tsx @@ -0,0 +1,603 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useEffectOnce } from 'react-use'; +import clsx from 'clsx'; +import { Form, Checkbox, Row, Col, Input, Radio } from 'antd'; +import CommonMessage from 'components/CommonMessage'; +import CommonButton, { CommonButtonProps } from 'components/CommonButton'; +import CommonTooltipSwitchModal, { ICommonTooltipSwitchModalRef } from 'components/CommonTooltipSwitchModal'; +import EmptyDataBox from 'components/EmptyDataBox'; +import IconFont from 'components/IconFont'; +import TokenRow from '../TokenRow'; +import ListingTip from '../ListingTip'; +import CreationProgressModal, { ICreationProgressModalProps } from './CreationProgressModal'; +import { + TSelectChainFormValues, + SelectChainFormKeys, + TTokenItem, + TSelectChainFormValidateData, + FormValidateStatus, + TChains, + TSearchParams, +} from 'types/listingApplication'; +import { ApplicationChainStatusEnum, TApplicationChainStatusItem } from 'types/api'; +import { WalletTypeEnum } from 'types/wallet'; +import { + SELECT_CHAIN_FORM_CHAIN_CREATED_NOT_LISTED_STATUS_LIST, + SELECT_CHAIN_FORM_CHAIN_DISABLED_STATUS_LIST, + SELECT_CHAIN_FORM_CHAIN_LISTED_STATUS_LIST, + SELECT_CHAIN_FORM_CHAIN_TOOLTIP_MAP, + SELECT_CHAIN_FORM_INITIAL_VALUES, + SELECT_CHAIN_FORM_INITIAL_VALIDATE_DATA, + SELECT_CHAIN_FORM_LABEL_MAP, + DEFAULT_CHAINS, + WALLET_CONNECTION_REQUIRED, + ListingStep, + LISTING_STEP_ITEMS, +} from 'constants/listingApplication'; +import { BUTTON_TEXT_BACK, SELECT_CHAIN } from 'constants/misc'; +import useGlobalLoading from 'hooks/useGlobalLoading'; +import { useAElf, useTon, useWeb3 } from 'hooks/web3'; +import { useConnect } from 'hooks/useConnect'; +import { useSetAelfAuthFromStorage } from 'hooks/aelfAuthToken'; +import { useMobile } from 'contexts/useStore/hooks'; +import { + getApplicationTokenList, + getApplicationChainStatusList, + addApplicationChain, + getApplicationTokenInfo, +} from 'utils/api/application'; +import { formatListWithAnd, formatWithCommas } from 'utils/format'; +import { handleListingErrorMessage } from 'utils/error'; +import { getChainIdByAPI, getChainName, getIconByAPIChainId } from 'utils/chain'; +import { isEVMChain, isTONChain } from 'utils/wallet'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; +import { sleep } from 'utils'; +import styles from './styles.module.less'; +import { formatSymbol } from 'utils/token'; +import { isMobileDevices } from 'utils/isMobile'; + +interface ISelectChainProps { + symbol?: string; + handleNextStep: (params?: TSearchParams) => void; + handlePrevStep: (params?: TSearchParams) => void; +} + +export default function SelectChain({ symbol, handleNextStep, handlePrevStep }: ISelectChainProps) { + const isMobile = useMobile(); + const _isMobileDevices = isMobileDevices(); + const { setGlobalLoading } = useGlobalLoading(); + const connect = useConnect(); + const { account: aelfAccount } = useAElf(); + const { account: evmAccount } = useWeb3(); + const { account: tonAccount } = useTon(); + const { isAelfConnected, isEVMConnected, isTONConnected } = useMemo( + () => ({ + isAelfConnected: !!aelfAccount, + isEVMConnected: !!evmAccount, + isTONConnected: !!tonAccount, + }), + [aelfAccount, evmAccount, tonAccount], + ); + const setAelfAuthFromStorage = useSetAelfAuthFromStorage(); + const [form] = Form.useForm(); + const tooltipSwitchModalsRef = useRef>({}); + + const [formData, setFormData] = useState(SELECT_CHAIN_FORM_INITIAL_VALUES); + const [formValidateData, setFormValidateData] = useState(SELECT_CHAIN_FORM_INITIAL_VALIDATE_DATA); + const [chainListData, setChainListData] = useState(DEFAULT_CHAINS); + const [creationProgressModalProps, setCreationProgressModalProps] = useState< + Pick + >({ + open: false, + chains: [], + }); + const [token, setToken] = useState(); + const tokenRef = useRef(token); + const [isShowInitialSupplyFormItem, setIsShowInitialSupplyFormItem] = useState(false); + const [isButtonDisabled, setIsButtonDisabled] = useState(true); + + const handleBackStep = useCallback(() => { + handlePrevStep({ symbol }); + }, [handlePrevStep, symbol]); + + const getToken = useCallback(async () => { + if (!symbol) return; + const res = await getApplicationTokenList({}); + const list = (res.tokenList || []).map((item) => ({ + name: item.tokenName, + symbol: item.symbol, + icon: item.tokenImage, + status: item.status, + totalSupply: item.totalSupply, + })); + const _token = list.find((item) => item.symbol === symbol); + if (_token) { + const tokenInfo = await getApplicationTokenInfo({ symbol }); + if (tokenInfo && tokenInfo.symbol) { + setToken(_token); + tokenRef.current = _token; + } else { + throw new Error('Failed to get token info'); + } + } else { + throw new Error('Token not found'); + } + }, [symbol]); + + const getChainList = useCallback(async () => { + if (!symbol) return; + const res = await getApplicationChainStatusList({ symbol }); + const _chains = (res.chainList || []).map((item) => ({ + ...item, + chainName: getChainName(getChainIdByAPI(item.chainId)), + tokenName: item.tokenName || tokenRef.current?.name || '', + })); + + const listData = { + [SelectChainFormKeys.CHAINS]: _chains, + }; + setChainListData(listData); + }, [symbol]); + + const judgeIsChainDisabled = useCallback((status: ApplicationChainStatusEnum): boolean => { + return SELECT_CHAIN_FORM_CHAIN_DISABLED_STATUS_LIST.includes(status); + }, []); + + const hasDisabledChain = useMemo( + () => chainListData[SelectChainFormKeys.CHAINS].some((chain) => judgeIsChainDisabled(chain.status)), + [chainListData, judgeIsChainDisabled], + ); + + const judgeIsShowInitialSupplyFormItem = useCallback((currentChains: TApplicationChainStatusItem[]): boolean => { + return currentChains.some((v) => v.status === ApplicationChainStatusEnum.Unissued); + }, []); + + const judgeIsTokenError = useCallback(() => !token, [token]); + + const judgeIsChainsError = useCallback( + ({ chains }: { chains: TApplicationChainStatusItem[] }): boolean => { + const isChainSelected = hasDisabledChain || chains.length > 0; + const hasValue = chains.length > 0; + + if (isChainSelected && hasValue) { + return false; + } + return true; + }, + [hasDisabledChain], + ); + + const judgeIsInitialSupplyError = useCallback( + ({ + _isShowInitialSupplyFormItem, + value, + validateStatus, + }: { + _isShowInitialSupplyFormItem: boolean; + value: string; + validateStatus?: FormValidateStatus; + }): boolean => + _isShowInitialSupplyFormItem && (!value || (!!validateStatus && validateStatus === FormValidateStatus.Error)), + [], + ); + + const unconnectedWallets = useMemo(() => { + const list: WalletTypeEnum[] = []; + formData[SelectChainFormKeys.CHAINS].forEach((item) => { + if (isEVMChain(item.chainId) && !isEVMConnected) { + list.push(WalletTypeEnum.EVM); + } else if (isTONChain(item.chainId) && !isTONConnected) { + list.push(WalletTypeEnum.TON); + } + }); + return [...new Set(list)]; + }, [formData, isEVMConnected, isTONConnected]); + + const judgeIsButtonDisabled = useCallback(() => { + const isDisabled = + judgeIsTokenError() || + judgeIsChainsError({ + chains: formData[SelectChainFormKeys.CHAINS], + }) || + judgeIsInitialSupplyError({ + _isShowInitialSupplyFormItem: isShowInitialSupplyFormItem, + value: String(token?.totalSupply), + validateStatus: formValidateData[SelectChainFormKeys.INITIAL_SUPPLY].validateStatus, + }); + setIsButtonDisabled(isDisabled); + }, [ + judgeIsTokenError, + judgeIsChainsError, + formData, + judgeIsInitialSupplyError, + isShowInitialSupplyFormItem, + token?.totalSupply, + formValidateData, + ]); + + useEffect(() => { + judgeIsButtonDisabled(); + }, [judgeIsButtonDisabled]); + + const handleFormDataChange = useCallback( + ({ + formKey, + value, + validateData, + }: { + formKey: SelectChainFormKeys; + value: TSelectChainFormValues[SelectChainFormKeys]; + validateData?: TSelectChainFormValidateData[SelectChainFormKeys.INITIAL_SUPPLY]; + }) => { + const newFormData = { ...formData, [formKey]: value }; + setFormData(newFormData); + + let newFormValidateData = formValidateData; + if (validateData) { + newFormValidateData = { + ...newFormValidateData, + [formKey]: validateData, + }; + setFormValidateData(newFormValidateData); + } + + const _isShowInitialSupplyFormItem = judgeIsShowInitialSupplyFormItem(newFormData[SelectChainFormKeys.CHAINS]); + setIsShowInitialSupplyFormItem(_isShowInitialSupplyFormItem); + }, + [formData, formValidateData, judgeIsShowInitialSupplyFormItem], + ); + + const getNewChains = useCallback( + ({ + formKey, + value, + checked, + }: { + formKey: SelectChainFormKeys; + value: TApplicationChainStatusItem; + checked: boolean; + }) => { + const preChains = (formData[formKey] as TApplicationChainStatusItem[]) || []; + let newChains: TApplicationChainStatusItem[] = []; + if (checked) { + newChains = [...preChains, value]; + } else { + newChains = preChains.filter((v) => v.chainId !== value.chainId); + } + return newChains; + }, + [formData], + ); + + const handleChainsChange = useCallback( + ({ chain, checked }: { chain: TApplicationChainStatusItem; checked: boolean }) => { + let newChains: TApplicationChainStatusItem[] = []; + if (_isMobileDevices) { + if (checked) { + newChains = [chain]; + } + } else { + newChains = getNewChains({ + formKey: SelectChainFormKeys.CHAINS, + value: chain, + checked, + }); + } + console.log('====== newChains', newChains); + + handleFormDataChange({ + formKey: SelectChainFormKeys.CHAINS, + value: newChains, + }); + }, + [_isMobileDevices, getNewChains, handleFormDataChange], + ); + + const { unissuedChains, issuingChains, issuedChains } = useMemo(() => { + const unissuedChains = formData[SelectChainFormKeys.CHAINS].filter( + (chain) => chain.status === ApplicationChainStatusEnum.Unissued, + ); + const issuingChains = formData[SelectChainFormKeys.CHAINS].filter( + (chain) => chain.status === ApplicationChainStatusEnum.Issuing, + ); + const issuedChains = { + [SelectChainFormKeys.CHAINS]: formData[SelectChainFormKeys.CHAINS].filter( + (chain) => chain.status === ApplicationChainStatusEnum.Issued, + ), + }; + return { unissuedChains, issuingChains, issuedChains }; + }, [formData]); + + const handleConnectWallets = useCallback(() => { + connect(); + }, [connect]); + + const handleCreateToken = useCallback(() => { + setCreationProgressModalProps({ + open: true, + chains: [...unissuedChains, ...issuingChains], + }); + }, [issuingChains, unissuedChains]); + + const handleAddChain = useCallback( + async ({ errorChainIds }: { errorChainIds?: string[] } = {}) => { + if (!token?.symbol) return; + setGlobalLoading(true); + try { + const data = await addApplicationChain({ + chainIds: formData[SelectChainFormKeys.CHAINS] + .filter((v) => !errorChainIds?.includes(v.chainId)) + .map((v) => v.chainId), + symbol: token.symbol, + }); + if (!data?.chainList) { + throw new Error('Failed to add chain'); + } + const networks = formData[SelectChainFormKeys.CHAINS] + .filter((item) => data?.chainList?.some((v) => v.chainId === item.chainId)) + .map((v) => ({ + name: v.chainName, + })); + const networksString = JSON.stringify(networks); + + handleNextStep({ networks: networksString }); + } catch (error: any) { + CommonMessage.error(handleListingErrorMessage(error)); + } finally { + setGlobalLoading(false); + } + }, + [formData, handleNextStep, setGlobalLoading, token?.symbol], + ); + + const handleCreationProgressModalClose = useCallback(() => { + setCreationProgressModalProps({ + open: false, + chains: [], + }); + }, []); + + const handleCreateFinish = useCallback( + async ({ errorChainIds }: { errorChainIds?: string[] } = {}) => { + handleCreationProgressModalClose(); + await handleAddChain({ errorChainIds }); + }, + [handleAddChain, handleCreationProgressModalClose], + ); + + const getActionButtonProps = useCallback(() => { + let props: CommonButtonProps = { + children: SELECT_CHAIN, + }; + + if (unissuedChains.length !== 0 || issuingChains.length !== 0) { + if (unconnectedWallets.length !== 0 && !isButtonDisabled) { + props = { + children: `Connect ${unconnectedWallets.join(', ')} Wallet${unconnectedWallets.length > 1 ? 's' : ''}`, + onClick: handleConnectWallets, + }; + } else { + props = { + children: 'Create Token and Submit', + onClick: handleCreateToken, + }; + } + } else if (Object.values(issuedChains).some((v) => v.length !== 0)) { + props = { + children: 'Submit', + onClick: () => handleAddChain(), + }; + } + + return props; + }, [ + unissuedChains.length, + issuingChains.length, + issuedChains, + unconnectedWallets, + isButtonDisabled, + handleConnectWallets, + handleCreateToken, + handleAddChain, + ]); + + const init = useCallback(async () => { + try { + setGlobalLoading(true); + await setAelfAuthFromStorage(); + await sleep(500); + + await getToken(); + await getChainList(); + } catch (error) { + console.log('SelectChain init', error); + handleBackStep(); + } finally { + setGlobalLoading(false); + } + }, [getChainList, getToken, handleBackStep, setAelfAuthFromStorage, setGlobalLoading]); + + const connectAndInit = useCallback(() => { + if (!isAelfConnected || !symbol) { + handleBackStep(); + } else { + init(); + } + }, [handleBackStep, init, isAelfConnected, symbol]); + const connectAndInitRef = useRef(connectAndInit); + connectAndInitRef.current = connectAndInit; + const connectAndInitSleep = useCallback(async () => { + setGlobalLoading(true); + // Delay 3s to determine the login status, because the login data is acquired slowly, to prevent the login pop-up window from being displayed first and then automatically logging in successfully later. + await sleep(3000); + connectAndInitRef.current(); + }, [setGlobalLoading]); + useEffectOnce(() => { + connectAndInitSleep(); + }); + + const initForLogout = useCallback(async () => { + setGlobalLoading(true); + handleBackStep(); + }, [handleBackStep, setGlobalLoading]); + const initLogoutRef = useRef(initForLogout); + initLogoutRef.current = initForLogout; + + useEffectOnce(() => { + // log out \ exit + const { remove: removeLogoutSuccess } = eBridgeEventBus.AelfLogoutSuccess.addListener(() => { + initLogoutRef.current(); + }); + + return () => { + removeLogoutSuccess(); + }; + }); + + const renderChainsFormItem = useMemo(() => { + return ( + + + {chainListData[SelectChainFormKeys.CHAINS].map((chain) => { + const isDisabled = judgeIsChainDisabled(chain.status); + + const checked = + isDisabled || formData[SelectChainFormKeys.CHAINS]?.some((v) => v.chainId === chain.chainId); + + let tooltip = ''; + if (isDisabled) { + if (SELECT_CHAIN_FORM_CHAIN_LISTED_STATUS_LIST.includes(chain.status)) { + tooltip = SELECT_CHAIN_FORM_CHAIN_TOOLTIP_MAP.LISTED; + } else if (SELECT_CHAIN_FORM_CHAIN_CREATED_NOT_LISTED_STATUS_LIST.includes(chain.status)) { + tooltip = SELECT_CHAIN_FORM_CHAIN_TOOLTIP_MAP.CREATED_NOT_LISTED; + } + } + + return ( +
+ { + tooltipSwitchModalsRef.current[chain.chainId] = ref; + }} + tip={tooltip}> +
{ + if (tooltip) { + tooltipSwitchModalsRef.current[chain.chainId]?.open(); + } + handleChainsChange({ + chain, + checked: !checked, + }); + }}> +
+ + {chain.chainName} +
+ {_isMobileDevices ? ( + + ) : ( + + )} +
+
+ + ); + })} + + + ); + }, [_isMobileDevices, chainListData, formData, handleChainsChange, isMobile, judgeIsChainDisabled]); + + return ( + <> +
+
+ {LISTING_STEP_ITEMS[ListingStep.SELECT_CHAIN].title} + +

Tips:

+
    +
  • {`You can select multiple chains at the same time, and it will support bridging between the aelf chain and other chains.`}
  • +
  • {`After the token is listed, you can go to the "homepage" → "Pool" page to add liquidity.`}
  • +
+ + } + /> +
+ {isAelfConnected ? ( + <> +
+ + {token && ( +
+ +
+ )} +
+ {renderChainsFormItem} + {isShowInitialSupplyFormItem && ( + + + {SELECT_CHAIN_FORM_LABEL_MAP[SelectChainFormKeys.INITIAL_SUPPLY]} + +
+

+ {`The tokens are issued on ${formatListWithAnd( + formData[SelectChainFormKeys.CHAINS].map((v) => v.chainName), + )}, with the total supply as the issuance amount.`} +

+

{'The issued tokens will be directly transferred to the eBridge contract.'}

+
+
+ }> +
{formatSymbol(token?.symbol)} issuance amount
+ + + )} + +
+ + {BUTTON_TEXT_BACK} + + +
+ + ) : ( + + )} + + + + ); +} diff --git a/src/page-components/ListingApplication/SelectChain/styles.module.less b/src/page-components/ListingApplication/SelectChain/styles.module.less new file mode 100644 index 00000000..4d9d557c --- /dev/null +++ b/src/page-components/ListingApplication/SelectChain/styles.module.less @@ -0,0 +1,157 @@ +@import 'src/assets/theme/color.less'; +@import 'src/assets/theme/font.less'; +@import 'src/assets/theme/constants.less'; + +.select-chain-title-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + + .select-chain-title { + .font-family-semibold(); + + font-size: 20px; + line-height: 28px; + } +} + +.select-chain-form { + padding-top: 32px; + :global { + .@{app-prefix}-form-item { + margin-bottom: 32px; + &:has(.@{app-prefix}-form-item-explain) { + margin-bottom: 0; + } + .@{app-prefix}-input-affix-wrapper { + padding: 8px 12px; + } + .@{app-prefix}-input-disabled{ + color: @font-17; + border: none; + padding: 9px 12px; + } + } + } +} + +.select-chain-token-row { + padding: 9px 12px; + border-radius: 6px; + background-color: @bg-16; +} + +.select-chain-checkbox-list { + display: flex; + gap: 12px; +} + +.select-chain-checkbox-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 8px 12px; + border-radius: 6px; + border: 1px solid @border-6; + cursor: pointer; + + &.select-chain-checkbox-item-checked, + &.select-chain-checkbox-item-disabled { + background-color: @bg-15; + } + + &.select-chain-checkbox-item-disabled { + cursor: not-allowed; + } + + .select-chain-checkbox-content { + overflow: hidden; + display: flex; + align-items: center; + gap: 6px; + } + + .select-chain-checkbox-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + .font-family-medium(); + } + + :global { + .@{app-prefix}-checkbox-inner, + .@{app-prefix}-checkbox::after { + border-radius: 4px; + } + } +} + +.select-chain-chains-label-wrapper { + display: flex; + align-items: center; + gap: 4px; +} + +.select-chain-initial-supply-label-wrapper { + display: flex; + flex-direction: column; + gap: 8px; +} + +.select-chain-label { + .font-family-semibold(); + + color: @font-15; + font-size: 16px; + line-height: 22px; +} + +.select-chain-description { + .font-family-regular(); + + color: @font-13; + font-size: 14px; + line-height: 22px; +} + +.select-chain-footer { + display: flex; + justify-content: center; + gap: 16px; +} + +.select-chain-footer-button { + min-width: 240px; + height: 48px; + font-size: 16px; + line-height: 24px; +} + +.supply-amount{ + padding-top: 16px; + padding-bottom: 8px; + font-size: 16px; + line-height: 22px; + color: @font-13; +} + +@media screen and (max-width: @md-width) { + .select-chain-form { + padding-top: 24px; + :global { + .@{app-prefix}-form-item { + margin-bottom: 24px; + &:has(.@{app-prefix}-form-item-explain) { + margin-bottom: 0; + } + } + } + } + .select-chain-footer { + flex-direction: column-reverse; + } + .select-chain-footer-button { + width: 100%; + } +} diff --git a/src/page-components/ListingApplication/TokenInformation/TokenSelect/index.tsx b/src/page-components/ListingApplication/TokenInformation/TokenSelect/index.tsx new file mode 100644 index 00000000..38d95801 --- /dev/null +++ b/src/page-components/ListingApplication/TokenInformation/TokenSelect/index.tsx @@ -0,0 +1,126 @@ +import { useCallback, useMemo, useRef, useState } from 'react'; +import clsx from 'clsx'; +import CommonLink from 'components/CommonLink'; +import DynamicArrow from 'components/DynamicArrow'; +import CommonImage from 'components/CommonImage'; +import CommonSelectTokenModal from 'components/CommonSelectTokenModal'; +import TokenRow from '../../TokenRow'; +import { checkFilled16Icon, closeFilled16Icon } from 'assets/images'; +import { TTokenConfig, TTokenItem } from 'types/listingApplication'; +import { AwakenHost } from 'constants/index'; +import { LISTING_TOKEN_TIP } from 'constants/listingApplication'; +import styles from './styles.module.less'; +import { TApplicationTokenStatus } from 'types/api'; + +interface ITokenSelectProps { + className?: string; + token?: TTokenItem; + liquidityInUsd?: string; + holders?: number; + tokenList: TTokenItem[]; + tokenConfig?: TTokenConfig; + placeholder?: string; + selectCallback?: (item: TTokenItem) => void; +} + +export default function TokenSelect({ + className, + token, + liquidityInUsd, + holders, + tokenList, + tokenConfig, + placeholder, + selectCallback, +}: ITokenSelectProps) { + const [isShowTokenSelectModal, setIsShowTokenSelectModal] = useState(false); + const tokenSymbolRef = useRef(token?.symbol); + + const formatTokenList = useMemo(() => { + const _list: Array = []; + tokenList.forEach((item) => { + _list.push({ + ...item, + isShowSuffix: item.status !== TApplicationTokenStatus.Available, + disable: item.status !== TApplicationTokenStatus.Available, + }); + }, []); + + return _list; + }, [tokenList]); + + const getInfoValidateIcon = useCallback((isPass: boolean) => { + return ( + + ); + }, []); + + const onSelectToken = useCallback( + async (item: TTokenItem) => { + if (item?.status !== TApplicationTokenStatus.Available) { + return; + } + + tokenSymbolRef.current = item.symbol; + setIsShowTokenSelectModal(false); + selectCallback?.(item); + }, + [selectCallback], + ); + + return ( + <> +
+
setIsShowTokenSelectModal(true)}> + {token ? ( + + ) : ( + {placeholder} + )} + +
+ + {token && ( +
+ {!!tokenConfig?.liquidityInUsd && tokenConfig.liquidityInUsd !== '0' && ( +
+
+ {getInfoValidateIcon( + !!liquidityInUsd && parseFloat(liquidityInUsd) > parseFloat(tokenConfig.liquidityInUsd), + )} + {`Liquidity > $${tokenConfig.liquidityInUsd}`} +
+ + Add Liquidity + +
+ )} + {!!tokenConfig?.holders && ( +
+
+ {getInfoValidateIcon( + tokenConfig?.holders !== undefined && holders !== undefined && holders > tokenConfig.holders, + )} + {`Holders > ${tokenConfig.holders}`} +
+
+ )} +
+ )} +
+ + setIsShowTokenSelectModal(false)} + /> + + ); +} diff --git a/src/page-components/ListingApplication/TokenInformation/TokenSelect/styles.module.less b/src/page-components/ListingApplication/TokenInformation/TokenSelect/styles.module.less new file mode 100644 index 00000000..a6e3cd71 --- /dev/null +++ b/src/page-components/ListingApplication/TokenInformation/TokenSelect/styles.module.less @@ -0,0 +1,63 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.token-selected-wrapper { + display: flex; + flex-direction: column; + gap: 8px; +} + +.token-selected { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 12px; + border-radius: 6px; + border: 1px solid @border-5; + font-size: 14px; + line-height: 22px; + cursor: pointer; + + .token-selected-placeholder { + color: @font-18; + .font-family-medium(); + } +} + +.token-selected-info-card { + display: flex; + flex-direction: column; + gap: 4px; + padding: 8px 12px; + border-radius: 6px; + background-color: @bg-16; + + .token-selected-info-card-row { + display: flex; + align-items: center; + justify-content: space-between; + } + + .token-selected-info-card-row-content { + display: flex; + align-items: center; + gap: 8px; + } + + .token-selected-info-card-row-icon { + width: 16px; + height: 16px; + } + + .token-selected-info-card-row-link { + height: 22px; + padding: 0; + border: none; + .font-family-medium(); + + color: @font-14; + font-size: 14px; + line-height: 22px; + } +} diff --git a/src/page-components/ListingApplication/TokenInformation/index.tsx b/src/page-components/ListingApplication/TokenInformation/index.tsx new file mode 100644 index 00000000..33465e77 --- /dev/null +++ b/src/page-components/ListingApplication/TokenInformation/index.tsx @@ -0,0 +1,587 @@ +import { useCallback, useMemo, useRef, useState } from 'react'; +import { useEffectOnce } from 'react-use'; +import { useRouter } from 'next/router'; +import clsx from 'clsx'; +import { Form, Input, InputProps } from 'antd'; +import ConnectWallet from 'components/ConnectWallet'; +import CommonButton from 'components/CommonButton'; +import CommonMessage from 'components/CommonMessage'; +import ListingTip from '../ListingTip'; +import TokenSelect from './TokenSelect'; +import { sleep } from 'utils'; +import { + commitTokenInfo, + getApplicationTokenInfo, + getApplicationTokenList, + getApplicationTokenConfig, + getApplicationTokenDetail, +} from 'utils/api/application'; +import { getListingUrl } from 'utils/listingApplication'; +import { getChainType } from 'utils/chain'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; +import { handleInputFocus } from 'utils/input'; +import { handleListingErrorMessage } from 'utils/error'; +import { useAElf } from 'hooks/web3'; +import { useAelfLogin } from 'hooks/wallet'; +import { useSetAelfAuthFromStorage } from 'hooks/aelfAuthToken'; +import useGlobalLoading from 'hooks/useGlobalLoading'; +import { useMobile } from 'contexts/useStore/hooks'; +import { TCommitTokenInfoRequest, TGetApplicationTokenInfoResult } from 'types/api'; +import { + TTokenInformationFormValues, + TokenInformationFormKeys, + TTokenInformationFormValidateData, + FormValidateStatus, + TTokenItem, + TSearchParams, + TTokenConfig, +} from 'types/listingApplication'; +import { + TOKEN_INFORMATION_FORM_LABEL_MAP, + TOKEN_INFORMATION_FORM_PLACEHOLDER_MAP, + TOKEN_INFORMATION_FORM_INITIAL_VALIDATE_DATA, + TOKEN_INFORMATION_FORM_INITIAL_VALUES, + REQUIRED_ERROR_MESSAGE, + ListingStep, + LISTING_STEP_ITEMS, +} from 'constants/listingApplication'; +import { CONNECT_AELF_WALLET, BUTTON_TEXT_NEXT } from 'constants/misc'; +import { MAIN_SIDE_CHAIN_ID } from 'constants/index'; +import styles from './styles.module.less'; +import { useWallet } from 'contexts/useWallet/hooks'; +import { isELFChain } from 'utils/aelfUtils'; + +interface ITokenInformationProps { + symbol?: string; + handleNextStep: (params?: TSearchParams) => void; +} + +const AELF_CHAIN_ID = MAIN_SIDE_CHAIN_ID.mainChain; + +export default function TokenInformation({ symbol, handleNextStep }: ITokenInformationProps) { + const isMobile = useMobile(); + const router = useRouter(); + const setAelfAuthFromStorage = useSetAelfAuthFromStorage(); + const handleAelfLogin = useAelfLogin(); + const { setGlobalLoading } = useGlobalLoading(); + const [form] = Form.useForm(); + const aelfWallet = useAElf(); + const { isActive: isAelfActive, account: aelfAccount } = aelfWallet || {}; + const { fromWallet, fromOptions, toWallet, toOptions } = useWallet(); + + const currentChain = useMemo(() => { + const fromChainId = fromWallet?.chainId; + const toChainId = toWallet?.chainId; + + if (isELFChain(fromChainId)) return { chainId: fromChainId, chainType: fromOptions?.chainType }; + if (isELFChain(toChainId)) return { chainId: toChainId, chainType: toOptions?.chainType }; + return { chainId: AELF_CHAIN_ID, chainType: getChainType(AELF_CHAIN_ID) }; + }, [fromOptions?.chainType, fromWallet?.chainId, toOptions?.chainType, toWallet?.chainId]); + + const [formValues, setFormValues] = useState(TOKEN_INFORMATION_FORM_INITIAL_VALUES); + const [formValidateData, setFormValidateData] = useState(TOKEN_INFORMATION_FORM_INITIAL_VALIDATE_DATA); + const [isButtonDisabled, setIsButtonDisabled] = useState(true); + const [tokenList, setTokenList] = useState([]); + const [liquidityInUsd, setLiquidityInUsd] = useState(); + const [holders, setHolders] = useState(); + const liquidityInUsdRef = useRef(); + const holdersRef = useRef(); + const [tokenConfig, setTokenConfig] = useState(); + const currentTokenInfoRef = useRef(); + const [isActionButtonLoading, setIsActionButtonLoading] = useState(false); + + const judgeIsButtonDisabled = useCallback( + ( + currentFormData: Partial, + currentFormValidateData: TTokenInformationFormValidateData, + currentTokenConfig?: TTokenConfig, + ) => { + const isTokenValid = + !!liquidityInUsdRef.current && + !!currentTokenConfig?.liquidityInUsd && + parseFloat(liquidityInUsdRef.current) > parseFloat(currentTokenConfig.liquidityInUsd) && + holdersRef.current !== undefined && + currentTokenConfig?.holders !== undefined && + holdersRef.current > currentTokenConfig.holders; + const emailValid = !!currentFormData[TokenInformationFormKeys.EMAIL]; + const isDisabled = + Object.values(currentFormValidateData).some((item) => item.validateStatus === FormValidateStatus.Error) || + !isTokenValid || + !emailValid; + setIsButtonDisabled(isDisabled); + }, + [], + ); + + const reset = useCallback(() => { + setFormValues(TOKEN_INFORMATION_FORM_INITIAL_VALUES); + setFormValidateData(TOKEN_INFORMATION_FORM_INITIAL_VALIDATE_DATA); + setIsButtonDisabled(true); + setTokenList([]); + }, []); + + const getTokenList = useCallback(async () => { + try { + await setAelfAuthFromStorage(); + await sleep(500); + const res = await getApplicationTokenList({}); + const list = (res.tokenList || []).map((item) => ({ + name: item.tokenName, + symbol: item.symbol, + icon: item.tokenImage, + status: item.status, + totalSupply: item.totalSupply, + })); + setTokenList(list); + return list; + } catch (error) { + console.error(error); + return []; + } + }, [setAelfAuthFromStorage]); + + const getTokenConfig = useCallback(async (_symbol: string) => { + try { + const config = await getApplicationTokenConfig({ symbol: _symbol }); + setTokenConfig(config); + return config; + } catch (error) { + console.error(error); + return undefined; + } + }, []); + + const getTokenDetail = useCallback(async (symbol: string) => { + try { + const _tokenDetail = await getApplicationTokenDetail({ symbol: symbol }); + setLiquidityInUsd(_tokenDetail.liquidityInUsd); + liquidityInUsdRef.current = _tokenDetail.liquidityInUsd; + setHolders(_tokenDetail.holders); + holdersRef.current = _tokenDetail.holders; + } catch (error) { + console.log('getApplicationTokenDetail error', error); + } + }, []); + + const getTokenInfo = useCallback( + async (_symbol: string, _tokenList: TTokenItem[], _tokenConfig: TTokenConfig) => { + const token = _tokenList.find((item) => item.symbol === _symbol); + const newFormValues = { + ...TOKEN_INFORMATION_FORM_INITIAL_VALUES, + [TokenInformationFormKeys.TOKEN]: token, + }; + const newFormValidateData = TOKEN_INFORMATION_FORM_INITIAL_VALIDATE_DATA; + + await getTokenDetail(_symbol); + + try { + const res = await getApplicationTokenInfo({ symbol: _symbol }); + if (token && res && res.symbol) { + currentTokenInfoRef.current = res; + newFormValues[TokenInformationFormKeys.OFFICIAL_WEBSITE] = res.officialWebsite; + newFormValues[TokenInformationFormKeys.OFFICIAL_TWITTER] = res.officialTwitter; + newFormValues[TokenInformationFormKeys.TITLE] = res.title; + newFormValues[TokenInformationFormKeys.PERSON_NAME] = res.personName; + newFormValues[TokenInformationFormKeys.TELEGRAM_HANDLER] = res.telegramHandler; + newFormValues[TokenInformationFormKeys.EMAIL] = res.email; + } + } catch (error) { + console.error(error); + currentTokenInfoRef.current = undefined; + } finally { + setFormValues(newFormValues); + setFormValidateData(newFormValidateData); + judgeIsButtonDisabled(newFormValues, newFormValidateData, _tokenConfig); + } + }, + [getTokenDetail, judgeIsButtonDisabled], + ); + + const init = useCallback(async () => { + setGlobalLoading(true); + const list = await getTokenList(); + if (symbol) { + const config = await getTokenConfig(symbol); + if (config) { + await getTokenInfo(symbol, list, config); + } + } + setGlobalLoading(false); + }, [getTokenConfig, getTokenInfo, getTokenList, setGlobalLoading, symbol]); + const initRef = useRef(init); + initRef.current = init; + + const connectAndInit = useCallback(() => { + if (!isAelfActive) { + handleAelfLogin(true, initRef.current, true); + } else { + initRef.current(); + } + }, [handleAelfLogin, isAelfActive]); + const connectAndInitRef = useRef(connectAndInit); + connectAndInitRef.current = connectAndInit; + const connectAndInitSleep = useCallback(async () => { + setGlobalLoading(true); + // Delay 3s to determine the login status, because the login data is acquired slowly, to prevent the login pop-up window from being displayed first and then automatically logging in successfully later. + await sleep(3000); + connectAndInitRef.current(); + }, [setGlobalLoading]); + useEffectOnce(() => { + connectAndInitSleep(); + }); + + const initForLogout = useCallback(async () => { + reset(); + }, [reset]); + const initLogoutRef = useRef(initForLogout); + initLogoutRef.current = initForLogout; + + const initForReLogin = useCallback(async () => { + initRef.current(); + }, []); + const initForReLoginRef = useRef(initForReLogin); + initForReLoginRef.current = initForReLogin; + + useEffectOnce(() => { + // log in + const { remove: removeLoginSuccess } = eBridgeEventBus.AelfLoginSuccess.addListener(() => { + initForReLoginRef.current(); + }); + // log out \ exit + const { remove: removeLogoutSuccess } = eBridgeEventBus.AelfLogoutSuccess.addListener(() => { + initLogoutRef.current(); + }); + + return () => { + removeLoginSuccess(); + removeLogoutSuccess(); + }; + }); + + const handleFormDataChange = useCallback( + ({ + formKey, + value, + validateData, + }: { + formKey: TokenInformationFormKeys; + value?: TTokenInformationFormValues[TokenInformationFormKeys]; + validateData: TTokenInformationFormValidateData[TokenInformationFormKeys]; + }) => { + const newFormValues = { ...formValues, [formKey]: value }; + const newFormValidateData = { + ...formValidateData, + [formKey]: validateData, + }; + setFormValues(newFormValues); + setFormValidateData(newFormValidateData); + judgeIsButtonDisabled(newFormValues, newFormValidateData, tokenConfig); + }, + [formValidateData, formValues, judgeIsButtonDisabled, tokenConfig], + ); + + const handleSelectToken = useCallback( + async (item: TTokenItem) => { + setGlobalLoading(true); + // Re-obtain the token list data by switching tokens, so that users can see the latest operable tokens. + const list = await getTokenList(); + const currentList = list.length > 0 ? list : tokenList; + const newItem = currentList.find((v) => v.symbol === item.symbol); + if (newItem) { + handleFormDataChange({ + formKey: TokenInformationFormKeys.TOKEN, + value: newItem, + validateData: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + }); + router.replace(getListingUrl(ListingStep.TOKEN_INFORMATION, { symbol: newItem.symbol })); + const config = await getTokenConfig(newItem.symbol); + if (config) { + await getTokenInfo(newItem.symbol, currentList, config); + } + } else { + handleFormDataChange({ + formKey: TokenInformationFormKeys.TOKEN, + value: undefined, + validateData: { + validateStatus: FormValidateStatus.Error, + errorMessage: REQUIRED_ERROR_MESSAGE, + }, + }); + } + setTokenList(currentList); + setGlobalLoading(false); + }, + [setGlobalLoading, getTokenList, tokenList, handleFormDataChange, router, getTokenConfig, getTokenInfo], + ); + + const handleCommonInputChange = useCallback( + (value: string, key: TokenInformationFormKeys) => { + handleFormDataChange({ + formKey: key, + value: value, + validateData: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + }); + }, + [handleFormDataChange], + ); + + const handleUrlChange = useCallback( + (value: string, key: TokenInformationFormKeys) => { + if (value && !value.startsWith('https://')) { + handleFormDataChange({ + formKey: key, + value: value, + validateData: { + validateStatus: FormValidateStatus.Error, + errorMessage: 'Please enter a valid URL', + }, + }); + } else { + handleFormDataChange({ + formKey: key, + value: value, + validateData: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + }); + } + }, + [handleFormDataChange], + ); + + const handleEmailChange = useCallback( + (value: string) => { + if (!value) { + handleFormDataChange({ + formKey: TokenInformationFormKeys.EMAIL, + value: value, + validateData: { + validateStatus: FormValidateStatus.Error, + errorMessage: REQUIRED_ERROR_MESSAGE, + }, + }); + } else if (!value.includes('@')) { + handleFormDataChange({ + formKey: TokenInformationFormKeys.EMAIL, + value: value, + validateData: { + validateStatus: FormValidateStatus.Error, + errorMessage: 'Please enter a valid email address', + }, + }); + } else { + handleFormDataChange({ + formKey: TokenInformationFormKeys.EMAIL, + value: value, + validateData: { + validateStatus: FormValidateStatus.Normal, + errorMessage: '', + }, + }); + } + }, + [handleFormDataChange], + ); + + const checkFormChange = useCallback((params: TCommitTokenInfoRequest) => { + return ( + params.symbol === currentTokenInfoRef.current?.symbol && + params.officialWebsite === currentTokenInfoRef.current?.officialWebsite && + params.officialTwitter === currentTokenInfoRef.current?.officialTwitter && + params.title === currentTokenInfoRef.current?.title && + params.personName === currentTokenInfoRef.current?.personName && + params.telegramHandler === currentTokenInfoRef.current?.telegramHandler && + params.email === currentTokenInfoRef.current?.email + ); + }, []); + + const handleSubmit = useCallback(async () => { + if (!formValues[TokenInformationFormKeys.TOKEN]?.symbol || !formValues[TokenInformationFormKeys.EMAIL]) return; + + setIsActionButtonLoading(true); + const params: TCommitTokenInfoRequest = { + symbol: formValues[TokenInformationFormKeys.TOKEN]?.symbol, + officialWebsite: formValues[TokenInformationFormKeys.OFFICIAL_WEBSITE], + officialTwitter: formValues[TokenInformationFormKeys.OFFICIAL_TWITTER], + title: formValues[TokenInformationFormKeys.TITLE], + personName: formValues[TokenInformationFormKeys.PERSON_NAME], + telegramHandler: formValues[TokenInformationFormKeys.TELEGRAM_HANDLER], + email: formValues[TokenInformationFormKeys.EMAIL], + }; + + if (checkFormChange(params)) { + handleNextStep({ symbol: params.symbol }); + setIsActionButtonLoading(false); + } else { + try { + const isSuccess = await commitTokenInfo(params); + if (isSuccess) { + handleNextStep({ symbol: params.symbol }); + } + } catch (error: any) { + CommonMessage.error(handleListingErrorMessage(error)); + } finally { + setIsActionButtonLoading(false); + } + } + }, [checkFormChange, formValues, handleNextStep]); + + const getCommonFormItemProps = useCallback( + (key: TokenInformationFormKeys) => ({ + validateStatus: formValidateData[key].validateStatus, + help: formValidateData[key].errorMessage, + }), + [formValidateData], + ); + + const getCommonInputProps = useCallback( + (key: TokenInformationFormKeys): Partial => { + const id = `tokenInformationInput_${key}`; + return { + size: 'large', + allowClear: true, + autoComplete: 'off', + placeholder: TOKEN_INFORMATION_FORM_PLACEHOLDER_MAP[key], + value: formValues[key] as string, + id, + onFocus: () => handleInputFocus(id), + }; + }, + [formValues], + ); + + const renderTokenRequirements = useCallback(() => { + const requirements = []; + if (tokenConfig?.liquidityInUsd) { + requirements.push(`Liquidity > $${tokenConfig.liquidityInUsd}`); + } + if (tokenConfig?.holders) { + requirements.push(`Holders > ${tokenConfig.holders}`); + } + return requirements.map((requirement, index) => ( +

{`${!isMobile ? `${index + 1}. ` : ''}${requirement}`}

+ )); + }, [isMobile, tokenConfig?.holders, tokenConfig?.liquidityInUsd]); + + return ( +
+
+ + {LISTING_STEP_ITEMS[ListingStep.TOKEN_INFORMATION].title} + + {(tokenConfig?.liquidityInUsd || tokenConfig?.holders) && ( + +

The token must meet the requirements of:

+ {renderTokenRequirements()} + + } + /> + )} +
+
+ + + {TOKEN_INFORMATION_FORM_LABEL_MAP[TokenInformationFormKeys.TOKEN]} + + +
+ }> + + + + handleUrlChange(e.target.value, TokenInformationFormKeys.OFFICIAL_WEBSITE)} + /> + + + handleUrlChange(e.target.value, TokenInformationFormKeys.OFFICIAL_TWITTER)} + /> + + + handleCommonInputChange(e.target.value, TokenInformationFormKeys.TITLE)} + /> + + + handleCommonInputChange(e.target.value, TokenInformationFormKeys.PERSON_NAME)} + /> + + + handleCommonInputChange(e.target.value, TokenInformationFormKeys.TELEGRAM_HANDLER)} + /> + + + handleEmailChange(e.target.value)} + /> + + +
+ {isAelfActive ? ( + + {!isActionButtonLoading && BUTTON_TEXT_NEXT} + + ) : ( + handleAelfLogin(true, initRef.current)}> + {CONNECT_AELF_WALLET} + + )} +
+ + ); +} diff --git a/src/page-components/ListingApplication/TokenInformation/styles.module.less b/src/page-components/ListingApplication/TokenInformation/styles.module.less new file mode 100644 index 00000000..1690fa57 --- /dev/null +++ b/src/page-components/ListingApplication/TokenInformation/styles.module.less @@ -0,0 +1,73 @@ +@import 'src/assets/theme/color.less'; +@import 'src/assets/theme/font.less'; +@import 'src/assets/theme/constants.less'; + +.token-information-title-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + + .token-information-title { + .font-family-semibold(); + + font-size: 20px; + line-height: 28px; + } +} + +.token-information-form { + padding-top: 32px; + + :global { + .@{app-prefix}-form-item { + margin-bottom: 32px; + &:has(.@{app-prefix}-form-item-explain) { + margin-bottom: 0; + } + } + } + + .token-information-form-label-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + } + + .token-information-form-label { + color: @font-13; + font-size: 16px; + line-height: 22px; + } +} + +.token-information-footer-button-wrapper { + display: flex; + justify-content: center; +} + +.token-information-footer-button { + width: 240px; + height: 48px; + font-size: 16px; + line-height: 24px; +} + +@media screen and (max-width: @md-width) { + .token-information-form { + padding-top: 24px; + + :global { + .@{app-prefix}-form-item { + margin-bottom: 24px; + &:has(.@{app-prefix}-form-item-explain) { + margin-bottom: 0; + } + } + } + } + + .token-information-footer-button { + width: 100%; + } +} diff --git a/src/page-components/ListingApplication/TokenRow/index.tsx b/src/page-components/ListingApplication/TokenRow/index.tsx new file mode 100644 index 00000000..a36c4d2f --- /dev/null +++ b/src/page-components/ListingApplication/TokenRow/index.tsx @@ -0,0 +1,27 @@ +import clsx from 'clsx'; +import DisplayImage from 'components/DisplayImage'; +import { formatSymbolDisplay } from 'utils/format'; +import styles from './styles.module.less'; + +interface ITokenRowProps { + className?: string; + symbol?: string; + name?: string; + icon?: string; +} + +export default function TokenRow({ className, symbol = '', name = '', icon = '' }: ITokenRowProps) { + return ( +
+ + {formatSymbolDisplay(symbol)} + {name} +
+ ); +} diff --git a/src/page-components/ListingApplication/TokenRow/styles.module.less b/src/page-components/ListingApplication/TokenRow/styles.module.less new file mode 100644 index 00000000..953443ca --- /dev/null +++ b/src/page-components/ListingApplication/TokenRow/styles.module.less @@ -0,0 +1,32 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.token-row { + overflow: hidden; + display: flex; + align-items: center; + gap: 8px; +} + +.token-logo { + margin-right: 0; + line-height: 1; +} + +.token-symbol, +.token-name { + flex-shrink: 1; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.token-symbol { + .font-family-medium(); +} + +.token-name { + color: @font-13; + font-size: 12px; + line-height: 20px; +} diff --git a/src/page-components/ListingApplication/UnsavedChangesWarningModal/index.tsx b/src/page-components/ListingApplication/UnsavedChangesWarningModal/index.tsx new file mode 100644 index 00000000..891fa39d --- /dev/null +++ b/src/page-components/ListingApplication/UnsavedChangesWarningModal/index.tsx @@ -0,0 +1,30 @@ +import CommonModal, { ICommonModalProps } from 'components/CommonModal'; +import CommonButton from 'components/CommonButton'; +import styles from './styles.module.less'; + +type TUnsavedChangesWarningModalProps = Pick; + +export default function UnsavedChangesWarningModal(props: TUnsavedChangesWarningModalProps) { + return ( + +

+ Are you sure you want to go back? Any information you’ve entered will be lost. +

+
+ + Cancel + + + Confirm + +
+
+ ); +} diff --git a/src/page-components/ListingApplication/UnsavedChangesWarningModal/styles.module.less b/src/page-components/ListingApplication/UnsavedChangesWarningModal/styles.module.less new file mode 100644 index 00000000..7b96af0b --- /dev/null +++ b/src/page-components/ListingApplication/UnsavedChangesWarningModal/styles.module.less @@ -0,0 +1,53 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.unsaved-changes-warning-modal { + :global { + .@{app-prefix}-modal-content { + height: 100%; + .@{app-prefix}-modal-close { + .@{app-prefix}-modal-close-x { + height: 68px; + padding-top: 8px; + svg path { + fill: @font-15; + } + } + } + .@{app-prefix}-modal-header { + height: 68px; + padding-top: 24px; + padding-bottom: 16px; + + .@{app-prefix}-modal-title { + .font-family-semibold(); + + font-size: 16px; + line-height: 24px; + text-align: left; + } + } + .@{app-prefix}-modal-body { + width: 100%; + min-width: auto; + padding-top: 0; + } + } + } + + .unsaved-changes-warning-modal-text { + line-height: 20px; + color: @font-13; + } + + .unsaved-changes-warning-modal-footer { + display: flex; + gap: 16px; + margin-top: 16px; + } + + .unsaved-changes-warning-modal-footer-button { + flex: 1; + height: 48px; + } +} diff --git a/src/page-components/ListingApplication/ViewProgress/index.tsx b/src/page-components/ListingApplication/ViewProgress/index.tsx new file mode 100644 index 00000000..471f3612 --- /dev/null +++ b/src/page-components/ListingApplication/ViewProgress/index.tsx @@ -0,0 +1,114 @@ +import CommonModal from 'components/CommonModal'; +import { useMemo } from 'react'; +import { formatSymbol } from 'utils/token'; +import { GOT_IT } from 'constants/misc'; +import { ApplicationChainStatusEnum } from 'types/api'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { ListingProcessStep, VIEW_PROGRESS, VIEW_PROGRESS_STEPS } from 'constants/listingApplication'; +import DisplayImage from 'components/DisplayImage'; +import CommonSteps, { ICommonStepsProps } from 'components/CommonSteps'; +import CommonButton from 'components/CommonButton'; +import { getChainIdByAPI, getChainName } from 'utils/chain'; +import clsx from 'clsx'; +import styles from './styles.module.less'; +import { TelegramPlatform } from 'utils/telegram/telegram'; + +export default function ViewProgress({ + className, + open = false, + status, + tokenSymbol, + tokenIcon, + chainId, + onClose, + onConfirm, +}: { + className?: string; + open?: boolean; + status: ApplicationChainStatusEnum; + tokenSymbol: string; + tokenIcon?: string; + chainId: string; + onClose: () => void; + onConfirm: () => void; +}) { + const isMd = useMediaQueries('md'); + + const currentStep = useMemo(() => { + if (status === ApplicationChainStatusEnum.Unissued) { + return ListingProcessStep.BASIC_INFO; + } + if (status === ApplicationChainStatusEnum.Issuing) { + return ListingProcessStep.SELECT_CHAIN; + } + if ( + status === ApplicationChainStatusEnum.Issued || + status === ApplicationChainStatusEnum.PoolInitializing || + status === ApplicationChainStatusEnum.PoolInitialized + ) { + return ListingProcessStep.INITIALIZE_TOKEN_POOL; + } + + // ApplicationChainStatusEnum.Failed; + return ListingProcessStep.COMPLETE; + }, [status]); + + const formatSteps = useMemo(() => { + const _stepItem: ICommonStepsProps['stepItems'] = JSON.parse(JSON.stringify(VIEW_PROGRESS_STEPS)); + _stepItem.forEach((item, index) => { + if (index === currentStep) { + item.isLoading = true; + } + }); + return _stepItem; + }, [currentStep]); + + const content = useMemo(() => { + return ( +
+
+
+ + {formatSymbol(tokenSymbol)} +
+
{getChainName(getChainIdByAPI(chainId))}
+
+
+ +
+
+ ); + }, [tokenSymbol, tokenIcon, chainId, formatSteps, currentStep]); + + return ( + + {GOT_IT} + + }> + {content} + + ); +} diff --git a/src/page-components/ListingApplication/ViewProgress/styles.module.less b/src/page-components/ListingApplication/ViewProgress/styles.module.less new file mode 100644 index 00000000..382e9e92 --- /dev/null +++ b/src/page-components/ListingApplication/ViewProgress/styles.module.less @@ -0,0 +1,107 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/font.less'; + +.view-progress-modal { + :global { + .@{app-prefix}-modal-content { + height: 100%; + min-width: 360px; + .@{app-prefix}-modal-close { + .@{app-prefix}-modal-close-x { + width: 68px; + height: 68px; + padding-top: 8px; + svg path { + fill: @font-15; + } + } + } + .@{app-prefix}-modal-header { + height: 60px; + padding-top: 24px; + padding-bottom: 8px; + } + .@{app-prefix}-modal-body { + width: 100%; + min-width: auto; + padding-top: 0; + + .@{app-prefix}-steps-item-description { + .font-family-regular() !important; + } + } + .@{app-prefix}-modal-footer{ + border-top: none; + padding-top: 0; + padding-bottom: 24px; + @media screen and (max-width: @md-width) { + padding: 24px; + padding-bottom: 34px; + } + } + } + } +} + +.token-info { + background-color: @bg-16; + padding: 9px 12px; + border-radius: 6px; + + .token-symbol { + font-size: 14px; + line-height: 22px; + .font-family-medium(); + } + + .token-symbol-icon{ + font-size: 12px; + } + + .token-network { + color: @font-13; + font-size: 12px; + line-height: 20px; + } +} + +.view-progress-steps-wrapper { + padding-top: 16px; + margin-top: 8px; + margin-bottom: 8px; + + .view-progress-steps { + :global { + .@{app-prefix}-steps-item { + &:last-child { + .@{app-prefix}-steps-item-content { + min-height: auto; + } + } + &:not(:last-child) { + .@{app-prefix}-steps-item-content { + min-height: 80px !important; + } + } + .@{app-prefix}-steps-item-content { + .@{app-prefix}-steps-item-title { + margin-top: 2px; + } + } + .@{app-prefix}-steps-item-icon { + width: 24px; + height: 24px; + .@{app-prefix}-steps-icon { + font-size: 12px; + } + } + } + } + } +} + +.confirm-button { + width: 100%; + height: 48px; +} diff --git a/src/page-components/ListingApplication/index.tsx b/src/page-components/ListingApplication/index.tsx new file mode 100644 index 00000000..a940be6a --- /dev/null +++ b/src/page-components/ListingApplication/index.tsx @@ -0,0 +1,170 @@ +import { useEffect, useMemo, useRef, useState, memo, useCallback } from 'react'; +import { useRouter } from 'next/router'; +import CommonSteps from 'components/CommonSteps'; +import CommonImage from 'components/CommonImage'; +import UnsavedChangesWarningModal from './UnsavedChangesWarningModal'; +import TokenInformation from './TokenInformation'; +import SelectChain from './SelectChain'; +import InitializeTokenPool from './InitializeTokenPool'; +import { backIcon } from 'assets/images'; +import { LISTING_STEP_ITEMS, ListingStep, LISTING_STEP_PATHNAME_MAP } from 'constants/listingApplication'; +import { ROUTE_PATHS } from 'constants/link'; +import { useMobile } from 'contexts/useStore/hooks'; +import { isMobileDevices } from 'utils/isMobile'; +import { getListingUrl } from 'utils/listingApplication'; +import { useAelfAuthListener } from 'hooks/wallet'; +import { TSearchParams } from 'types/listingApplication'; +import styles from './styles.module.less'; + +let globalCanAccessStep = false; + +function ListingApplication() { + const isMobile = useMobile(); + const router = useRouter(); + const { query } = router; + const symbol = useMemo(() => (query.symbol as string) || undefined, [query]); + const networks = useMemo(() => { + const str = query.networks || ''; + try { + return str && typeof str === 'string' ? JSON.parse(str) : []; + } catch (error) { + return []; + } + }, [query]); + + const [currentStep, setCurrentStep] = useState(); + const [isWarningModalOpen, setIsWarningModalOpen] = useState(false); + + const isNavigatingRef = useRef(false); + const nextUrlRef = useRef(''); + + useAelfAuthListener(); + + useEffect(() => { + const { stepText } = router.query; + const step = Object.values(LISTING_STEP_PATHNAME_MAP).findIndex( + (item) => stepText && typeof stepText === 'string' && item.includes(stepText), + ); + + if (step === ListingStep.TOKEN_INFORMATION || step === ListingStep.SELECT_CHAIN || globalCanAccessStep) { + setCurrentStep(step); + } else { + router.replace('/my-applications'); + } + }, [router]); + + useEffect(() => { + if (isMobileDevices()) return; + + const handleBeforeUnload = (e: BeforeUnloadEvent) => { + if (currentStep === ListingStep.TOKEN_INFORMATION || currentStep === ListingStep.SELECT_CHAIN) { + e.preventDefault(); + } + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, [currentStep]); + + const handleNextStep = useCallback( + (params?: TSearchParams) => { + if (typeof currentStep !== 'number') return; + const nextStep = currentStep + 1; + if (nextStep <= ListingStep.INITIALIZE_TOKEN_POOL) { + globalCanAccessStep = true; + const replaceUrl = getListingUrl(nextStep, params); + router.replace(replaceUrl); + } + }, + [currentStep, router], + ); + + const handlePrevStep = useCallback( + (params?: TSearchParams) => { + if (typeof currentStep !== 'number') return; + const prevStep = currentStep - 1; + if (prevStep >= ListingStep.TOKEN_INFORMATION) { + globalCanAccessStep = true; + const replaceUrl = getListingUrl(prevStep, params); + router.replace(replaceUrl); + } + }, + [currentStep, router], + ); + + const handleWarningModalConfirm = useCallback(() => { + setIsWarningModalOpen(false); + if (nextUrlRef.current === 'back') { + isNavigatingRef.current = true; + router.back(); + } else if (nextUrlRef.current) { + isNavigatingRef.current = true; + router.push(nextUrlRef.current); + } + }, [router]); + + const handleWarningModalCancel = useCallback(() => { + setIsWarningModalOpen(false); + nextUrlRef.current = ''; + }, []); + + const renderForm = useMemo(() => { + switch (currentStep) { + case ListingStep.TOKEN_INFORMATION: + return ; + case ListingStep.SELECT_CHAIN: + return ; + case ListingStep.INITIALIZE_TOKEN_POOL: + return ; + default: + return null; + } + }, [currentStep, handleNextStep, handlePrevStep, networks, symbol]); + + return ( + <> +
+ {typeof currentStep === 'number' && ( +
+ {!isMobile && ( +
{ + if (currentStep === ListingStep.TOKEN_INFORMATION || currentStep === ListingStep.SELECT_CHAIN) { + setIsWarningModalOpen(true); + nextUrlRef.current = window.history.length > 1 ? 'back' : ROUTE_PATHS.BRIDGE; + } else if (window.history.length > 1) { + router.back(); + } else { + router.push(ROUTE_PATHS.BRIDGE); + } + }}> + + Back +
+ )} +
+
+
Listing Application
+ +
+
+
{renderForm}
+
+
+
+ )} +
+ + + ); +} + +export default memo(ListingApplication); diff --git a/src/page-components/ListingApplication/styles.module.less b/src/page-components/ListingApplication/styles.module.less new file mode 100644 index 00000000..629d4a40 --- /dev/null +++ b/src/page-components/ListingApplication/styles.module.less @@ -0,0 +1,127 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + +@listing-content-padding: 48px; + +.listing-container { + flex: 1; + height: 100%; + padding: 48px 0; + background-color: @bg-14; +} + +.listing-content { + max-width: calc(890px + (@listing-content-padding * 2)); + margin: 0 auto; + padding: 0 @listing-content-padding; +} + +.listing-back { + height: 26px; + display: inline-flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; + cursor: pointer; +} + +.listing-back-icon { + width: 12px; + height: 12px; +} + +.listing-back-text { + .font-family-medium(); + + font-size: 14px; + line-height: 22px; + color: @font-15; +} + +.listing-card-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.listing-card { + padding: 32px; + border-radius: 16px; + border: 1px solid @border-5; + background-color: @bg-2; +} + +.listing-card-steps-title { + margin-bottom: 24px; + .font-family-semibold(); + + font-size: 24px; + line-height: 32px; +} + +.listing-card-form-content { + :global { + .@{app-prefix}-form-item-label { + label { + width: 100%; + color: @font-13; + font-size: 16px; + line-height: 22px; + } + } + + .@{app-prefix}-input, + .@{app-prefix}-input-affix-wrapper { + padding: 12px; + border-radius: 6px; + border-color: @border-5; + font-size: 14px; + line-height: 22px; + .font-family-medium(); + + &::placeholder { + color: @font-18; + .font-family-medium(); + } + + &:hover { + border-color: @bg-17; + } + + &:focus, + &.@{app-prefix}-input-affix-wrapper-focused { + border-color: @bg-17; + box-shadow: none; + caret-color: @bg-17; + } + } + + .@{app-prefix}-input-suffix { + margin-left: 8px; + } + + .@{app-prefix}-form-item-margin-offset { + margin-bottom: 0 !important; + } + } +} + +@media screen and (max-width: @md-width) { + .listing-container { + padding: 16px 12px 48px; + background-color: @bg-2; + } + + .listing-content { + padding: 0; + } + + .listing-card { + padding: 24px 12px; + } + + .listing-card-steps-title { + margin-bottom: 24px; + } +} diff --git a/src/page-components/MyApplication/ActionBox/index.tsx b/src/page-components/MyApplication/ActionBox/index.tsx new file mode 100644 index 00000000..b6d3cda4 --- /dev/null +++ b/src/page-components/MyApplication/ActionBox/index.tsx @@ -0,0 +1,76 @@ +import { useCallback, useMemo, useState } from 'react'; +import { BRIDGE_NOW, VIEW_PROGRESS } from 'constants/listingApplication'; +import { useRouter } from 'next/router'; +import { ApplicationChainStatusEnum } from 'types/api'; +import styles from './styles.module.less'; +import ViewProgress from 'page-components/ListingApplication/ViewProgress'; +import { ROUTE_PATHS } from 'constants/link'; +import { DEFAULT_NULL_VALUE } from 'constants/misc'; + +export default function ActionBox({ + symbol, + tokenIcon, + chainId, + status, +}: { + symbol: string; + tokenIcon?: string; + chainId: string; + status: ApplicationChainStatusEnum; +}) { + const router = useRouter(); + const [openViewProgress, setOpenViewProgress] = useState(false); + + const isSucceed = useMemo(() => { + return status === ApplicationChainStatusEnum.Complete; + }, [status]); + + const isFailed = useMemo(() => { + return status === ApplicationChainStatusEnum.Failed; + }, [status]); + + const handleViewProgress = useCallback(() => { + setOpenViewProgress(true); + }, []); + + const handleCloseViewProgress = useCallback(() => { + setOpenViewProgress(false); + }, []); + + const handleConfirmViewProgress = useCallback(() => { + setOpenViewProgress(false); + }, []); + + const handleGoBridge = useCallback(() => { + router.push(ROUTE_PATHS.BRIDGE); + }, [router]); + + if (isSucceed) { + return ( +
+ {BRIDGE_NOW} +
+ ); + } + + if (isFailed) { + return
{DEFAULT_NULL_VALUE}
; + } + + return ( + <> +
+ {VIEW_PROGRESS} +
+ + + ); +} diff --git a/src/page-components/MyApplication/ActionBox/styles.module.less b/src/page-components/MyApplication/ActionBox/styles.module.less new file mode 100644 index 00000000..f070ccae --- /dev/null +++ b/src/page-components/MyApplication/ActionBox/styles.module.less @@ -0,0 +1,11 @@ +@import '/src/assets/theme/color.less'; + +.action { + color: @primary-color; + cursor: pointer; +} + +.action-disable { + color: @bg-4; + cursor: not-allowed; +} diff --git a/src/page-components/MyApplication/MyApplicationTable/index.tsx b/src/page-components/MyApplication/MyApplicationTable/index.tsx new file mode 100644 index 00000000..b400d295 --- /dev/null +++ b/src/page-components/MyApplication/MyApplicationTable/index.tsx @@ -0,0 +1,121 @@ +import CommonTable from 'components/CommonTable'; +import clsx from 'clsx'; +import { formatSymbol } from 'utils/token'; +import styles from './styles.module.less'; +import StatusBox from '../StatusBox'; +import ActionBox from '../ActionBox'; +import { TMyApplicationItem } from 'types/api'; +import { getApplicationDisplayInfo } from '../utils'; +import { NO_APPLICATION } from 'constants/listingApplication'; +import { DEFAULT_NULL_VALUE } from 'constants/misc'; +import DisplayImage from 'components/DisplayImage'; +import { TablePagination } from 'page-components/Transactions/components'; +import IconFont from 'components/IconFont'; +import { getChainIdByAPI, getChainName, getIconByChainId } from 'utils/chain'; + +const MyApplicationTableColumns = [ + { + title: 'Token', + dataIndex: 'symbol', + key: 'symbol', + render: (symbol: string, item: TMyApplicationItem) => { + const { chainTokenInfo } = getApplicationDisplayInfo(item); + return ( +
+ + {formatSymbol(symbol)} +
+ ); + }, + }, + { + title: 'Chain', + dataIndex: 'networkName', + key: 'networkName', + render: (_: any, item: TMyApplicationItem) => { + const { chainTokenInfo } = getApplicationDisplayInfo(item); + const iconProps = getIconByChainId(getChainIdByAPI(chainTokenInfo?.chainId || '')); + return chainTokenInfo?.chainId ? ( +
+ +
{getChainName(getChainIdByAPI(chainTokenInfo?.chainId))}
+
+ ) : ( + DEFAULT_NULL_VALUE + ); + }, + }, + { + title: 'Status', + dataIndex: 'status', + key: 'status', + width: '112px', + render: (_: any, item: TMyApplicationItem) => { + const { chainTokenInfo, failReason } = getApplicationDisplayInfo(item); + return chainTokenInfo?.status ? ( + + ) : ( + DEFAULT_NULL_VALUE + ); + }, + }, + { + title: 'Action', + dataIndex: 'action', + key: 'action', + align: 'right' as any, + render: (_: any, item: TMyApplicationItem) => { + const { chainTokenInfo } = getApplicationDisplayInfo(item); + return chainTokenInfo?.status && chainTokenInfo.chainId ? ( + + ) : ( + DEFAULT_NULL_VALUE + ); + }, + }, +]; + +export default function MyApplicationTable({ + applicationList, + totalCount, + maxResultCount, + skipPageCount, + tableOnChange, +}: { + applicationList: TMyApplicationItem[]; + totalCount: number; + maxResultCount: number; + skipPageCount: number; + tableOnChange: (page: number, pageSize: number) => void; +}) { + return ( + <> + 0 ? null : styles['my-application-table-empty'], + )} + rowKey={'id'} + dataSource={applicationList} + columns={MyApplicationTableColumns} + scroll={{ x: 670 }} + emptyText={NO_APPLICATION} + pagination={{ hideOnSinglePage: true, pageSize: maxResultCount }} + /> + {totalCount > maxResultCount && ( + + )} + + ); +} diff --git a/src/page-components/MyApplication/MyApplicationTable/styles.module.less b/src/page-components/MyApplication/MyApplicationTable/styles.module.less new file mode 100644 index 00000000..e7508f09 --- /dev/null +++ b/src/page-components/MyApplication/MyApplicationTable/styles.module.less @@ -0,0 +1,66 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/font.less'; + +.my-application-table { + .token-symbol { + max-width: 152px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .network-icon { + height: 24px !important; + width: 24px !important; + border-radius: 50%; + font-size: 24px; + } + :global { + .@{app-prefix}-table-cell-ellipsis { + white-space: normal; + word-break: normal; + } + .@{app-prefix}-table-thead > tr > th { + padding: 13px 12px; + background: @bg-19; + border-bottom-width: 0; + color: @font-15; + line-height: 22px; + .font-family-medium(); + .text-overflow(1); + + white-space: nowrap; + } + .@{app-prefix}-table-container table > thead > tr:first-child th:last-child { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + } + .@{app-prefix}-table-container table > thead > tr:first-child th:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + } + .@{app-prefix}-table-tbody > tr > td { + padding: 16px 8px; + color: @font-15; + .font-family-medium(); + } + .@{app-prefix}-table-thead > tr > th, + .@{app-prefix}-table-tbody > tr > td, + .@{app-prefix}-table tfoot > tr > th, + .@{app-prefix}-table tfoot > tr > td { + font-size: 14px; + line-height: 20px; + &::before { + display: none; + } + } + } +} + +.my-application-table-empty { + :global { + .@{app-prefix}-table-tbody > tr > td { + border-bottom: none; + } + } +} diff --git a/src/page-components/MyApplication/StatusBox/index.tsx b/src/page-components/MyApplication/StatusBox/index.tsx new file mode 100644 index 00000000..1b914d22 --- /dev/null +++ b/src/page-components/MyApplication/StatusBox/index.tsx @@ -0,0 +1,59 @@ +import styles from './styles.module.less'; +import { useMemo } from 'react'; +import { TMyApplicationStatus } from 'types/listingApplication'; +import clsx from 'clsx'; +import { ApplicationChainStatusEnum } from 'types/api'; +import CommonImage from 'components/CommonImage'; +import { questionFilledIcon } from 'assets/images'; +import ListingTip from 'page-components/ListingApplication/ListingTip'; + +type TStatusBoxProps = { + wrapperClassName?: string; + className?: string; + status: ApplicationChainStatusEnum; + failReason?: string; +}; + +export default function StatusBox({ wrapperClassName, className, status, failReason }: TStatusBoxProps) { + const isSucceed = useMemo(() => { + return status === ApplicationChainStatusEnum.Complete; + }, [status]); + + const isFailed = useMemo(() => { + return status === ApplicationChainStatusEnum.Failed; + }, [status]); + + const content = useMemo(() => { + if (isSucceed) { + return ( +
+
+ {TMyApplicationStatus.Succeed} +
+ ); + } + + if (isFailed) { + return ( +
+
+ {TMyApplicationStatus.Failed} + } + /> +
+ ); + } + + return ( +
+
+ {TMyApplicationStatus.Processing} +
+ ); + }, [isSucceed, isFailed, className, failReason]); + + return
{content}
; +} diff --git a/src/page-components/MyApplication/StatusBox/styles.module.less b/src/page-components/MyApplication/StatusBox/styles.module.less new file mode 100644 index 00000000..a71ff107 --- /dev/null +++ b/src/page-components/MyApplication/StatusBox/styles.module.less @@ -0,0 +1,38 @@ +@import '/src/assets/theme/color.less'; + +.status-wrapper { + min-width: 112px; +} + +.status-box { + display: flex; + justify-content: flex-start; + align-items: center; + color: @font-13; + white-space: nowrap; + gap: 4px; +} + +.badge { + width: 8px; + height: 8px; + border-radius: 50%; +} + +.badge-success { + background-color: @bg-18; +} + +.badge-failed { + background-color: @bg-21; +} + +.badge-warning { + background-color: @bg-20; +} + +.question-icon { + width: 22px; + height: 22px; + cursor: pointer; +} diff --git a/src/page-components/MyApplication/index.tsx b/src/page-components/MyApplication/index.tsx new file mode 100644 index 00000000..6bf701a8 --- /dev/null +++ b/src/page-components/MyApplication/index.tsx @@ -0,0 +1,181 @@ +import clsx from 'clsx'; +import { memo, useCallback, useRef, useState } from 'react'; +import { useEffectOnce } from 'react-use'; +import { getMyApplicationList } from 'utils/api/application'; +import MyApplicationTable from './MyApplicationTable'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { useAElf } from 'hooks/web3'; +import { useAelfAuthListener, useAelfLogin } from 'hooks/wallet'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; +import styles from './styles.module.less'; +import CommonImage from 'components/CommonImage'; +import { backIcon } from 'assets/images'; +import { BUTTON_TEXT_BACK } from 'constants/misc'; +import { MY_APPLICATIONS } from 'constants/listingApplication'; +import { useRouter } from 'next/router'; +import { CONTACT_US_FORM_URL } from 'constants/link'; +import { openWithBlank } from 'utils/link'; +import { sleep } from 'utils'; +import { useSetAelfAuthFromStorage } from 'hooks/aelfAuthToken'; +import useGlobalLoading from 'hooks/useGlobalLoading'; + +const DefaultSkipCount = 0; +const DefaultMaxResultCount = 10; +const DefaultTotalCount = 0; + +function MyApplications() { + const router = useRouter(); + const isMd = useMediaQueries('md'); + const { setGlobalLoading } = useGlobalLoading(); + const { isActive } = useAElf(); + const handleAelfLogin = useAelfLogin(); + const setAelfAuthFromStorage = useSetAelfAuthFromStorage(); + useAelfAuthListener(); + const [currentApplicationList, setCurrentApplicationList] = useState([]); + + // pagination + const [skipPageCount, setSkipPageCount] = useState(DefaultSkipCount); + const [maxResultCount, setMaxResultCount] = useState(DefaultMaxResultCount); + const [totalCount, setTotalCount] = useState(DefaultTotalCount); + + const getApplicationData = useCallback( + async ({ skip, max }: { skip?: number; max?: number }) => { + console.log(skip, max); + try { + setGlobalLoading(true); + await setAelfAuthFromStorage(); + await sleep(500); + + const currentSkipPageCount = typeof skip !== 'number' ? skipPageCount : skip; + const currentMaxCount = typeof max !== 'number' ? maxResultCount : max; + const currentSkipCount = currentSkipPageCount * currentMaxCount; + const res = await getMyApplicationList({ + skipCount: currentSkipCount, + maxResultCount: currentMaxCount, + }); + + setCurrentApplicationList(res.items); + setTotalCount(res.totalCount); + } catch (error) { + console.log('>>>>>> getApplicationData error', error); + } finally { + setGlobalLoading(false); + } + }, + [maxResultCount, setAelfAuthFromStorage, setGlobalLoading, skipPageCount], + ); + + // web get page date + const tableOnChange = useCallback( + (page: number, pageSize: number) => { + let skip = skipPageCount; + let max = maxResultCount; + const newPage = page - 1; + if (newPage !== skipPageCount) { + skip = newPage; + setSkipPageCount(newPage); + } + if (maxResultCount !== pageSize) { + // pageSize change and skipCount need init + skip = DefaultSkipCount; + max = pageSize; + setSkipPageCount(DefaultSkipCount); + setMaxResultCount(pageSize); + } + + getApplicationData({ + skip, + max, + }); + }, + [getApplicationData, maxResultCount, skipPageCount], + ); + + const init = useCallback(async () => { + getApplicationData({}); + }, [getApplicationData]); + const initRef = useRef(init); + initRef.current = init; + + const connectAndInit = useCallback(() => { + if (!isActive) { + handleAelfLogin(true, initRef.current, true); + } else { + initRef.current(); + } + }, [handleAelfLogin, isActive]); + const connectAndInitRef = useRef(connectAndInit); + connectAndInitRef.current = connectAndInit; + const connectAndInitSleep = useCallback(async () => { + setGlobalLoading(true); + // Delay 3s to determine the login status, because the login data is acquired slowly, to prevent the login pop-up window from being displayed first and then automatically logging in successfully later. + await sleep(3000); + connectAndInitRef.current(); + }, [setGlobalLoading]); + useEffectOnce(() => { + connectAndInitSleep(); + }); + + const initForLogout = useCallback(async () => { + setCurrentApplicationList([]); + setSkipPageCount(DefaultSkipCount); + setMaxResultCount(DefaultMaxResultCount); + setTotalCount(DefaultSkipCount); + }, []); + const initLogoutRef = useRef(initForLogout); + initLogoutRef.current = initForLogout; + + const initForReLogin = useCallback(async () => { + getApplicationData({ skip: DefaultSkipCount, max: DefaultMaxResultCount }); + }, [getApplicationData]); + const initForReLoginRef = useRef(initForReLogin); + initForReLoginRef.current = initForReLogin; + + useEffectOnce(() => { + // log in + const { remove: removeLoginSuccess } = eBridgeEventBus.AelfLoginSuccess.addListener(() => + initForReLoginRef.current(), + ); + // log out \ exit + const { remove: removeLogoutSuccess } = eBridgeEventBus.AelfLogoutSuccess.addListener(() => { + initLogoutRef.current(); + }); + + return () => { + removeLoginSuccess(); + removeLogoutSuccess(); + }; + }); + + return ( +
+ {!isMd && ( +
router.back()}> + +
{BUTTON_TEXT_BACK}
+
+ )} + +
+
+
{MY_APPLICATIONS}
+ +
+ {`If you need any support, please`}  + openWithBlank(CONTACT_US_FORM_URL)}>{`contact us`} + {`.`} +
+
+ +
+
+ ); +} + +export default memo(MyApplications); diff --git a/src/page-components/MyApplication/styles.module.less b/src/page-components/MyApplication/styles.module.less new file mode 100644 index 00000000..9c038302 --- /dev/null +++ b/src/page-components/MyApplication/styles.module.less @@ -0,0 +1,83 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + +.my-applications-page-container-wrapper { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + background-color: @bg-14; + padding: 48px 48px 250px; + gap: 12px; + + @media screen and (max-width: @md-width) { + padding: 16px; + } +} + +.my-applications-page-back { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 8px; + height: 26px; + max-width: 890px; + width: 100%; + cursor: pointer; + + .my-applications-page-back-icon { + width: 12px; + height: 12px; + span { + width: 12px; + height: 12px; + } + } + + .my-applications-page-back-text { + font-size: 14px; + color: @font-15; + .font-family-medium(); + } +} + +.my-applications-page-body { + max-width: 890px; + width: 100%; + height: 100%; + padding: 32px; + border-radius: 16px; + border: 1px solid @border-5; + background-color: @bg-2; + + @media screen and (max-width: @md-width) { + padding: 24px 12px; + } +} + +.my-applications-page-title { + margin-bottom: 16px; + gap: 8px; + + @media screen and (max-width: @md-width) { + margin-bottom: 24px; + } +} + +.my-applications-page-title-text { + font-size: 24px; + text-align: left; + .font-family-semibold(); +} + +.right-tip { + font-size: 14px; + color: @font-13; +} + +.action { + color: @primary-color; + cursor: pointer; +} diff --git a/src/page-components/MyApplication/utils.tsx b/src/page-components/MyApplication/utils.tsx new file mode 100644 index 00000000..bfb68f97 --- /dev/null +++ b/src/page-components/MyApplication/utils.tsx @@ -0,0 +1,35 @@ +import { ApplicationChainStatusEnum, TMyApplicationItem } from 'types/api'; + +export const getApplicationDisplayInfo = (data: TMyApplicationItem) => { + const chainTokenInfo = data?.chainTokenInfo; + if (!chainTokenInfo) { + return { + chainTokenInfo: undefined, + failTime: 0, + failReason: '', + chainId: '', + }; + } + + const getFailResult = () => { + switch (chainTokenInfo.status) { + case ApplicationChainStatusEnum.Failed: + return { + time: data.failedTime, + reason: data.failedReason, + }; + default: + return { + time: 0, + reason: '', + }; + } + }; + + return { + chainTokenInfo, + chainId: chainTokenInfo.chainId, + failTime: getFailResult().time, + failReason: getFailResult().reason, + }; +}; diff --git a/src/page-components/Pools/Pool/AddPool/index.tsx b/src/page-components/Pools/Pool/AddPool/index.tsx new file mode 100644 index 00000000..15a7c8d8 --- /dev/null +++ b/src/page-components/Pools/Pool/AddPool/index.tsx @@ -0,0 +1,179 @@ +import { Col, Row } from 'antd'; +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import { useMemo, useState } from 'react'; +import { getChainType } from 'utils/chain'; +import styles from '../styles.module.less'; +import { ChainId, TokenInfo } from 'types'; +import { useEVMSwitchChain, useWeb3Wallet } from 'hooks/web3'; +import { useConnect } from 'hooks/useConnect'; +import CommonButton from 'components/CommonButton'; +import { Trans } from 'react-i18next'; +import { useBalances } from 'hooks/useBalances'; +import { isELFChain } from 'utils/aelfUtils'; +import { divDecimals } from 'utils/calculate'; +import CommonAmountRow from 'components/CommonAmountRow'; +import { getMaxAmount, parseInputChange } from 'utils/input'; +import { getShareOfPool, unitConverter } from 'utils/converter'; +import { formatSymbol } from 'utils/token'; +import TokenLogo from 'components/TokenLogo'; +import { usePoolContract, useTokenContract } from 'hooks/useContract'; +import { addLiquidity } from 'utils/pools'; +import { useThrottleCallback } from 'hooks'; +import { ZERO } from 'constants/misc'; +import useLoadingModal from 'hooks/useLoadingModal'; +import { ResultType } from 'components/Loading/ResultModal'; +import { usePoolTotalLiquidity } from 'hooks/pools'; + +export type TAddPoolProps = { + chainId: ChainId; + tokenInfo?: TokenInfo; + price: string | number; +}; + +export default function AddPool({ chainId, tokenInfo, price }: TAddPoolProps) { + const { t } = useLanguage(); + + const [amount, setAmount] = useState(); + const web3Wallet = useWeb3Wallet(chainId); + + const poolContract = usePoolContract(chainId, undefined, web3Wallet?.isPortkey); + const tokenContract = useTokenContract(chainId, tokenInfo?.address, web3Wallet?.isPortkey); + const totalLiquidity = usePoolTotalLiquidity({ poolContract, tokenContract, tokenInfo }); + const evmSwitchChain = useEVMSwitchChain(); + const { loadingOpen, modal, setLoadingModal, setResultModal } = useLoadingModal(); + const { account } = web3Wallet || {}; + const connect = useConnect(); + const [[balance]] = useBalances( + { ...web3Wallet, chainId }, + useMemo(() => { + // if (isELFChain(chainId) || tokenInfo?.isNativeToken) return tokenInfo; + // return tokenInfo?.address; + + if (!chainId) return; + const { symbol, address, isNativeToken } = tokenInfo || {}; + if (isELFChain(chainId)) return [symbol]; + if (isNativeToken) return [symbol]; + return [address]; + }, [chainId, tokenInfo]), + ); + + const showBalance = useMemo(() => divDecimals(balance, tokenInfo?.decimals), [balance, tokenInfo?.decimals]); + + const min = useMemo(() => divDecimals(1, tokenInfo?.decimals), [tokenInfo?.decimals]); + const max = useMemo( + () => getMaxAmount({ chainId, symbol: tokenInfo?.symbol, balance: showBalance }), + [chainId, showBalance, tokenInfo?.symbol], + ); + + const showError = useMemo(() => amount && account && max.lt(amount), [account, amount, max]); + + const onAddLiquidity = useThrottleCallback(async () => { + try { + if (!tokenInfo || !account || !tokenContract || !poolContract || !amount) return; + setLoadingModal({ open: true }); + if (poolContract.contractType === 'ERC') await evmSwitchChain(chainId); + + const req = await addLiquidity({ + tokenInfo, + amount: amount, + account: account, + poolContract, + chainId, + tokenContract, + }); + if (req?.error) { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onAddLiquidity }); + } else { + setAmount(''); + setResultModal({ open: true, type: ResultType.APPROVED }); + } + } catch (error) { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onAddLiquidity }); + } finally { + totalLiquidity.onGetTotalLiquidity(); + setLoadingModal({ open: false }); + } + }, [ + tokenInfo, + account, + tokenContract, + poolContract, + amount, + setLoadingModal, + evmSwitchChain, + chainId, + setResultModal, + totalLiquidity, + ]); + const btnProps = useMemo(() => { + let children = 'Enter Amount'; + let disabled = true; + let onClick = onAddLiquidity; + + if (!account) { + onClick = () => connect(getChainType(chainId), chainId); + children = 'Connect'; + disabled = false; + return { children, disabled, onClick }; + } + + if (max.lt(amount ?? '0')) { + disabled = true; + onClick = () => connect(getChainType(chainId), chainId); + children = 'Insufficient balance'; + return { children, disabled, onClick }; + } + + if (amount && ZERO.lte(amount)) { + children = 'Add'; + disabled = false; + } + + return { children, disabled, onClick }; + }, [account, amount, chainId, connect, max, onAddLiquidity]); + return ( +
+ setAmount(parseInputChange(max.toFixed(), min, tokenInfo?.decimals))} + onAmountInputChange={(e) => setAmount(parseInputChange(e.target.value, min, tokenInfo?.decimals))} + leftHeaderTitle={t('Amount')} + rightHeaderTitle={`${unitConverter(showBalance)} ${formatSymbol(tokenInfo?.symbol)}`} + rightInputEle={ + + +
{formatSymbol(tokenInfo?.symbol)}
+
+ } + /> +
+ {t('Estimated value')}: ${unitConverter(ZERO.plus(amount ?? 0).times(price))} +
+
+
+
{t('Share of Pool')}
{getShareOfPool(amount, totalLiquidity.showTotalLiquidity)}%
+
+
+
{t('Liquidity')}
+
${unitConverter(ZERO.plus(totalLiquidity.showTotalLiquidity).times(price))}
+
+ + {modal} + + {btnProps.children} + + + ); +} diff --git a/src/page-components/Pools/Pool/RemovePool/index.tsx b/src/page-components/Pools/Pool/RemovePool/index.tsx new file mode 100644 index 00000000..e8747ecd --- /dev/null +++ b/src/page-components/Pools/Pool/RemovePool/index.tsx @@ -0,0 +1,162 @@ +import { Col, Row } from 'antd'; +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import { useMemo, useState } from 'react'; +import { getChainType } from 'utils/chain'; +import styles from '../styles.module.less'; +import { ChainId, TokenInfo } from 'types'; +import { useEVMSwitchChain, useWeb3Wallet } from 'hooks/web3'; +import { useConnect } from 'hooks/useConnect'; +import CommonButton from 'components/CommonButton'; +import { Trans } from 'react-i18next'; +import { divDecimals } from 'utils/calculate'; +import CommonAmountRow from 'components/CommonAmountRow'; +import { parseInputChange } from 'utils/input'; +import { percentConverter, unitConverter } from 'utils/converter'; +import { formatSymbol } from 'utils/token'; +import TokenLogo from 'components/TokenLogo'; +import { usePoolContract, useTokenContract } from 'hooks/useContract'; +import { removeLiquidity } from 'utils/pools'; +import { useThrottleCallback } from 'hooks'; +import { ZERO } from 'constants/misc'; +import useLoadingModal from 'hooks/useLoadingModal'; +import { ResultType } from 'components/Loading/ResultModal'; +import { usePoolMyLiquidity, usePoolTotalLiquidity } from 'hooks/pools'; + +export type TRemovePoolProps = { + chainId: ChainId; + tokenInfo?: TokenInfo; + price: string | number; +}; + +export default function RemovePool({ chainId, tokenInfo, price }: TRemovePoolProps) { + const { t } = useLanguage(); + + const [amount, setAmount] = useState(); + + const web3Wallet = useWeb3Wallet(chainId); + const evmSwitchChain = useEVMSwitchChain(); + + const poolContract = usePoolContract(chainId, undefined, web3Wallet?.isPortkey); + const tokenContract = useTokenContract(chainId, tokenInfo?.address, web3Wallet?.isPortkey); + const totalLiquidity = usePoolTotalLiquidity({ poolContract, tokenContract, tokenInfo }); + const myLiquidity = usePoolMyLiquidity({ poolContract, account: web3Wallet?.account, tokenInfo }); + + const { loadingOpen, modal, setLoadingModal, setResultModal } = useLoadingModal(); + const { account } = web3Wallet || {}; + const connect = useConnect(); + + const min = useMemo(() => divDecimals(1, tokenInfo?.decimals), [tokenInfo?.decimals]); + const max = useMemo(() => ZERO.plus(myLiquidity.showMyLiquidity), [myLiquidity.showMyLiquidity]); + + const showError = useMemo(() => amount && account && max.lt(amount), [account, amount, max]); + + const onRemoveLiquidity = useThrottleCallback(async () => { + try { + if (!tokenInfo || !account || !poolContract || !amount) return; + setLoadingModal({ open: true }); + if (poolContract.contractType === 'ERC') await evmSwitchChain(chainId); + const req = await removeLiquidity({ + tokenInfo, + amount: amount, + account: account, + poolContract, + chainId, + }); + if (req?.error) { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onRemoveLiquidity }); + } else { + setAmount(''); + setResultModal({ open: true, type: ResultType.APPROVED }); + } + } catch (error) { + setResultModal({ open: true, type: ResultType.REJECTED, onRetry: onRemoveLiquidity }); + } finally { + totalLiquidity.onGetTotalLiquidity(); + myLiquidity.onGetMyLiquidity(); + setLoadingModal({ open: false }); + } + }, [ + account, + amount, + chainId, + evmSwitchChain, + myLiquidity, + poolContract, + setLoadingModal, + setResultModal, + tokenInfo, + totalLiquidity, + ]); + const btnProps = useMemo(() => { + let children = 'Enter Amount'; + let disabled = true; + let onClick = onRemoveLiquidity; + + if (!account) { + onClick = () => connect(getChainType(chainId), chainId); + children = 'Connect'; + disabled = false; + return { children, disabled, onClick }; + } + + if (max.lt(amount ?? '0')) { + disabled = true; + onClick = () => connect(getChainType(chainId), chainId); + children = 'Insufficient amount'; + return { children, disabled, onClick }; + } + + if (amount && ZERO.lte(amount)) { + children = 'Remove'; + disabled = false; + } + + return { children, disabled, onClick }; + }, [account, amount, chainId, connect, max, onRemoveLiquidity]); + return ( +
+ setAmount(parseInputChange(max.toFixed(), min, tokenInfo?.decimals))} + onAmountInputChange={(e) => setAmount(parseInputChange(e.target.value, min, tokenInfo?.decimals))} + leftHeaderTitle={t('Amount')} + rightHeaderTitle={`${unitConverter(myLiquidity.showMyLiquidity)} ${formatSymbol(tokenInfo?.symbol)}`} + rightInputEle={ + + +
{formatSymbol(tokenInfo?.symbol)}
+
+ } + /> +
+ {t('Estimated value')}: ${unitConverter(ZERO.plus(amount ?? 0).times(price))} +
+
+
+
{t('Share of Pool')}
{' '} +
{percentConverter(ZERO.plus(amount ?? 0).div(totalLiquidity.showTotalLiquidity))}%
+
+
+
{t('Liquidity')}
+
${unitConverter(ZERO.plus(totalLiquidity.showTotalLiquidity).times(price))}
+
+ + {modal} + + {btnProps.children} + + + ); +} diff --git a/src/page-components/Pools/Pool/index.tsx b/src/page-components/Pools/Pool/index.tsx new file mode 100644 index 00000000..5ae316a2 --- /dev/null +++ b/src/page-components/Pools/Pool/index.tsx @@ -0,0 +1,80 @@ +import { Row, Tabs } from 'antd'; +import clsx from 'clsx'; +import IconFont from 'components/IconFont'; +import MainContentHeader from 'components/MainContentHeader'; +import { useLanguage } from 'i18n'; +import { useRouter } from 'next/router'; +import { useEffect, useMemo, useState } from 'react'; +import { useLocation } from 'react-use'; +import { getChainIdByAPI, getChainName, getIconByChainId } from 'utils/chain'; +import styles from './styles.module.less'; +import { ChainId, OperatePool } from 'types'; +import AddPool from './AddPool'; +import RemovePool from './RemovePool'; +import { useGetTokenInfoByWhitelist, useTokenPrice } from 'hooks/token'; +import CommonImage from 'components/CommonImage'; +import { backIcon } from 'assets/images'; +import Col from 'antd/es/grid/col'; +import { useGetTokenInfoByPoolList } from 'hooks/pools'; +export default function Pool() { + const { t } = useLanguage(); + + const [activeKey, setActiveKey] = useState(OperatePool.add); + + const { push, back } = useRouter(); + const { pathname } = useLocation(); + + const [apiChainId, symbol] = useMemo(() => pathname?.replace('/pool/', '').split('/') || [], [pathname]); + const chainId = useMemo(() => getChainIdByAPI(apiChainId), [apiChainId]); + + const getTokenInfoByWhitelist = useGetTokenInfoByWhitelist(); + const getTokenInfoByPoolList = useGetTokenInfoByPoolList(); + const { price } = useTokenPrice(symbol); + + const tokenInfo = useMemo(() => { + return getTokenInfoByWhitelist(chainId as ChainId, symbol) || getTokenInfoByPoolList(chainId as ChainId, symbol); + }, [chainId, getTokenInfoByPoolList, getTokenInfoByWhitelist, symbol]); + + const chainIcon = useMemo(() => { + const iconProps = getIconByChainId(chainId); + if (!iconProps) return null; + return ( + + + {getChainName(chainId)} + + ); + }, [chainId]); + + useEffect(() => { + if (!chainIcon || !tokenInfo) push('/pools'); + }, [chainIcon, push, tokenInfo]); + return ( +
+
+ + +
{t('Back')}
+
+
+ + setActiveKey(v as OperatePool)}> + + + + {activeKey === OperatePool.add ? ( + + ) : ( + + )} +
+ + + ); +} diff --git a/src/page-components/Pools/Pool/styles.module.less b/src/page-components/Pools/Pool/styles.module.less new file mode 100644 index 00000000..4d7076c9 --- /dev/null +++ b/src/page-components/Pools/Pool/styles.module.less @@ -0,0 +1,147 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.pool-page-wrap{ + padding: 48px 48px 250px; + + @media screen and (max-width: @md-width) { + padding: 16px; + } +} + +.estimated-value { + margin-top: 8px; + font-size: 14px; + line-height: 22px; + color: @font-15; +} +.back-row { + background: transparent; + border-width: 0; + padding: 1px; + border-color: transparent; + overflow: hidden; + .back-icon{ + width: 12px; + height: 12px; + margin-right: 8px; + } +} +.pool-page { + gap: 24px; + margin: 12px auto 0; + :global { + .@{app-prefix}-tabs{ + margin-top: 24px; + } + .@{app-prefix}-table-container .@{app-prefix}-table-content { + &::-webkit-scrollbar { + height: 8px; + width: 0; + overflow-y: scroll; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: @border-3; + } + } + .@{app-prefix}-tabs-top > .@{app-prefix}-tabs-nav { + margin-bottom: 0; + } + .@{app-prefix}-tabs-top > .@{app-prefix}-tabs-nav .@{app-prefix}-tabs-ink-bar, + .@{app-prefix}-tabs-top > div > .@{app-prefix}-tabs-nav .@{app-prefix}-tabs-ink-bar { + background: @border-2; + height: 4px; + border-radius: 1px; + } + .@{app-prefix}-tabs-tab { + padding-top: 0; + padding-bottom: 18px; + &:hover { + color: @font-1; + } + .@{app-prefix}-tabs-tab-active .@{app-prefix}-tabs-tab-btn { + text-shadow: none; + } + .@{app-prefix}-tabs-tab-btn { + font-size: 16px; + } + } + .@{app-prefix}-table-ping-right:not(.@{app-prefix}-table-has-fix-right) > .@{app-prefix}-table-container::after { + box-shadow: inset -10px 0 8px -8px rgb(0 0 0 / 3%); + } + } +} +.chain-icon-row { + display: flex; + font-size: 14px; + font-weight: 500; + .chain-icon { + width: 24px; + height: 24px; + font-size: 24px; + margin-right: 4px; + } +} +.token-logo-row { + font-size: 20px; + line-height: 24px; + .token-logo { + width: 24px; + height: 24px; + margin-right: 8px; + } +} + +.action-btn { + margin: auto; + width: 240px; + height: 48px; + font-size: 16px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; +} + +.action-btn-loading { + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: center; + gap: 8px; + &[disabled] { + cursor: not-allowed; + } +} + +@media screen and (max-width: @md-width) { + .action-btn { + width: 100%; + } +} +.share-col { + margin: 24px 0; + padding: 6px 24px 24px; + background: @bg-15; + border-radius: 12px; + .share-row { + margin-top: 16px; + div { + .font-family-medium(); + + line-height: 22px; + &:first-child { + color: @font-13; + } + &:last-child { + color: @font-15; + } + } + } +} + +.operate-pool { + margin-top: 40px; +} diff --git a/src/page-components/Pools/PoolList/columns.tsx b/src/page-components/Pools/PoolList/columns.tsx new file mode 100644 index 00000000..66ce2bd9 --- /dev/null +++ b/src/page-components/Pools/PoolList/columns.tsx @@ -0,0 +1,74 @@ +import { Row } from 'antd'; +import type { ColumnType } from 'antd/lib/table'; +import { Trans } from 'react-i18next'; +import { APIPoolItem } from 'types/api'; +import { getChainIdByAPI, getChainName, getIconByChainId } from 'utils/chain'; +import { formatSymbolAndNativeToken } from 'utils/token'; +import styles from './styles.module.less'; +import TokenLogo from 'components/TokenLogo'; +import IconFont from 'components/IconFont'; +import { showUSDConverter } from 'utils/converter'; + +const token: ColumnType = { + title: () => Token, + width: 150, + key: 'Token', + ellipsis: true, + dataIndex: 'token', + render: (token, item) => { + const chainId = getChainIdByAPI(item.chainId); + return ( + + +
{formatSymbolAndNativeToken(token?.symbol)}
+
+ ); + }, +}; + +const network: ColumnType = { + width: 300, + title: () => Network, + key: 'Network', + ellipsis: true, + dataIndex: 'token', + render: (_, item) => { + const iconProps = getIconByChainId(getChainIdByAPI(item.chainId)); + return ( + + +
{getChainName(getChainIdByAPI(item.chainId))}
+
+ ); + }, +}; + +const yourLiquidity: ColumnType = { + title: () => Your Liquidity, + key: 'yourLiquidity', + ellipsis: true, + dataIndex: 'myTvlInUsd', + render: (myTvlInUsd) => { + return showUSDConverter(myTvlInUsd); + }, +}; + +const totalLiquidity: ColumnType = { + align: 'right', + title: () => Total Liquidity, + key: 'totalLiquidity', + ellipsis: true, + dataIndex: 'totalTvlInUsd', + render: (totalTvlInUsd) => { + return showUSDConverter(totalTvlInUsd); + }, +}; + +const columns = { + token, + network, + yourLiquidity, + totalLiquidity, +}; + +export default columns; diff --git a/src/page-components/Pools/PoolList/index.tsx b/src/page-components/Pools/PoolList/index.tsx new file mode 100644 index 00000000..0cefef71 --- /dev/null +++ b/src/page-components/Pools/PoolList/index.tsx @@ -0,0 +1,62 @@ +import CommonTable from 'components/CommonTable'; +import styles from './styles.module.less'; +import Columns from './columns'; +import { ColumnsType } from 'antd/lib/table'; +import { APIPoolItem } from 'types/api'; +import { useRouter } from 'next/router'; +import { usePoolList } from 'hooks/pools'; +import { useMemo, useState } from 'react'; +import { ZERO } from 'constants/misc'; +import { formatSymbolAndNativeToken } from 'utils/token'; +import { Checkbox, Row } from 'antd'; +import { useLanguage } from 'i18n'; +import clsx from 'clsx'; +import { useActiveAddresses } from 'hooks/web3'; +const columns: ColumnsType = [Columns.token, Columns.network, Columns.yourLiquidity, Columns.totalLiquidity]; + +const Pagination = { hideOnSinglePage: true, pageSize: 100 }; + +export default function PoolList() { + const { t } = useLanguage(); + const { push } = useRouter(); + const [checked, setChecked] = useState(false); + const { poolList } = usePoolList(); + const activeAddresses = useActiveAddresses(); + const sortList = useMemo( + () => + poolList?.items.filter((i) => { + if (checked) return ZERO.lt(i.myTvlInUsd ?? 0); + return true; + }), + [checked, poolList?.items], + ); + return ( + <> + + { + setChecked(e.target.checked); + }} + /> +
{t('Only show my positions')}
+
+ item?.chainId + item?.token?.symbol} + className={clsx(styles.table, Array.isArray(sortList) && sortList.length > 0 ? null : styles['table-empty'])} + columns={columns} + dataSource={sortList} + scroll={{ x: 700 }} + emptyText={t('You currently don’t have any positions in pools.')} + onRow={(record: APIPoolItem) => { + return { + onClick: () => push(`pool/${record.chainId}/${formatSymbolAndNativeToken(record.token?.symbol)}`), + }; + }} + /> + + ); +} diff --git a/src/page-components/Pools/PoolList/styles.module.less b/src/page-components/Pools/PoolList/styles.module.less new file mode 100644 index 00000000..c2c73dee --- /dev/null +++ b/src/page-components/Pools/PoolList/styles.module.less @@ -0,0 +1,88 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + +.token-logo { + height: 20px !important; + width: 20px !important; + margin-right: 4px; +} + +.network-icon { + height: 20px !important; + width: 20px !important; + margin-right: 4px; + font-size: 20px; +} +.check-box-row { + font-size: 14px; + line-height: 22px; + color: @font-13; + .check-box { + margin-right: 8px; + :global { + .bridge-checkbox { + .bridge-checkbox-inner { + width: 16px; + height: 16px; + border-radius: 4px; + &::after { + left: 22%; + } + } + } + } + } +} + +.table { + margin-top: 24px; + :global { + .@{app-prefix}-table-cell-ellipsis { + white-space: normal; + word-break: normal; + } + .@{app-prefix}-table-thead > tr > th { + padding: 13px 12px; + background: @bg-19; + border-bottom-width: 0; + color: @font-15; + line-height: 22px; + .font-family-medium(); + .text-overflow(1); + + white-space: nowrap; + } + .@{app-prefix}-table-container table > thead > tr:first-child th:last-child { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + } + .@{app-prefix}-table-container table > thead > tr:first-child th:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + } + .@{app-prefix}-table-tbody > tr > td { + padding: 18px 12px; + color: @font-15; + .font-family-medium(); + } + .@{app-prefix}-table-thead > tr > th, + .@{app-prefix}-table-tbody > tr > td, + .@{app-prefix}-table tfoot > tr > th, + .@{app-prefix}-table tfoot > tr > td { + font-size: 14px; + line-height: 20px; + &::before { + display: none; + } + } + } +} + +.table-empty { + :global { + .@{app-prefix}-table-tbody > tr > td { + border-bottom: none; + } + } +} diff --git a/src/page-components/Pools/PoolOverviewCard/index.tsx b/src/page-components/Pools/PoolOverviewCard/index.tsx new file mode 100644 index 00000000..e8c30617 --- /dev/null +++ b/src/page-components/Pools/PoolOverviewCard/index.tsx @@ -0,0 +1,27 @@ +import CommonImage from 'components/CommonImage'; +import styles from '../styles.module.less'; +import { questionFilledIcon } from 'assets/images'; +import clsx from 'clsx'; +import { useLanguage } from 'i18n'; +import CommonTooltipSwitchModal from 'components/CommonTooltipSwitchModal'; + +export type TPoolOverviewCardProps = { + title: string; + tooltipTitle: string; + data?: string; +}; + +export default function PoolOverviewCard({ title, tooltipTitle, data }: TPoolOverviewCardProps) { + const { t } = useLanguage(); + return ( +
+
+ {t(title)} + + + +
+
{data ? t(data) : ''}
+
+ ); +} diff --git a/src/page-components/Pools/index.tsx b/src/page-components/Pools/index.tsx new file mode 100644 index 00000000..1b8bdba6 --- /dev/null +++ b/src/page-components/Pools/index.tsx @@ -0,0 +1,71 @@ +import { memo, useMemo } from 'react'; +import styles from './styles.module.less'; +import { Col, Row } from 'antd'; +import clsx from 'clsx'; +import PoolOverviewCard, { TPoolOverviewCardProps } from './PoolOverviewCard'; +import PoolList from './PoolList'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { usePoolOverview } from 'hooks/pools'; +import { TPoolOverview } from 'types/api'; +import { showUSDConverter, unitConverter } from 'utils/converter'; + +const OverviewCardList: (TPoolOverviewCardProps & { + key: keyof TPoolOverview; +})[] = [ + { + title: 'Total TVL', + tooltipTitle: 'The total value of all assets locked in liquidity pools across eBridge.', + key: 'totalTvlInUsd', + }, + { + title: 'Your Total Liquidity', + tooltipTitle: 'The total value of the liquidity you have added to all pools on eBridge.', + key: 'myTotalTvlInUsd', + }, + { title: 'Pools', tooltipTitle: 'The number of liquidity pools currently active on eBridge.', key: 'poolCount' }, + { + title: 'Tokens', + tooltipTitle: 'The number of different tokens available across all liquidity pools on eBridge.', + key: 'tokenCount', + }, +]; + +const Pools = () => { + const isMd = useMediaQueries('md'); + const { poolOverview } = usePoolOverview(); + const overviewCardListMemo = useMemo(() => { + return OverviewCardList.map((i) => { + const isUSD = i.key.toUpperCase().includes('USD'); + return { + ...i, + data: `${ + isUSD + ? showUSDConverter({ num: poolOverview?.[i.key], defaultVal: '$0.00' }) + : unitConverter(poolOverview?.[i.key]) + }`, + }; + }); + }, [poolOverview]); + + return ( +
+
+ + {overviewCardListMemo.map((i, key) => { + return ( + + + + ); + })} + + + +
+ +
+ + ); +}; + +export default memo(Pools); diff --git a/src/page-components/Pools/styles.module.less b/src/page-components/Pools/styles.module.less new file mode 100644 index 00000000..8e4b2264 --- /dev/null +++ b/src/page-components/Pools/styles.module.less @@ -0,0 +1,72 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; +.tooltip-content { + .tooltip-title { + color: @font-15; + } + ol { + margin-left: 18px; + margin-bottom: 0; + list-style-type: decimal; + } +} + +.pools-page { + min-width: 0; + width: 100%; +} + +.pool-page-component-wrap { + max-width: 893px; + width: auto; + margin: 0 auto 24px; + padding: 32px; + background-color: @bg-2; + border-radius: 16px; + border: 1px solid @border-5; +} + +.page-overview-row { + background: transparent; + border-width: 0; + padding: 0; +} + +.page-overview-card { + padding: 24px 24px 24px 32px; + background-color: @bg-2; + border-radius: 16px; + border: 1px solid @border-5; + font-size: 14px; + .page-overview-data { + margin-top: 12px; + color: @font-15; + font-size: 20px; + font-weight: 600; + line-height: 28px; + } + .page-overview-title-row { + white-space: nowrap; + color: @font-13; + } +} +.question-icon { + width: 16px; + height: 16px; + margin-left: 4px; +} + +@media screen and (max-width: @md-width) { + .pool-page-component-wrap { + margin: 0; + padding: 24px 12px; + } + .page-overview-card { + padding: 16px 12px; + margin: 0; + } + .page-overview-row { + margin-bottom: 16px; + } +} diff --git a/src/page-components/PrivacyPolicy/index.tsx b/src/page-components/PrivacyPolicy/index.tsx new file mode 100644 index 00000000..b499123e --- /dev/null +++ b/src/page-components/PrivacyPolicy/index.tsx @@ -0,0 +1,182 @@ +import { memo } from 'react'; +import { Row, Col } from 'antd'; + +import styles from './styles.module.less'; + +function PrivacyPolicy() { + return ( +
+
Privacy Policy
+
Last updated: August 2024
+ +
+ + + {`This privacy policy ("`} + {`Privacy Policy`} + {`") elucidates the procedures by which the eBridge Team ("eBridge", "our", "we", or "us") gathers, employs, and divulges information about you. It is applicable when you use or access `} + + https://ebridge.exchange/ + + {` and/or `} + + https://test.ebridge.exchange/ + + {` (the "`} + {`Site`} + {`"), interact with our customer service team, engage with us on social media platforms, or otherwise communicate with us.`} + + + {`We reserve the right to modify this Privacy Policy periodically. Should alterations occur, we will notify you by amending the date at the top of this Privacy Policy. Additionally, in certain instances, we may provide further notification, such as appending a statement to our Site or dispatching a notification to you. We encourage you to regularly review this Privacy Policy to remain informed about our information procedures and the choices at your disposal.`} + + + {`Your use of the Site is at all times subject to the Terms of Service, which incorporates this Privacy Policy.`} + + + + + + 1. Acceptance of the Privacy Policy + + {`By accessing and using the Site, you acknowledge and agree to the terms outlined in this Privacy Policy. Where required by applicable law, your explicit consent will be sought for the collection and use of your personal information as detailed herein.`} + + + {`From time to time, additional "just-in-time" disclosures or specific information regarding the data processing practices of particular services may be provided. These disclosures are intended to supplement or clarify the privacy practices described in this Privacy Policy and may offer you additional choices regarding how your personal data is processed.`} + + + {`If you do not agree with any part of this Privacy Policy or any of its terms herein, you should immediately cease accessing and using the Site. Access to the Site is restricted to individuals who are of legal age of majority in their jurisdiction of residence.`} + + + + + + 2. What does this Privacy Policy cover? + + {`This Privacy Policy sets forth our policy for collecting or using personal or behavioral data in connection with users access and using of the Site.`} + + + + + + 3. Collection of Information + {`(a) Non-Collection of Personal Information`} + + {`eBridge does not collect personal information or utilize automatic tracking technologies. The eBridge application operates using blockchain technologies that access only publicly available information on the blockchain. Users are not required to provide any personal information to access or use the Site. However, please note that transactions conducted from your wallets are publicly accessible on blockchain networks accessed through the Site.`} + + {`(b) IP Address Handling`} + + {`eBridge does not track or collect IP addresses. In instances where third parties may collect IP addresses by default, eBridge shall: (1) request the manual removal of IP tracking, and (2) ensure the anonymization of IP data to prevent product analytics services from receiving identifiable IP data.`} + + {`(c) Google Analytics Usage`} + + {`eBridge uses Google Analytics solely for purposes of monitoring action on the Site. All IP addresses processed through Google Analytics are anonymized to protect user privacy.`} + + {`(d) Data Storage and Usage`} + + {`eBridge does not store personal or message information and does not use information in any way that could associate or cross-associate wallet data.`} + + {`(e) Do Not Track Signals`} + + {`Some Internet browsers allow users to transmit "Do Not Track" or "DNT" signals. As uniform standards for "DNT" signals have not been adopted, the Site does not currently process or respond to "DNT" signals.`} + + {`(f) Security of Private Keys`} + + {`eBridge will never collect your seed phrase or private keys nor ask you to share your wallet private keys or seed phrase. Always exercise caution and never trust any entity or site that requests you to enter your private keys or similar security information.`} + + {`(g) Use of Blockchain Information`} + + {`eBridge may collect publicly available blockchain information relevant to your transactions for the purpose of utilizing such information in, or providing such information to third-parties building or maintaining, analytics pages and block explorers. Such information will always be anonymous and never tied to any personal information, and will only be maintained for as long as necessary for the stated purpose.`} + + + + + + 4. Sharing of Personal Information + + {`We may from time to time disclose personal information under the following circumstances or as otherwise outlined in this Privacy Policy.`} + + + {`Personal information is shared with vendors, service providers, and consultants who require access to such information to perform services for us. This includes entities assisting with web hosting, event organising, payment processing, fraud prevention, customer service, and marketing and advertising.`} + + + {`Personal information may be disclosed if we believe such action is consistent with, or required by, any applicable law or legal process, including lawful requests by public authorities to meet national security or law enforcement requirements.`} + + + {`We may share personal information if we believe your actions are inconsistent with our user agreements or policies, if we suspect you have violated the law, or if we deem it necessary to protect the rights, property, and safety of eBridge, our users, the public, or others.`} + + + {`Personal information is shared with our legal and professional advisors as needed to obtain advice or to safeguard and manage our business interests.`} + + + {`Personal information may be shared in connection with, or during negotiations related to, any merger, sale of company assets, financing, or acquisition of all or part of our business by another company.`} + + + {`Personal information is shared with your consent or at your direction.`} + + + {`Additionally, we share aggregated or de-identified information that cannot reasonably identify you.`} + + + + + + 5. Security of your Personal Information + + {`The security of your personal information is a shared responsibility. While we implement reasonable measures to protect your data, unauthorized access, hardware or software failures, and other unforeseen events may compromise the security of your information. Your wallet is safeguarded by your password, private key, and/or seed phrase. We strongly recommend that you maintain the confidentiality of these credentials and avoid leaving your wallet accessible in an unsecured manner.`} + + + {`Our approach to protecting personal information includes minimizing the collection of personal data wherever possible. However, please be aware that no security measures are foolproof. Despite our efforts, we cannot guarantee that your personal information will not be accessed, disclosed, altered, or destroyed as a result of a breach of our security protocols.`} + + + {`By using the Site, you acknowledge and accept that there are inherent risks associated with the transmission and storage of personal information, and you agree to assume these risks.`} + + + + + + 6. Transfer of Information to other Jurisdictions + + {`eBridge may operate and engage service providers in various jurisdictions. Therefore, we and our service providers may transfer your personal information to, or store or access it in, jurisdictions that may not provide levels of data protection that are equivalent to those of your home jurisdiction. By using our Site, you acknowledge and agree to such transfers and processing, including to and in the United States. We will take steps to ensure that your personal information receives an adequate level of protection in the jurisdictions in which we process it.`} + + + {`If you are a resident of the European Economic Area ("EEA") or Switzerland, you may be entitled to additional rights concerning your Personal Data under the General Data Protection Regulation ("GDPR") and other applicable laws, as described below.`} + + + {`For the purposes of this section, the terms "Personal Data" and "processing" are used as defined under the GDPR. "Personal Data" generally refers to any information that can be used to identify an individual, while "processing" encompasses a wide range of actions related to data, including but not limited to collection, use, storage, and disclosure.`} + + + + + + 7. Use of Social and Developer Networks + + {`We may utilize social and developer networks, such as Twitter and Telegram, to engage with our community, provide updates, and respond to user inquiries. Please be aware that when you interact with us on these platforms, the operators of the respective networks may collect and process your personal data in accordance with their own privacy policies. The responsibility for this processing lies with the respective social and developer networks, and we encourage you to review their privacy policies to understand how your data may be handled. eBridge is not responsible for the collection or processing of data by these networks.`} + + + + + + 8. Changes to Privacy Policy + + {`If we change our Privacy Policy, we will post those changes on the Site so that users are always aware of what information we collect, how we use it, and under what circumstances, if any, we will disclose it. These changes will be effective immediately. In any event, changes to this Privacy Policy may affect our use of Personal Information that you provided us prior to our notification to you of the changes. Please check this Privacy Policy periodically to inform yourself of any changes. If you do not accept the changes made to this Privacy Policy, you should immediately stop using the Site and our products and services.`} + + + + + + 9. Contact Us + + {`If you have any questions about this Privacy Policy, please contact us at `} + + info@ebridge.exchange + + {`.`} + + + + + + ); +} + +export default memo(PrivacyPolicy); diff --git a/src/page-components/PrivacyPolicy/styles.module.less b/src/page-components/PrivacyPolicy/styles.module.less new file mode 100644 index 00000000..3c502af0 --- /dev/null +++ b/src/page-components/PrivacyPolicy/styles.module.less @@ -0,0 +1,52 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.privacy-policy { + margin: 64px auto 250px; + max-width: calc(@max-width + 82px); + padding: 20px 32px; + background-color: @bg-2; + border-radius: 20px; + .title { + font-size: 24px; + font-weight: 500; + line-height: 32px; + color: @font-4; + } + .sub-title { + font-size: 12px; + color: @font-3; + margin: 20px 0; + } + .content { + color: @font-5; + line-height: 22px; + font-size: 14px; + } + .list-item { + display: list-item; + margin-left: 20px; + } + .h1-title { + flex: 0 0 100%; + font-size: 16px; + line-height: 24px; + margin-bottom: 4px; + .font-family-medium(); + } + a { + color: @font-12; + word-wrap: break-word; + } +} + +@media screen and (max-width: @md-width) { + .privacy-policy { + padding: 20px 16px; + margin-left: 16px; + margin-right: 16px; + margin-bottom: 193px; + max-width: 100%; + } +} diff --git a/src/page-components/TermsOfService/index.tsx b/src/page-components/TermsOfService/index.tsx new file mode 100644 index 00000000..bc1c5bd9 --- /dev/null +++ b/src/page-components/TermsOfService/index.tsx @@ -0,0 +1,262 @@ +import { memo } from 'react'; +import { Row, Col } from 'antd'; + +import styles from './styles.module.less'; + +function TermsOfService() { + return ( +
+
Terms of Service
+
Last updated: September 2024
+ +
+ + + {`Please read these terms and conditions that follow ("Terms") carefully as they form a contract between you and the eBridge Team ("eBridge ", "we", "our", or "us"). These Terms govern your access and use of the website-hosted interface provided by eBridge (`} + + https://ebridge.exchange/ + + {`, the "Site").The Site provides users with an informational interface relating to a decentralized protocol on the eBridge application that allows users to bridge, stake, and pool certain digital assets (the "Protocol"), subject to the latest features and functions published on the site. The Site is one, but not the exclusive, means of constructing data inputs for purposes of accessing the Protocol.`} + + + {`Please note that by accessing, browsing or otherwise using the Site, or by acknowledging agreement to the Terms and the eBridge Privacy Policy (`} + + https://ebridge.exchange/privacy-policy + + {`, you are accepting and agreeing to these Terms and the policies and guidelines referenced in these Terms. If you do not agree to these Terms, you are not authorized to access or use the Site and should not use the Site.`} + + {`PLEASE NOTE: THE "GOVERNING LAW AND DISPUTE RESOLUTION" SECTION OF THESE TERMS CONTAINS AN ARBITRATION CLAUSE THAT REQUIRES DISPUTES TO BE ARBITRATED ON AN INDIVIDUAL BASIS, AND PROHIBITS CLASS ACTION CLAIMS. IT AFFECTS HOW DISPUTES BETWEEN YOU AND THE eBridge ARE RESOLVED. BY ACCEPTING THESE TERMS, YOU AGREE TO BE BOUND BY THIS ARBITRATION PROVISION. PLEASE READ IT CAREFULLY.`} + + + + + 1. Modification of these Terms + + {`eBridge reserves the right, in its sole discretion, to modify these Terms from time to time. If any modifications are made, we will notify you of amendments to these Terms by either: (a) posting the revised terms on our Site; (b) sending you an email notification to the email address that you provided to us as part of your account registration, or a notification via SMS or other messaging service; or (c) presenting the revised Terms to you when you access the Site. It is your responsibility to provide and update your external email address, check for such notices, and make sure our notices have not been trapped by your spam filter. It is your responsibility to periodically revisit these Terms as posted on our Site. Such updated Terms will become effective the earlier of: (a) when you accept it online or offline, (b) after we post or email the update, in which case your continued use of the Site will indicate your acceptance of the amendment.`} + + + + + + 2. Eligibility + + {`To access or use the Site, you must be legally capable of entering into a binding contract with us. By using the Site, you represent and warrant that you are at least the age of majority in your jurisdiction and possess the full legal right, authority, and capacity to enter into and abide by the terms and conditions of these Terms of Use on behalf of yourself and any entity you represent.`} + + + {`You further represent and warrant that your access to and use of the Site will be in full compliance with all applicable laws, regulations, and standards, and that you will not use the Site to conduct, promote, or facilitate any unlawful activities.`} + + + {`While the Site does not hold or manage any user assets, and therefore cannot "block" any interests in property, eBridge reserves the right, at its sole and absolute discretion, to notify relevant authorities or identifiable parties in the event of any breach of these representations and/or obligations, to enable the blocking or freezing of interests in property as required under applicable laws and regulations.`} + + + + + + 3. Proprietary Rights + + {`All intellectual property rights and proprietary interests in and to the Site, including but not limited to the software, text, images, trademarks, service marks, copyrights, patents, and designs, are owned by eBridge and its affiliated entities. The Protocol is composed of source-available software running on public distributed blockchains. Your use of the Site does not grant you any rights, title, or interest in or to any intellectual property or proprietary rights contained on the Site, except for the limited rights of use expressly granted under these Terms.`} + + + + + + 4. Additional Rights + + {`eBridge reserves the following rights, which may be exercised at its sole discretion, with or without notice to you: (a) To modify, substitute, eliminate, or add to the Site or any part thereof; (b) To review, modify, filter, disable, delete, and remove any content or information submitted or uploaded to the Site by you or any other user; (c) To cooperate with any law enforcement, judicial, or governmental investigation, order, or third-party request, including disclosing any information or content provided by you, as required by applicable law or as we, in our sole discretion, deem necessary or appropriate.`} + + + {`By accessing and using the Site, you acknowledge and agree that eBridge shall not be liable for any modification, suspension, or discontinuance of the Site or any part thereof, nor for the removal of any content or information you provide.`} + + + + + + 5. Privacy + + {`When you access or use the Site, the only information we collect from you is your blockchain wallet address and your transaction data. We do not collect any personally identifiable information, such as your name or other identifiers that can be directly linked to you. For further details on our data practices, please refer to our Privacy Policy.`} + + + {`When you utilize any data inputs provided by the Site to execute transactions, you are interacting with public blockchains, which inherently provide transparency into your transactions. eBridge does not control, and is not responsible for, any information you choose to make public on any blockchain by engaging in activities using the data provided by the Site.`} + + + {`To promote the safety, security, and integrity of the Site, eBridge may share the information it collects with blockchain analytics providers. Such information will only be retained for as long as necessary to fulfill these purposes and will be handled in accordance with our Privacy Policy.`} + + + + + + 6. Prohibited Activity + + {`By accessing and using the Site, you agree not to engage in, or attempt to engage in, any of the following prohibited activities:`} + + + {`Intellectual Property Infringement: `} + {`Any activity that infringes upon or violates any copyright, trademark, service mark, patent, right of publicity, right of privacy, or other proprietary or intellectual property rights recognized under applicable law.`} + + + {`Cyberattacks: `} + {`Any activity intended to interfere with or compromise the integrity, security, or proper functioning of any computer system, server, network, personal device, or other information technology systems, including but not limited to the deployment of viruses, malware, or denial-of-service attacks.`} + + + {`Fraud and Misrepresentation: `} + {`Any activity designed to defraud eBridge or any other person or entity, including but not limited to providing false, inaccurate, or misleading information to unlawfully obtain the property of another, impersonating any person or entity, or misrepresenting your affiliation with any person or entity.`} + + + {`Circumvention of Security/Compliance Measures: `} + {`The use of any techniques, including but not limited to IP address masking, proxy IP addresses, or virtual private networks, to bypass or circumvent any security or compliance measures implemented by eBridge with respect to Site access.`} + + + {`Violation of Law: `} + {`Any activity that violates any applicable federal, state, local, national, or international law, or any regulations with the force of law, including but not limited to laws governing the integrity of trading markets (e.g., spoofing, wash trading) or the trading of securities or derivatives, and any activity that furthers or promotes criminal conduct or provides instructional information about illegal activities.`} + + + + + + 7. Not Registered with FinCEN or Any Regulatory Agency + + {`eBridge is not registered with the Financial Crimes Enforcement Network ("FinCEN") as a money services business or in any other capacity, nor is it registered with any other regulatory body in any jurisdiction. You acknowledge and agree that eBridge does not broker trading orders on your behalf, match orders between buyers and sellers of securities or facilitate the execution or settlement of transactions. All transactions conducted through the Site occur entirely on public distributed blockchains. The Site serves solely as a platform for users to construct transaction data, which they may then utilize by executing utilizing third-party blockchain wallet applications.`} + + + + + + 8. Non-Solicitation; No Investment Advice; No Fiduciary Duties + + {`You acknowledge and agree that all transfers, pooling, staking or other actions you undertake using transaction data provided by the Site are conducted on an unsolicited basis. This means that you have not received any investment advice or recommendations from us in connection with any such actions, and we do not conduct any suitability review regarding your use of the Site.`} + + + {`All information provided by the Site is for informational purposes only and should not be construed as investment advice. You should not take or refrain from taking any action based on the information available on the Site. We do not provide investment recommendations, nor do we express opinions on the merits of any specific investment transaction or opportunity. You are solely responsible for determining whether any investment, investment strategy, or related transaction is suitable for you, based on your personal investment objectives, financial circumstances, and risk tolerance.`} + + + + + + 9. No Warranties + + {`THE SITE IS PROVIDED ON AN "AS-IS" AND "AS-AVAILABLE" BASIS. TO THE FULLEST EXTENT PERMITTED BY LAW, eBridge DISCLAIMS ANY AND ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT. eBridge WILL NOT BE LIABLE FOR ANY DAMAGES OF ANY KIND ARISING FROM YOUR USE OF THE SITE, INCLUDING BUT NOT LIMITED TO ANY DIRECT, INDIRECT, INCIDENTAL, PUNITIVE, EXEMPLARY, SPECIAL, OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU AGREE THAT YOUR USE OF THE SITE IS AT YOUR SOLE RISK.`} + + + {`eBridge IS NOT RESPONSIBLE FOR ANY DAMAGES OR LOSSES THAT MAY ARISE FROM YOUR USE OF THE SITE, INCLUDING BUT NOT LIMITED TO DAMAGES OR LOSSES RESULTING FROM YOUR USE OR INABILITY TO USE THE SITE; ANY CHANGES TO OR TERMINATION OF THE SITE; ANY DELAY, FAILURE, UNAUTHORIZED ACCESS TO, OR ALTERATION OF ANY TRANSMISSION OR DATA; ANY TRANSACTION OR AGREEMENT ENTERED INTO THROUGH THE SITE; ANY ACTIVITIES OR COMMUNICATIONS OF THIRD PARTIES; OR ANY DATA OR MATERIAL ACCESSED THROUGH THE SITE. WE MAKE NO WARRANTIES OR REPRESENTATIONS REGARDING THE ACCURACY OR COMPLETENESS OF THE SITE'S CONTENT OR THE CONTENT OF ANY WEBSITES LINKED TO THE SITE.`} + + + {`eBridge ASSUMES NO LIABILITY OR RESPONSIBILITY FOR ANY OF THE FOLLOWING: (1) ERRORS, MISTAKES, OR INACCURACIES OF CONTENT AND MATERIALS; (2) PERSONAL INJURY OR PROPERTY DAMAGE, OF ANY KIND, RESULTING FROM YOUR ACCESS TO AND USE OF THE SITE; (3) UNAUTHORIZED ACCESS TO OR USE OF OUR SECURE SERVERS AND/OR ANY PERSONAL OR FINANCIAL INFORMATION STORED THEREIN; (4) INTERRUPTION OR CESSATION OF TRANSMISSION TO OR FROM THE SITE; (5) BUGS, VIRUSES, TROJAN HORSES, OR OTHER MALICIOUS CODE TRANSMITTED THROUGH THE SITE BY ANY THIRD PARTY; OR (6) ERRORS OR OMISSIONS IN ANY CONTENT AND MATERIALS, OR FOR ANY LOSS OR DAMAGE INCURRED AS A RESULT OF THE USE OF ANY CONTENT POSTED, TRANSMITTED, OR OTHERWISE MADE AVAILABLE VIA THE SITE. IF YOU ARE DISSATISFIED WITH THE SITE, YOUR SOLE AND EXCLUSIVE REMEDY IS TO DISCONTINUE USE OF THE SITE. CERTAIN JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE LIMITATIONS AND EXCLUSIONS MAY NOT APPLY TO YOU.`} + + + + + + 10. Non-Custodial and No Fiduciary Duties + + {`The Site functions as a purely non-custodial interface, meaning you retain sole responsibility for the custody and security of the cryptographic private keys associated with your digital asset wallets. These Terms are not intended to, and do not, create or impose any fiduciary duties on us. To the fullest extent permitted by law, you acknowledge and agree that we owe no fiduciary duties or liabilities to you or any other party. Any such duties or liabilities that may exist under law or in equity are hereby irrevocably disclaimed, waived, and eliminated. You further agree that our only duties and obligations to you are those expressly set forth in these Terms.`} + + + + + + 11. Compliance Obligations + + {`By accessing or using the Site, you agree that you are solely and entirely responsible for compliance with all applicable laws and regulations.`} + + + + + + 12. Assumption of Risk + + {`By accessing and using the Site, you represent that you possess the necessary financial and technical expertise to understand the inherent risks associated with cryptographic and blockchain-based systems. You acknowledge that blockchain-based transactions are irreversible.`} + + + {`You further acknowledge that digital asset markets are highly volatile, influenced by factors such as adoption, speculation, technology, security, and regulation. You accept that the cost and speed of transactions using blockchain-based systems can fluctuate and may increase significantly at any time. You also acknowledge the risk that your digital assets may lose some or all of their value when interacting with the Protocol through the Site, that you may experience losses due to price fluctuations in trading pairs or liquidity pools, and that in advanced modes, you may encounter significant price slippage and costs. You understand that anyone can create tokens, including fraudulent versions of existing tokens and tokens that falsely claim to represent projects, and you accept the risk of inadvertently trading such tokens. You acknowledge that we do not own or control the Protocol and cannot be held liable for any losses you may incur while using the Site. Accordingly, you assume full responsibility for all risks associated with accessing and using the Site.`} + + + + + + 13. Third-Party Resources and Promotions + + {`The Site may include references or links to third-party resources, including but not limited to information, materials, products, or services that we do not own or control. Additionally, third parties may offer promotions related to your access and use of the Site. We do not endorse or assume any responsibility for any such third-party resources or promotions. If you choose to access or use such resources or participate in promotions, you do so at your own risk, and these Terms do not apply to your dealings with third parties. You expressly release us from any liability arising from your use of any such resources or participation in promotions.`} + + + + + + 14. Release of Claims + + {`You expressly assume all risks in connection with your access and use of the Site and your interaction with the Protocol. You further expressly waive and release us from any liability, claims, causes of action, or damages arising from or in any way relating to your use of the Site and your interaction with the Protocol. If you are a California resident, you waive the benefits and protections of California Civil Code § 1542, which provides: "[a] general release does not extend to claims that the creditor or releasing party does not know or suspect to exist in his or her favor at the time of executing the release and that, if known by him or her, would have materially affected his or her settlement with the debtor or released party."`} + + + + + + 15. Indemnity + + {`You agree to indemnify, defend, and hold harmless eBridge, its officers, directors, employees, contractors, agents, affiliates, and subsidiaries from and against any claims, damages, obligations, losses, liabilities, costs, and expenses, including legal fees, arising from: (a) your access and use of the Site; (b) your violation of any term or condition of these Terms, any rights of a third party, or any applicable law, rule, or regulation; and (c) any third party's access and use of the Site with your assistance or through any device or account that you own or control.`} + + + + + + 16. Limitation of Liability + + {`Under no circumstances shall eBridge or any of its officers, directors, employees, contractors, agents, affiliates, or subsidiaries be liable to you for any indirect, punitive, incidental, special, consequential, or exemplary damages, including but not limited to damages for loss of profits, goodwill, use, data, or other intangible property, arising out of or relating to your access or use of the Site, nor will we be liable for any damage, loss, or injury resulting from hacking, tampering, or other unauthorized access or use of the Site or its information. We assume no liability for any: (a) errors or inaccuracies of content; (b) personal injury or property damage resulting from your use of the Site; (c) unauthorized access to or use of any secure server or database in our control, or the use of any information stored therein; (d) interruption or cessation of Site functionality; (e) bugs, viruses, trojan horses, or similar malicious code transmitted through the Site; (f) errors or omissions in any content, or any loss or damage incurred as a result of the use of any content made available through the Site; or (g) defamatory, offensive, or illegal conduct of any third party. In no event shall our liability exceed the amount you paid to us in exchange for access to and use of the Site, or US$100.00, whichever is greater. This limitation of liability applies regardless of the legal basis of your claim, whether in contract, tort, negligence, strict liability, or otherwise, and even if we have been advised of the possibility of such liability. Some jurisdictions do not allow the exclusion or limitation of certain warranties or liabilities, so some of the disclaimers and limitations in these Terms may not apply to you. This limitation of liability applies to the fullest extent permitted by law.`} + + + + + + 17. Governing Law and Dispute Resolution + + {`Governing Law: `} + {`These Terms, and any disputes arising out of or in connection to them, shall be governed by and construed in accordance with the laws of the British Virgin Islands, without regard to its conflict of law principles.`} + + + {`Pre-Arbitration Procedures: `} + {`You and eBridge mutually agree to attempt to resolve all disputes using good-faith and strive for a prompt, low-cost, and mutually beneficial outcome. Therefore, a party who intends to seek arbitration must first send to the other party a written Notice of Dispute (a “`} + {`Dispute Notice`} + {`”). Any Dispute Notice to aelf must be sent to legal@aelf.io, and any Dispute Notice we send to you will be sent to the email address registered with your account or otherwise associated with your use of the Site and agreement to these Terms. Dispute Notices must include each of the following: (a) the contact details of the party providing the Dispute Notice; (b) a description of the nature and basis of the claim or dispute; (c) an explanation of the specific relief sought, including the total damages sought, if any, and the basis for the damage calculations; (d) a signed statement from the party providing the Dispute Notice verifying the accuracy of its contents; and (e) if the dispute is from you and you have retained an attorney, then a signed statement from you authorising aelf to disclose your account details to your attorney if necessary in resolving your claim or dispute. If an agreement is not reached to resolve a claim within 60 days after a notice of demand is received, then either party may commence an arbitration proceeding; except that, if either you or aelf send the other an incomplete Dispute Notice, the 60-day period begins tolling only after a complete Dispute Notice is received. Additionally, if either you or aelf request a telephone discussion, the 60-day period begins tolling only after the discussion has occurred. The statute of limitations and any filing fee deadlines will be tolled while the parties engage in required informal dispute resolution procedures.`} + + + {`If we are unable to reach an amicable resolution within the 60-day period, you agree that any dispute, claim, or controversy arising out of or relating to the Site, these Terms, or any other acts or omissions for which you may hold us liable, including but not limited to disputes regarding arbitrability ("Dispute"), shall be resolved by binding arbitration in accordance with the United Nation’s UNCITRAL Arbitration Rules (at `} + + https://uncitral.un.org/en/texts/arbitration/contractualtexts/arbitration + + {`), which are incorporated into these Terms of Use by reference.`} + + + {`Arbitration Procedures: `} + {`Should any dispute proceed to arbitration, you and eBridge expressly agree that an arbitrator properly appointed under the UNCITRAL Arbitration Rules may issue all appropriate declaratory and injunctive relief necessary to ensure the arbitration and final settlement of disputes (but only in favour of the individual party seeking relief and only to the extent necessary to provide relief warranted by that party’s individual claim). You and aelf agree to keep any arbitration strictly confidential. The properly appointed arbitrator will have the authority to order any remedies, legal or equitable, that a party could obtain from a court of competent jurisdiction in an individual case based on the claims asserted, and nothing more.`} + + + {`For clarity, you acknowledge that you are solely responsible for all interactions with other users in connection with the Services, and eBridge shall bear no liability or responsibility in this regard. eBridge reserves the right, but is not obligated, to intervene in disputes between you and any other user of the Site.`} + + + + + + 18. Class Action and Jury Trial Waiver + + {`You agree to bring any and all disputes against us in your individual capacity, and not as a plaintiff or class member in any purported class action, collective action, private attorney general action, or other representative proceeding. This waiver applies to class arbitration as well. Both you and eBridge expressly waive any right to a trial by jury in any disputes.`} + + + + + + 19. Entire Agreement + + {`These Terms, along with our Privacy Policy, constitute the entire agreement between you and us concerning your use of the Site and supersede all prior or contemporaneous communications, agreements, and understandings, whether written or oral, regarding the subject matter of these Terms.`} + + + {`The information provided on the Site is not intended for distribution to or use by any person or entity in any jurisdiction or country where such distribution or use would violate any law or regulation or subject us to any registration requirement within such jurisdiction or country. By accessing the Site from any location outside of the British Virgin Islands, you do so at your own risk and are solely responsible for compliance with local laws and regulations, if and to the extent they are applicable.`} + + + + + + ); +} + +export default memo(TermsOfService); diff --git a/src/page-components/TermsOfService/styles.module.less b/src/page-components/TermsOfService/styles.module.less new file mode 100644 index 00000000..51afe47c --- /dev/null +++ b/src/page-components/TermsOfService/styles.module.less @@ -0,0 +1,48 @@ +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; + +.terms-of-service { + margin: 64px auto 250px; + max-width: calc(@max-width + 82px); + padding: 20px 32px; + background-color: @bg-2; + border-radius: 20px; + .title { + font-size: 24px; + font-weight: 500; + line-height: 32px; + color: @font-4; + } + .sub-title { + font-size: 12px; + color: @font-3; + margin: 20px 0; + } + .content { + color: @font-5; + line-height: 22px; + font-size: 14px; + } + .h1-title { + flex: 0 0 100%; + font-size: 16px; + line-height: 24px; + margin-bottom: 4px; + .font-family-medium(); + } + a { + color: @font-12; + word-wrap: break-word; + } +} + +@media screen and (max-width: @md-width) { + .terms-of-service { + padding: 20px 16px; + margin-left: 16px; + margin-right: 16px; + margin-bottom: 193px; + max-width: 100%; + } +} diff --git a/src/page-components/Transactions/columns.tsx b/src/page-components/Transactions/columns.tsx new file mode 100644 index 00000000..a6b04451 --- /dev/null +++ b/src/page-components/Transactions/columns.tsx @@ -0,0 +1,150 @@ +import { Progress } from 'antd'; +import type { ColumnType } from 'antd/lib/table'; +import CommonLink from 'components/CommonLink'; +import CommonImage from 'components/CommonImage'; +import IconFont from 'components/IconFont'; +import { Trans } from 'react-i18next'; +import { useHover } from 'react-use'; +import { ChainId, TokenInfo } from 'types'; +import { CrossChainItem } from 'types/api'; +import { CrossChainStatus } from 'types/misc'; +import { base64ToHexStr, formatNativeToken, getExploreLink, isTonChain, shortenString } from 'utils'; +import { shortenAddressByAPI, getIconByChainId } from 'utils/chain'; +import { unitConverter } from 'utils/converter'; +import { formatTime } from 'utils/time'; +import { formatSymbol } from 'utils/token'; +import { arrowRightGrayIcon } from 'assets/images'; +import clsx from 'clsx'; +import styles from './styles.module.less'; + +function Info({ + isFrom = false, + address, + chainId, + transactionId, +}: { + isFrom?: boolean; + address?: string; + chainId: ChainId; + transactionId?: string; +}) { + const iconProps = getIconByChainId(chainId); + + const hexId = isTonChain(chainId) ? base64ToHexStr(transactionId) : transactionId; + + return ( +
+
+
+ + + {shortenAddressByAPI(address || '', chainId)} + +
+ {!!hexId && ( +
+ TXID: + + {shortenString(hexId, 6)} + +
+ )} +
+ {isFrom && } +
+ ); +} + +function Amount({ amount, token }: { amount?: number; token?: TokenInfo }) { + const { symbol, decimals } = token || {}; + const tmpSymbol = formatNativeToken(symbol); + return ( +
+ {unitConverter({ num: amount, minDecimals: decimals })} {formatSymbol(tmpSymbol)} +
+ ); +} + +function FromTo({ items }: { items: CrossChainItem }) { + const { progress, status, transferTime } = items; + const [hoverable] = useHover((hovered: boolean) => { + const success = status === CrossChainStatus.Received; + return ( +
+ {transferTime &&
{formatTime(transferTime)}
} +
+ {hovered ? ( +
+
{Math.floor(progress ?? 0)} %
+ +
+ ) : ( + {success ? 'Success' : 'Confirming'} + )} +
+
+ ); + }); + return hoverable; +} + +const sendAmount: ColumnType = { + title: () => Amount, + key: 'Send amount', + width: 119, + ellipsis: true, + dataIndex: 'fromChainId', + render: (_, item) => , +}; + +const sendingAddress: ColumnType = { + title: () => From Address, + key: 'Sending address', + width: 236, + ellipsis: true, + dataIndex: 'fromChainId', + render: (fromChainId, item) => ( + + ), +}; + +const receivingAddress: ColumnType = { + title: () => To Address, + key: 'Receiving address', + width: 236, + ellipsis: true, + dataIndex: 'toChainId', + render: (toChainId, item) => ( + + ), +}; + +const fromTo = (): ColumnType => { + return { + title: () => Sent At, + dataIndex: 'id', + key: 'From - To', + width: 236, + ellipsis: true, + render: (_, items) => { + return ; + }, + }; +}; +const columns = { + sendAmount, + receivingAddress, + sendingAddress, + fromTo, +}; + +export default columns; diff --git a/src/page-components/Transactions/components.tsx b/src/page-components/Transactions/components.tsx new file mode 100644 index 00000000..710d98cc --- /dev/null +++ b/src/page-components/Transactions/components.tsx @@ -0,0 +1,63 @@ +import { Pagination, PaginationProps, Select, SelectProps } from 'antd'; +import clsx from 'clsx'; +import CommonSelect from 'components/CommonSelect'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { useLanguage } from 'i18n'; +import { useTranslation } from 'react-i18next'; +import { NetworkType } from 'types'; +import { CrossChainStatus } from 'types/misc'; +import styles from './styles.module.less'; +const statusList = Object.entries(CrossChainStatus) + .filter(([, value]) => typeof value !== 'number') + .map(([value, title]) => ({ value, title })); + +export function StatusSelect(props: SelectProps) { + const { t } = useLanguage(); + return ( + + {statusList.map((i) => { + return ( + + {t(i.title as string)} + + ); + })} + + ); +} + +export function NetworkSelect({ + networkList, + ...props +}: SelectProps & { + networkList: NetworkType[]; +}) { + return ( + + {networkList.map((i) => { + return ( + + {i.title} + + ); + })} + + ); +} + +export function TablePagination(props: PaginationProps) { + const isMd = useMediaQueries('md'); + const { t } = useTranslation(); + return ( +
+ + {!isMd &&
{t('records in total', { total: props.total })}
} +
+ ); +} diff --git a/src/page-components/Transactions/index.tsx b/src/page-components/Transactions/index.tsx new file mode 100644 index 00000000..eb41a760 --- /dev/null +++ b/src/page-components/Transactions/index.tsx @@ -0,0 +1,250 @@ +import { Row, Tabs } from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { request } from 'api'; +import { NetworkList } from 'constants/index'; +import { useWallet } from 'contexts/useWallet/hooks'; +import useInterval from 'hooks/useInterval'; +import useUrlSearchState from 'hooks/useUrlSearchState'; +import { memo, useCallback, useEffect, useMemo } from 'react'; +import { usePrevious, useSetState } from 'react-use'; +import { ChainId, CrossChainType, NetworkType } from 'types'; +import { CrossChainItem } from 'types/api'; +import { isELFChain } from 'utils/aelfUtils'; +import { getChainIdToMap } from 'utils/chain'; +import { parseCrossChainTransfers } from 'utils/transfers'; +import Columns from './columns'; +import { NetworkSelect, StatusSelect, TablePagination } from './components'; +import styles from './styles.module.less'; +import CommonTable from 'components/CommonTable'; +import { useLanguage } from 'i18n'; +import { isPortkey } from 'utils/portkey'; +import MainContentHeader from 'components/MainContentHeader'; +import clsx from 'clsx'; +import useMediaQueries from 'hooks/useMediaQueries'; +import PageHead from 'components/PageHead'; +type State = { + fromChainId?: ChainId; + toChainId?: ChainId; + status?: number; + page?: number; + totalCount?: number; + list?: CrossChainItem[]; +}; + +const PageSize = 10; + +const columns: ColumnsType = [ + Columns.sendAmount, + Columns.sendingAddress, + Columns.receivingAddress, + Columns.fromTo(), +]; + +const DefaultListState = { + page: 1, + totalCount: 0, + list: [], +}; +function Body({ + state, + selectState, + setSelect, + setState, + networkList, +}: { + state: State; + selectState: State; + setSelect: (v: any) => void; + setState: (v: any) => void; + networkList: NetworkType[]; +}) { + const { t } = useLanguage(); + const { page, totalCount, list } = state; + const { fromChainId, toChainId, status } = selectState; + return ( + <> + + { + setSelect({ status: value }); + setState(DefaultListState); + }} + /> + { + const tmpState: State = { fromChainId: value }; + if (toChainId && toChainId === value) tmpState.toChainId = fromChainId; + setSelect(tmpState); + setState(DefaultListState); + }} + /> + { + const tmpState: State = { toChainId: value }; + if (fromChainId && fromChainId === value) tmpState.fromChainId = toChainId; + setSelect(tmpState); + setState(DefaultListState); + }} + /> + + + setState({ page })} + pageSize={PageSize} + total={totalCount} + showSizeChanger={false} + /> + + ); +} + +function useHistory({ crossChainType }: { crossChainType: CrossChainType }) { + const [state, setState] = useSetState(DefaultListState); + const { page } = state; + const [selectState, setSelect] = useSetState(); + const { fromChainId, toChainId, status } = selectState; + const { fromWallet, toWallet } = useWallet(); + const { account: fromAccount } = fromWallet || {}; + const { account: toAccount } = toWallet || {}; + + const getReceiveList = useCallback(async () => { + if (!(fromAccount || toAccount)) { + return setState({ list: [], totalCount: 0 }); + } + const addresses = [fromAccount, toAccount].filter(Boolean).join(','); + const skipCount = page ? (page - 1) * PageSize : 0; + let type; + if (crossChainType === CrossChainType.homogeneous) { + type = '0'; + } else if (crossChainType === CrossChainType.heterogeneous) { + type = '1'; + } + const req = await request.cross.getCrossChainTransfers({ + params: { + addresses, + toChainId: getChainIdToMap(toChainId), + fromChainId: getChainIdToMap(fromChainId), + status, + skipCount, + type, + MaxResultCount: PageSize, + }, + }); + if (req.items) { + if (Array.isArray(req.items) && req.items.length === 0 && req.totalCount > 0) { + setState({ page: Math.ceil(req.totalCount / PageSize) }); + } else { + const list = parseCrossChainTransfers(req); + setState({ list, totalCount: req.totalCount }); + } + } + }, [crossChainType, fromAccount, fromChainId, page, setState, status, toAccount, toChainId]); + const preFromAccount = usePrevious(fromAccount); + const preToAccount = usePrevious(toAccount); + useEffect(() => { + if (preFromAccount !== fromAccount || preToAccount !== toAccount) setState(DefaultListState); + }, [fromAccount, preFromAccount, preToAccount, setState, toAccount]); + useInterval(getReceiveList, [getReceiveList], 10000); + const isomorphismNetworkList = useMemo(() => NetworkList.filter((i) => isELFChain(i.info.chainId)), []); + return useMemo( + () => ({ + networkList: crossChainType === CrossChainType.homogeneous ? isomorphismNetworkList : NetworkList, + state, + selectState, + setSelect, + setState, + }), + [crossChainType, isomorphismNetworkList, selectState, setSelect, setState, state], + ); +} + +function History() { + const [{ historyType = isPortkey() ? CrossChainType.heterogeneous : CrossChainType.all }, setActiveKey] = + useUrlSearchState(); + + const { t } = useLanguage(); + const isMd = useMediaQueries('md'); + + const allData = useHistory({ crossChainType: CrossChainType.all }); + const heterogeneousData = useHistory({ crossChainType: CrossChainType.heterogeneous }); + const homogeneousData = useHistory({ crossChainType: CrossChainType.homogeneous }); + + const tableData = useMemo(() => { + switch (historyType) { + case CrossChainType.heterogeneous: + return heterogeneousData; + case CrossChainType.homogeneous: + return homogeneousData; + case CrossChainType.all: + default: + return allData; + } + }, [allData, heterogeneousData, historyType, homogeneousData]); + return ( + <> + +
+ +

+ {t('Tips')} + {t(':')} +

+
    +
  1. {t('Check the transaction status from the transaction records.')}
  2. +
  3. {t('For more details, click on the From/To TXID to track its progress on the Explorer.')}
  4. +
+
+ ), + }} + /> + setActiveKey({ historyType: v })}> + {!isPortkey() && ( + <> + + + + )} + + + {!(isPortkey() && (historyType === CrossChainType.all || historyType === CrossChainType.homogeneous)) && ( +
+ +
+ )} + + + ); +} + +export default memo(History); diff --git a/src/page-components/Transactions/styles.module.less b/src/page-components/Transactions/styles.module.less new file mode 100644 index 00000000..4cc2b374 --- /dev/null +++ b/src/page-components/Transactions/styles.module.less @@ -0,0 +1,222 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; + +.tooltip-content { + .tooltip-title { + color: @font-15; + } + ol { + margin-left: 18px; + margin-bottom: 0; + list-style-type: decimal; + } +} +.history { + gap: 24px; + :global { + .@{app-prefix}-table-container .@{app-prefix}-table-content { + &::-webkit-scrollbar { + height: 8px; + width: 0; + overflow-y: scroll; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: @border-3; + } + } + .@{app-prefix}-tabs-top > .@{app-prefix}-tabs-nav { + margin-bottom: 0; + } + .@{app-prefix}-tabs-top > .@{app-prefix}-tabs-nav .@{app-prefix}-tabs-ink-bar, + .@{app-prefix}-tabs-top > div > .@{app-prefix}-tabs-nav .@{app-prefix}-tabs-ink-bar { + background: @border-2; + height: 4px; + border-radius: 1px; + } + .@{app-prefix}-tabs-tab { + padding-top: 0; + padding-bottom: 18px; + &:hover { + color: @font-1; + } + .@{app-prefix}-tabs-tab-active .@{app-prefix}-tabs-tab-btn { + text-shadow: none; + } + .@{app-prefix}-tabs-tab-btn { + font-size: 16px; + } + } + .@{app-prefix}-table-ping-right:not(.@{app-prefix}-table-has-fix-right) > .@{app-prefix}-table-container::after { + box-shadow: inset -10px 0 8px -8px rgb(0 0 0 / 3%); + } + } +} +.select { + flex: 1; + width: 100%; + margin-right: 33px; + &:last-child { + margin-right: 0; + } +} +.table { + margin-top: 24px; + table { + width: 100% !important; + } + .info-wrap { + gap: 8px; + } + .info-content { + gap: 4px; + } + .info-address { + gap: 4px; + } + .info-address-icon { + font-size: 24px; + } + .info-address-link { + .font-family-medium(); + } + .info-txid { + font-size: 12px; + line-height: 16px; + } + .info-arrow { + width: 24px; + height: 24px; + } + .from-to-wrap { + gap: 4px; + } + .transfer-time { + .font-family-medium(); + } + .from-to { + gap: 4px; + font-size: 12px; + line-height: 16px; + &::before { + content: ''; + width: 8px; + height: 8px; + border-radius: 50%; + background: @bg-20; + align-self: center; + } + } + .from-to-success { + &::before { + background: @bg-18; + } + } + .from-to-hovered { + .progress-wrap { + position: relative; + width: 100%; + height: 16px; + line-height: 16px; + } + .progress-text { + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + z-index: 1; + color: @font-15; + font-size: 10px; + .font-family-semibold(); + + opacity: 0.7; + } + .progress { + width: 100%; + height: 16px; + line-height: 16px; + :global { + .@{app-prefix}-progress-outer { + height: 16px; + } + .@{app-prefix}-progress-inner { + vertical-align: unset; + } + .@{app-prefix}-progress-success-bg, + .@{app-prefix}-progress-bg { + background: @bg-20; + } + } + } + } + .from-to-hovered-success { + .progress { + :global { + .@{app-prefix}-progress-success-bg, + .@{app-prefix}-progress-bg { + background: @bg-18; + } + } + } + } + :global { + .@{app-prefix}-table-cell-ellipsis { + white-space: normal; + word-break: normal; + } + .@{app-prefix}-table-thead > tr > th { + padding: 13px 8px; + background: @bg-19; + border-bottom-width: 0; + color: @font-15; + line-height: 22px; + .font-family-medium(); + .text-overflow(1); + + white-space: nowrap; + } + .@{app-prefix}-table-container table > thead > tr:first-child th:last-child { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + } + .@{app-prefix}-table-container table > thead > tr:first-child th:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + } + .@{app-prefix}-table-tbody > tr > td { + padding: 11px 8px; + color: @font-13; + } + .@{app-prefix}-table-thead > tr > th, + .@{app-prefix}-table-tbody > tr > td, + .@{app-prefix}-table tfoot > tr > th, + .@{app-prefix}-table tfoot > tr > td { + font-size: 14px; + line-height: 20px; + &::before { + display: none; + } + } + } +} +.pagination { + gap: 16px; + padding: 16px 0; + .pagination-total { + color: @font-13; + font-size: 14px; + line-height: 22px; + } +} + +@media screen and (max-width: @md-width) { + .select { + flex: 1 100%; + margin-top: 16px; + margin-right: 0; + &:first-child { + margin-top: 0; + } + } +} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx new file mode 100755 index 00000000..ee40affd --- /dev/null +++ b/src/pages/_app.tsx @@ -0,0 +1,89 @@ +import type { AppProps } from 'next/app'; +import '../styles/globals.less'; +import '../styles/common.less'; +import '../styles/antd.less'; +import '../utils/sentry'; +// import '../utils/vconsole'; +import dynamic from 'next/dynamic'; +import { DefaultHead } from 'components/PageHead'; +import Footer from 'components/Footer'; +import ScrollToTop from 'components/ScrollToTop'; +import Nav from 'components/Nav'; +import Loading from 'components/Loading'; +import { useRouter } from 'next/router'; +import { useMemo } from 'react'; +import clsx from 'clsx'; +import { HIDE_BACKGROUND_IMAGE_PATH_LIST, HIDE_MAIN_PAGE_LIST, NAV_LIST } from 'constants/link'; +import useMediaQueries from 'hooks/useMediaQueries'; +import { useIsTelegramPlatform } from 'hooks/telegram'; +import Mask from 'components/Mask'; + +const IS_MASK = false; +const Provider = dynamic(import('components/Provider'), { ssr: false }); +const Header = dynamic(import('components/Header'), { ssr: false }); + +export default function APP({ Component, pageProps }: AppProps) { + const router = useRouter(); + const isFull = useMemo(() => router.pathname === '/assets', [router.pathname]); + const isMainPage = useMemo(() => NAV_LIST.map((item) => item.href).includes(router.pathname), [router.pathname]); + const hideMainPageStyles = useMemo( + () => HIDE_MAIN_PAGE_LIST.map((item) => item.href).includes(router.pathname), + [router.pathname], + ); + const hideBackgroundImage = useMemo( + () => HIDE_BACKGROUND_IMAGE_PATH_LIST.some((item) => router.pathname.includes(item)), + [router.pathname], + ); + + const isMd = useMediaQueries('md'); + + const isTelegramPlatform = useIsTelegramPlatform(); + + const pageBodyClassName = useMemo(() => { + return clsx( + 'page-body', + isTelegramPlatform && 'tg-page-body', + hideBackgroundImage && 'page-body-hide-background-image', + ); + }, [isTelegramPlatform, hideBackgroundImage]); + + const renderPageBody = () => { + const comp = IS_MASK ? : ; + + if (isFull) { + return comp; + } else if (isMainPage) { + return ( +
+
+ {!IS_MASK && !isMd &&
+
+ +
+ ); + } else { + return ( +
+
{comp}
+
+ +
+ ); + } + }; + + return ( + <> + + + + {!isFull &&
} +
+ {renderPageBody()} +
+ + + ); +} diff --git a/src/pages/assets/index.tsx b/src/pages/assets/index.tsx new file mode 100644 index 00000000..09de4430 --- /dev/null +++ b/src/pages/assets/index.tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('page-components/Assets'), { + ssr: false, +}); diff --git a/src/pages/bridge.tsx b/src/pages/bridge.tsx new file mode 100644 index 00000000..1a2c6e8c --- /dev/null +++ b/src/pages/bridge.tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('page-components/Bridge'), { + ssr: false, +}); diff --git a/src/pages/index.tsx b/src/pages/index.tsx new file mode 100755 index 00000000..432d9f46 --- /dev/null +++ b/src/pages/index.tsx @@ -0,0 +1,14 @@ +import { ROUTE_PATHS } from 'constants/link'; + +export async function getServerSideProps() { + return { + redirect: { + destination: ROUTE_PATHS.BRIDGE, + permanent: false, + }, + }; +} + +const IndexPage = () => null; + +export default IndexPage; diff --git a/src/pages/listing-application/[stepText].tsx b/src/pages/listing-application/[stepText].tsx new file mode 100644 index 00000000..fc1d8a36 --- /dev/null +++ b/src/pages/listing-application/[stepText].tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('page-components/ListingApplication'), { + ssr: false, +}); diff --git a/src/pages/listing-application/index.tsx b/src/pages/listing-application/index.tsx new file mode 100644 index 00000000..e360c9fc --- /dev/null +++ b/src/pages/listing-application/index.tsx @@ -0,0 +1,16 @@ +import { useEffect } from 'react'; +import { useRouter } from 'next/router'; +import { getListingUrl } from 'utils/listingApplication'; +import { ListingStep } from 'constants/listingApplication'; + +const ListingApplicationIndex = () => { + const router = useRouter(); + + useEffect(() => { + router.replace(getListingUrl(ListingStep.TOKEN_INFORMATION)); + }, [router]); + + return null; +}; + +export default ListingApplicationIndex; diff --git a/src/pages/my-applications.tsx b/src/pages/my-applications.tsx new file mode 100644 index 00000000..fad6da59 --- /dev/null +++ b/src/pages/my-applications.tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('page-components/MyApplication'), { + ssr: false, +}); diff --git a/src/pages/pool/[chainId]/[symbol].tsx b/src/pages/pool/[chainId]/[symbol].tsx new file mode 100644 index 00000000..cb220acb --- /dev/null +++ b/src/pages/pool/[chainId]/[symbol].tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('page-components/Pools/Pool'), { + ssr: false, +}); diff --git a/src/pages/pools.tsx b/src/pages/pools.tsx new file mode 100644 index 00000000..1076fecd --- /dev/null +++ b/src/pages/pools.tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('page-components/Pools'), { + ssr: false, +}); diff --git a/src/pages/transactions.tsx b/src/pages/transactions.tsx new file mode 100644 index 00000000..cff97fa9 --- /dev/null +++ b/src/pages/transactions.tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('page-components/Transactions'), { + ssr: false, +}); diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts new file mode 100644 index 00000000..2c48e895 --- /dev/null +++ b/src/react-app-env.d.ts @@ -0,0 +1,33 @@ +/// +interface Window { + ethereum?: { + selectedAddress?: string; + isMetaMask?: true; + isCoinbaseWallet?: true; + on?: (...args: unknown[]) => void; + removeListener?: (...args: unknown[]) => void; + autoRefreshOnNetworkChange?: boolean; + request?: (...args: unknown[]) => void; + chainId?: number; + providerMap?: any; + }; + web3?: any; + plus?: any; + NightElf?: any; +} +declare module 'aelf-sdk'; +declare module 'aelf-sdk-cross-chain'; +declare module 'aelf-sdk-cross-chain/src'; +declare module '*.less'; +declare module '*.module.less'; +declare module './styles.module.less'; + +declare module '*.json'; +declare module '*.css'; + +declare module '*.png'; +declare module '*.jpg'; +declare module '*.jpeg'; +declare module '*.svg'; +declare module '*.gif'; +declare module '*.ico'; diff --git a/src/styles/antd.less b/src/styles/antd.less new file mode 100644 index 00000000..23e04297 --- /dev/null +++ b/src/styles/antd.less @@ -0,0 +1,135 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +.@{app-prefix}-btn { + border-radius: 40px; + .font-family-medium(); + .anticon { + display: inline-flex; + } +} +.@{app-prefix}-btn-default { + height: 40px; + padding-left: 20px; + padding-right: 20px; + color: @font-1; + border-color: @border-2; + &:hover { + background: @bg-8; + } + &:focus { + background: @bg-2; + } + &:disabled { + background: @bg-7; + border-color: @border-1; + color: @font-3; + } +} +.@{app-prefix}-btn-primary { + height: 40px; + padding-left: 20px; + padding-right: 20px; + &:disabled { + background: @primary-color; + border-color: @primary-color; + color: #fff; + opacity: 0.5; + } + &:hover { + background: @primary-color; + } +} +.@{app-prefix}-btn[disabled] { + &:hover { + background: @primary-color; + border-color: @primary-color; + color: #fff; + opacity: 0.5; + } +} +.@{app-prefix}-divider-vertical { + border-color: @border-1; +} + +.@{app-prefix}-message { + z-index: 20000; +} +.@{app-prefix}-message-notice { + .@{app-prefix}-message-notice-content { + padding: 24px 18px; + border-radius: 12px; + line-height: 14px; + box-shadow: 0 5px 24px 4px rgb(0 0 0 / 5%), 0 3px 10px 0 rgb(0 0 0 / 8%), 0 1px 16px -2px rgb(0 0 0 / 8%); + } +} +.@{app-prefix}-message-notice:first-child { + padding-top: 104px; +} + +.@{app-prefix}-input { + &::placeholder { + color: @font-10; + } +} +.@{app-prefix}-input, +.@{app-prefix}-input-affix-wrapper { + border-color: @border-1; +} + +.@{app-prefix}-input-suffix { + margin-left: 12px; + .@{app-prefix}-input-clear-icon { + line-height: 16px; + } + .anticon-close-circle { + vertical-align: middle; + } + svg { + width: 16px; + height: 16px; + path { + fill: @border-5; + } + } +} + +.@{app-prefix}-select-selector { + padding-right: 15px; + padding-left: 15px; +} + +.@{app-prefix}-select-arrow, +.@{app-prefix}-select-clear { + right: 15px; + width: 16px; + height: 16px; + margin-top: -8px; +} + +.@{app-prefix}-tooltip-inner { + padding: 8px; + border-radius: 8px; + background-color: @bg-2; + color: @font-13; + box-shadow: 0 0 16px 0 rgb(0 0 0 / 16%); + font-size: 12px; + font-weight: 400; + line-height: 20px; +} +.@{app-prefix}-tooltip-arrow-content::before { + background: @bg-2; +} + +.@{app-prefix}-tabs-top > .@{app-prefix}-tabs-nav::before { + border-color: @border-5; +} + +.ant-scrolling-effect { + min-width: 100%; +} + +body { + p { + margin-bottom: 0; + } +} diff --git a/src/styles/common.less b/src/styles/common.less new file mode 100644 index 00000000..7fa4bf41 --- /dev/null +++ b/src/styles/common.less @@ -0,0 +1,5 @@ +@import './common/commonLink.less'; +@import './common/commonModal.less'; +@import './common/accountModal.less'; +@import './common/modals.less'; +@import './common/message.less'; diff --git a/src/styles/common/accountModal.less b/src/styles/common/accountModal.less new file mode 100644 index 00000000..b0262e9d --- /dev/null +++ b/src/styles/common/accountModal.less @@ -0,0 +1,98 @@ +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '/src/assets/theme/constants.less'; +.account-modal { + .@{app-prefix}-modal-header { + .@{app-prefix}-modal-title .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-body { + padding: 24px; + } + .account-modal-info-wrap { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + + .account-modal-info-label { + line-height: 22px; + font-size: 14px; + color: @font-5; + .font-family-medium(); + } + .account-modal-info-assets { + display: flex; + align-items: center; + line-height: 20px; + font-size: 12px; + cursor: pointer; + color: @font-12; + min-width: 100px; + margin-left: 8px; + justify-content: flex-end; + .font-family-medium(); + + &-arrow { + margin-left: 8px; + transform: rotateZ(-90deg); + } + } + } + + .account-modal-card { + border: 0; + border-radius: 2px; + .@{app-prefix}-card-body { + padding: 0; + .account-modal-account { + font-size: 14px; + .font-family-medium(); + .anticon { + margin-right: 5px; + } + } + .account-modal-copy { + color: @font-5; + font-size: 14px; + height: 18px; + line-height: 18px; + margin-left: 8px; + .anticon { + margin-right: 2px; + font-size: 16px; + } + } + } + } + .account-modal-button { + width: 100%; + margin-top: 30px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + .@{app-prefix}-btn-primary { + height: 40px; + // width: calc(49% - 8px); + // margin: 0 4px; + flex: 1; + font-size: 16px; + line-height: 20px; + } + } + + .account-modal-card-box { + display: flex; + &-icon { + margin-right: 16px; + font-size: 48px; + } + &-link { + line-height: 24px; + margin-top: 0; + height: 24px; + } + } +} diff --git a/src/styles/common/commonLink.less b/src/styles/common/commonLink.less new file mode 100644 index 00000000..70bf9df4 --- /dev/null +++ b/src/styles/common/commonLink.less @@ -0,0 +1,13 @@ +.common-link { + height: 20px; + padding: 0; + font-size: 14px; + line-height: 20px; + .anticon { + font-size: 16px; + } + .anticon + span, + span + .anticon { + margin-left: 2px; + } +} diff --git a/src/styles/common/commonModal.less b/src/styles/common/commonModal.less new file mode 100644 index 00000000..311be35e --- /dev/null +++ b/src/styles/common/commonModal.less @@ -0,0 +1,146 @@ +@import '../../assets/theme/color.less'; +@import '../../assets/theme/font.less'; + +.common-modals { + .@{app-prefix}-modal-content { + border-radius: 8px; + overflow: auto; + padding: 0; + } + .@{app-prefix}-modal-header { + height: 52px; + padding: 24px 24px 0; + border-bottom: none; + } + .@{app-prefix}-modal-title { + .font-family-semibold(); + + color: @font-15; + font-size: 16px; + font-weight: 700; + line-height: 24px; + & > .@{app-prefix}-row > .@{app-prefix}-col { + text-align: left !important; + } + } + .@{app-prefix}-modal-close-x { + display: flex; + justify-content: center; + align-items: center; + padding-top: 10px; + } + .common-modal-close-icon { + width: 16px; + height: 16px; + } + .common-modal-left-icon { + cursor: pointer; + font-size: 18px; + text-align: left; + color: var(--ant-text4-color); + } + .common-button { + font-size: 18px; + margin-top: 15px; + width: 100%; + height: 56px; + } + + .@{app-prefix}-modal-body { + display: flex; + flex: 1; + padding: 16px 24px 24px; + width: 35vw; + min-width: 500px; + max-width: 500px; + max-height: 800px; + flex-direction: column; + } +} + +.mobile-site { + .@{app-prefix}-modal-wrap { + overflow: auto; + overflow-y: hidden; + } + .common-modals { + top: 0; + min-height: 100%; + display: flex; + flex-direction: column-reverse; + padding-bottom: 30px; + justify-content: center; + margin-top: 0; + max-width: calc(calc(100vw - 48px)); + + .common-button { + border-radius: 2px; + width: 100%; + height: 2.86rem; + font-size: 1rem; + line-height: 1.28rem; + } + .@{app-prefix}-btn-primary { + border: 0; + } + .@{app-prefix}-modal-body { + max-height: 65vh; + width: auto; + min-width: auto; + max-width: unset; + padding: 16px 16px 24px; + overflow: auto; + } + } + .tg-common-modals { + min-height: unset !important; + height: 100%; + } + .common-modals.common-bottom-modals { + top: 0; + margin: 0; + padding: 0; + min-height: 100%; + min-width: 100% !important; + display: flex; + flex-direction: column-reverse; + justify-content: flex-start; + .common-modal-close-icon { + width: 20px; + height: 20px; + } + .@{app-prefix}-modal-header { + height: 56px; + padding: 16px 0; + .@{app-prefix}-modal-title { + font-weight: 500; + line-height: 24px; + & > .@{app-prefix}-row > .@{app-prefix}-col { + text-align: center !important; + } + } + } + .@{app-prefix}-modal-close-x { + padding-top: 1px; + } + .@{app-prefix}-modal-body { + max-width: 100%; + padding: 0; + } + .@{app-prefix}-modal-content { + padding: 0 16px 16px; + border-radius: 0; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + } + } + .common-modals.tg-common-bottom-modals { + min-height: unset !important; + height: 100%; + } + + .common-modals.tg-view-progress-modal { + min-height: fill-available !important; + height: unset; + } +} diff --git a/src/styles/common/message.less b/src/styles/common/message.less new file mode 100644 index 00000000..73f849cb --- /dev/null +++ b/src/styles/common/message.less @@ -0,0 +1,19 @@ +@import '/src/assets/theme/color.less'; +.tx-row { + width: min(80vh, 320px); + background: @bg-7; + color: @font-3; + margin-top: 25px; + padding: 16px; + border-radius: 4px; + div:first-child { + color: @font-3; + } + a { + margin-right: 10px; + } +} +.tx-title-row { + width: 100%; + text-align: left; +} diff --git a/src/styles/common/modals.less b/src/styles/common/modals.less new file mode 100644 index 00000000..6095bdb4 --- /dev/null +++ b/src/styles/common/modals.less @@ -0,0 +1,26 @@ +.modals { + max-width: calc(calc(100vw - 40px)); + .@{app-prefix}-modal-body { + min-width: 448px; + max-width: 448px; + } +} +.mobile-site { + .modals { + max-width: calc(calc(100vw - 48px)); + .@{app-prefix}-modal-content { + padding: 0 24px 24px; + } + .@{app-prefix}-modal-header { + padding: 24px 0 21px; + .@{app-prefix}-modal-title { + line-height: 16px; + } + } + .@{app-prefix}-modal-body { + min-width: 90%; + padding: 24px 0 0; + margin: auto; + } + } +} diff --git a/src/styles/globals.less b/src/styles/globals.less new file mode 100755 index 00000000..76d0dcdc --- /dev/null +++ b/src/styles/globals.less @@ -0,0 +1,119 @@ +@import '~antd/dist/antd.variable.less'; +@import '/src/assets/theme/customize.less'; +@import '/src/assets/theme/common-class.less'; +@import '/src/assets/theme/constants.less'; +@import '/src/assets/theme/color.less'; +@import '/src/assets/theme/font.less'; +@import '@portkey/connect-web-wallet/dist/assets/index.css'; + +html, +body.pc-site, +body.mobile-site { + padding: 0; + margin: 0; + // font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, + // Helvetica Neue, sans-serif; + // background: url('../assets/images/bg.svg') no-repeat; + background-color: @bg-1 !important; + color: @font-2; + .font-family-regular(); + // Prevent scrollbars from appearing on body + // overflow: hidden; +} + +a { + color: @font-1; + text-decoration: none; +} + +* { + box-sizing: border-box; +} +/* stylelint-disable-next-line selector-id-pattern */ +div#__next { + height: 100%; +} +.page-body-wrap { + overflow: auto; + height: calc(100% - @header-height); + background-image: none; +} +.tg-page-body-wrap { + background-image: none; +} +.page-full { + height: 100vh !important; +} + +.page-body { + overflow: hidden; + display: flex; + flex-direction: column; + min-height: 100%; + background-image: url('../assets/images/background.png'); + background-repeat: no-repeat; + background-position: center calc(100% + 50px); + background-size: 100% auto; + background-color: @bg-14; +} +.tg-page-body { + min-height: unset !important; +} +.page-body-hide-background-image { + background-image: none; +} +.main-page-content-wrap { + display: flex; + align-items: flex-start; + gap: 8px; + padding: 48px 48px 250px 0; +} +.main-page-component-wrap { + flex: 1; + overflow-x: auto; + max-width: 893px; + margin: 16px auto 0; + padding: 32px; + background-color: @bg-2; + border-radius: 16px; + border: 1px solid @border-5; +} +.page-content { + flex: 1; + min-height: calc(100% - @footer-height); +} +.tg-page-content { + min-height: unset !important; +} +@media screen and (max-width: @md-width) { + .page-body { + background-position: center calc(100% + 14px); + } + .main-page-content-wrap { + padding: 16px 12px 193px; + } + .main-page-component-wrap { + margin: 0; + padding: 24px 12px; + } + .page-content { + min-height: 100%; + } + .tg-page-content { + min-height: unset !important; + } +} + +#tc-widget-root { + tc-root { + div { + z-index: 99999; + } + } +} +.web-login-connect-modal-root, +.web-login-disconnect-modal-root, +.portkey-connect-modal-root { + z-index: 99999; + position: relative; +} diff --git a/src/types/api.ts b/src/types/api.ts new file mode 100644 index 00000000..629004d2 --- /dev/null +++ b/src/types/api.ts @@ -0,0 +1,282 @@ +import { WhitelistMap } from 'contexts/useWhitelist/actions'; +import { ChainId, TokenInfo } from 'types'; + +/** + * AElf.CrossChainServer.CrossChain.CrossChainTransferIndexDto + */ +export interface CrossChainItem { + fromAddress?: string; + fromChainId?: ChainId; + id?: string; + progress?: number; + progressUpdateTime?: Date; + receiptId?: string; + receiveAmount?: number; + receiveTime?: Date; + receiveToken?: TokensToken; + receiveTransactionId?: string; + status?: number; + toAddress?: string; + toChainId?: ChainId; + transferAmount?: number; + transferBlockHeight?: number; + transferTime?: Date; + transferToken?: TokensToken; + transferTransactionId?: string; + type?: number; +} + +export interface APICrossChainItem { + fromAddress?: string; + fromChainId: string; + id?: string; + progress?: number; + progressUpdateTime?: Date; + receiptId?: string; + receiveAmount?: number; + receiveTime?: Date; + receiveToken?: TokensToken; + receiveTransactionId?: string; + status?: number; + toAddress?: string; + toChainId: string; + transferAmount?: number; + transferBlockHeight?: number; + transferTime?: Date; + transferToken?: TokensToken; + transferTransactionId?: string; + type?: number; +} +/** + * AElf.CrossChainServer.Tokens.TokenDto + */ +export type TokensToken = { + chainId?: string; + id?: string; +} & TokenInfo; + +export type TGetApplicationTokenListRequest = { + symbol?: string; +}; + +export type TGetApplicationTokenListResult = { + tokenList: TApplicationTokenItem[]; +}; + +export type TApplicationTokenItem = { + tokenName: string; + symbol: string; + tokenImage: string; + totalSupply: number; + status: TApplicationTokenStatus; +}; + +export enum TApplicationTokenStatus { + Listed = 'Listed', + Integrating = 'Integrating', + Available = 'Available', +} + +export type TGetApplicationTokenDetailRequest = { + symbol: string; +}; + +export type TGetApplicationTokenDetailResult = { + liquidityInUsd: string; + holders: number; +}; + +export type TCommitTokenInfoRequest = { + symbol: string; + officialWebsite?: string; + officialTwitter?: string; + title?: string; + personName?: string; + telegramHandler?: string; + email: string; +}; + +export type TCommitTokenInfoResult = boolean; + +export type TGetApplicationTokenInfoRequest = { + symbol: string; +}; + +export type TGetApplicationTokenInfoResult = { + symbol: string; + userAddress: string; + officialWebsite: string; + officialTwitter: string; + title: string; + personName: string; + telegramHandler: string; + email: string; +}; + +export type TGetApplicationChainStatusListRequest = { + symbol: string; +}; + +export type TGetApplicationChainStatusListResult = { + chainList: TApplicationChainStatusItem[]; +}; + +export type TApplicationChainStatusItem = { + chainId: string; + chainName: string; + status: ApplicationChainStatusEnum; + checked: boolean; + totalSupply: number; + decimals: number; + contractAddress: string; + symbol: string; + tokenName: string; + icon: string; + poolAddress: string; + bindingId?: string; + thirdTokenId?: string; +}; + +export enum ApplicationChainStatusEnum { + Unissued = 'Unissued', + Issuing = 'Issuing', + Issued = 'Issued', + PoolInitializing = 'PoolInitializing', + PoolInitialized = 'PoolInitialized', + Complete = 'Complete', + Failed = 'Failed', +} + +export type TAddApplicationChainRequest = { + chainIds?: string[]; + symbol: string; +}; + +export type TAddApplicationChainResult = { + chainList?: TAddApplicationChainResultChainItem[]; +}; + +export type TAddApplicationChainResultChainItem = { + id: string; + chainId: string; +}; + +export type TPrepareBindIssueRequest = { + address: string; + symbol: string; + chainId: string; + contractAddress: string; + supply: string; +}; + +export type TPrepareBindIssueResult = { + bindingId: string; + thirdTokenId: string; +}; + +export type TGetApplicationIssueRequest = { + bindingId: string; + thirdTokenId: string; + mintToAddress: string; +}; + +export type TGetApplicationIssueResult = boolean; + +export type TGetMyApplicationListRequest = { + skipCount?: number; + maxResultCount?: number; +}; + +export type TGetMyApplicationListResult = { + items: TMyApplicationItem[]; + totalCount: number; +}; + +export type TMyApplicationItem = { + id: string; + symbol: string; + status: ApplicationChainStatusEnum; + updateTime: number; + failedTime?: number; // status === ApplicationChainStatusEnum.Failed + failedReason?: string; // status === ApplicationChainStatusEnum.Failed + chainTokenInfo: TMyApplicationChainTokenInfo; +}; + +export type TMyApplicationChainTokenInfo = { + chainId: string; + chainName: string; + tokenName: string; + icon: string; + poolAddress: string; + status: ApplicationChainStatusEnum; +}; + +export type TGetApplicationDetailRequest = { + id: string; + symbol: string; + network?: string; +}; + +export type TGetApplicationDetailResult = TApplicationDetailItem[]; + +export type TApplicationDetailItem = { + id: string; + symbol: string; + userAddress: string; + status: ApplicationChainStatusEnum; + createTime: number; + updateTime: number; + chainTokenInfo?: TApplicationDetailItemChainTokenInfo; +}; + +export type TApplicationDetailItemChainTokenInfo = { + chainId: string; + chainName: string; + tokenName: string; + symbol: string; + totalSupply: number; + decimals: number; + icon: string; + poolAddress: string; + contractAddress: string; + status: ApplicationChainStatusEnum; + minAmount: string; // min amount usd + dailyLimit: string; + rateLimit: string; +}; + +export type TGetTokenConfigRequest = { + symbol: string; +}; + +export type TGetTokenConfigResult = { + liquidityInUsd: string; + holders: number; +}; + +export interface APIPoolItem { + token?: TokensToken; + chainId: string; + myTvlInUsd?: string; + totalTvlInUsd?: string; + tokenPrice?: string; +} + +export type TPoolOverview = { + totalTvlInUsd: string | number; + myTotalTvlInUsd: string | number; + poolCount: number; + tokenCount: number; +}; + +export type TGetTokenPriceRequest = { + symbol: string; + amount: string; +}; + +export type TGetTokenPriceResult = { + symbol: string; + tokenAmountInUsd: number; +}; + +export type TGetTokenWhiteListResult = WhitelistMap; diff --git a/src/types/index.tsx b/src/types/index.tsx new file mode 100644 index 00000000..c7b755ec --- /dev/null +++ b/src/types/index.tsx @@ -0,0 +1,102 @@ +import type { AElfDappBridge } from '@aelf-react/types'; +import type { AElfContextType } from '@aelf-react/core/dist/types'; +import { AElfNodes } from 'constants/aelf'; +import { CHAIN_NAME } from 'constants/index'; +import { Accounts } from '@portkey/provider-types'; +import { WalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { Wallet as TonWallet } from '@tonconnect/ui-react'; +import { DisconnectMutateAsync } from 'wagmi/query'; +import { Connector } from 'wagmi'; +import { SupportedChainId } from 'constants/chain'; + +export type ChainId = keyof typeof CHAIN_NAME; +export type ChainType = 'ERC' | 'ELF' | 'TON'; + +export type WalletType = 'PORTKEY' | 'NIGHTELF' | 'ERC' | 'TON' | 'FAIRY_VAULT'; + +export type NetworkType = { + title: string; + info: { + chainId: ChainId; + exploreUrl: string; + rpcUrl: string; + }; +}; + +export type AelfInstancesKey = keyof typeof AElfNodes; + +export const METAMASK_WALLET_ID = 'io.metamask'; // 'metaMaskSDK' +export const COINBASE_WALLET_ID = 'coinbaseWalletSDK'; // 'com.coinbase.wallet' +export const WALLET_CONNECT_ID = 'walletConnect'; + +export enum EVMConnectorId { + METAMASK = METAMASK_WALLET_ID, + COINBASE_WALLET = COINBASE_WALLET_ID, + WALLET_CONNECT = WALLET_CONNECT_ID, +} + +export enum AElfConnectorId { + NIGHTELF = 'NIGHTELF', + PORTKEY = 'PORTKEY', + FAIRY_VAULT = 'FAIRY_VAULT', +} + +export enum TONConnectorId { + TON = 'TON', +} + +export type TWalletConnectorId = EVMConnectorId | AElfConnectorId | TONConnectorId; + +export type Web3Type = { + chainId?: ChainId; + aelfInstance?: AElfDappBridge; + isActive?: boolean; + account?: string; + connector?: Connector | string; + connectorId?: TWalletConnectorId; + deactivate?: AElfContextType['deactivate'] | DisconnectMutateAsync; + aelfInstances?: { [key in AelfInstancesKey]: AElfDappBridge }; + isPortkey?: boolean; + walletType?: WalletType; + loginWalletType?: WalletTypeEnum; + accounts?: Accounts | readonly [`0x${string}`, ...`0x${string}`[]] | readonly `0x${string}`[]; + baseAccount?: TonWallet['account']; + getProvider?: (chainId?: SupportedChainId) => Promise; +}; +export type TokenInfo = { + decimals: number; + symbol: string; + tokenName?: string; + address?: string; + issueChainId?: number; + issuer?: string; + isBurnable?: boolean; + totalSupply?: number; + isNativeToken?: boolean; + onlyFrom?: boolean; + onlyTo?: boolean; + icon?: string; +}; + +export enum CrossChainType { + all = 'all', + homogeneous = 'homogeneous', + heterogeneous = 'heterogeneous', +} + +export enum OperatePool { + add = 'add', + remove = 'remove', +} + +export interface WalletInfo { + connectorId: TWalletConnectorId; + name: string; + description: string; + href: string | null; + primary?: true; + mobile?: true; + mobileOnly?: true; + iconType: string; + chainType: ChainType; +} diff --git a/src/types/listingApplication.ts b/src/types/listingApplication.ts new file mode 100644 index 00000000..3b766a98 --- /dev/null +++ b/src/types/listingApplication.ts @@ -0,0 +1,78 @@ +import { TApplicationChainStatusItem, TApplicationTokenStatus } from './api'; + +export enum TMyApplicationStatus { + Processing = 'Processing', + Succeed = 'Complete', + Failed = 'Failed', +} + +export type TSearchParams = { + symbol?: string; + id?: string; + networks?: string; +}; + +export enum TokenInformationFormKeys { + TOKEN = 'token', + OFFICIAL_WEBSITE = 'officialWebsite', + OFFICIAL_TWITTER = 'officialTwitter', + TITLE = 'title', + PERSON_NAME = 'personName', + TELEGRAM_HANDLER = 'telegramHandler', + EMAIL = 'email', +} + +export type TTokenItem = { + name: string; + symbol: string; + icon: string; + totalSupply: number; + status?: TApplicationTokenStatus; +}; + +export type TTokenConfig = { + liquidityInUsd: string; + holders: number; +}; + +export type TTokenInformationFormValues = { + [TokenInformationFormKeys.TOKEN]: TTokenItem; + [TokenInformationFormKeys.OFFICIAL_WEBSITE]: string; + [TokenInformationFormKeys.OFFICIAL_TWITTER]: string; + [TokenInformationFormKeys.TITLE]: string; + [TokenInformationFormKeys.PERSON_NAME]: string; + [TokenInformationFormKeys.TELEGRAM_HANDLER]: string; + [TokenInformationFormKeys.EMAIL]: string; +}; + +export enum FormValidateStatus { + Error = 'error', + Normal = '', +} + +export type TTokenInformationFormValidateData = { + [key in TokenInformationFormKeys]: { + validateStatus: FormValidateStatus; + errorMessage: string; + }; +}; + +export enum SelectChainFormKeys { + CHAINS = 'chains', + INITIAL_SUPPLY = 'initialSupply', +} + +export type TChains = { + [SelectChainFormKeys.CHAINS]: TApplicationChainStatusItem[]; +}; + +export type TSelectChainFormValues = TChains & { + [SelectChainFormKeys.INITIAL_SUPPLY]: string; +}; + +export type TSelectChainFormValidateData = { + [SelectChainFormKeys.INITIAL_SUPPLY]: { + validateStatus: FormValidateStatus; + errorMessage: string; + }; +}; diff --git a/src/types/misc.ts b/src/types/misc.ts new file mode 100644 index 00000000..79644ff6 --- /dev/null +++ b/src/types/misc.ts @@ -0,0 +1,10 @@ +export type IconInfo = { + type: string; + color?: string; +}; + +export enum CrossChainStatus { + Transferred, + Indexed, + Received, +} diff --git a/src/types/telegram.d.ts b/src/types/telegram.d.ts new file mode 100644 index 00000000..7fcf160d --- /dev/null +++ b/src/types/telegram.d.ts @@ -0,0 +1,864 @@ +declare const Telegram: ITelegram; + +type Color = string | false; + +interface ITelegram { + Utils: { + sessionStorageGet: (key) => any; + sessionStorageSet: (key, value) => any; + urlAppendHashParams: (url, addHash) => any; + urlParseHashParams: (locationHash) => any; + urlParseQueryString: (queryString) => any; + urlSafeDecode: (urlencoded) => any; + }; + WebView: { + callEventCallbacks: (eventType, func) => any; + initParams: { tgWebAppVersion: '7.0'; tgWebAppPlatform: 'macos'; tgWebAppThemeParams: '' }; + isIframe: boolean; + offEvent: (eventType, callback) => any; + onEvent: (eventType, callback) => any; + postEvent: (eventType, callback, eventData) => any; + receiveEvent: (eventType, eventData) => any; + }; + WebApp: WebApp; +} + +interface WebApp { + /** + * A string with raw data transferred to the Web App, convenient for + * validating data. WARNING: Validate data from this field before using it on + * the bot's server. + */ + initData: string; + /** + * An object with input data transferred to the Web App. WARNING: Data from + * this field should not be trusted. You should only use data from initData on + * the bot's server and only after it has been validated. + */ + initDataUnsafe: WebAppInitData; + /** + * The version of the Bot API available in the user's Telegram app. + */ + version: string; + /** + * The name of the platform of the user's Telegram app. + */ + platform: string; + /** + * The color scheme currently used in the Telegram app. Either “light” or + * “dark”. Also available as the CSS variable var(--tg-color-scheme). + */ + colorScheme: 'light' | 'dark'; + /** + * An object containing the current theme settings used in the Telegram app. + */ + themeParams: ThemeParams; + /** + * True if the Web App is expanded to the maximum available height. False, if + * the Web App occupies part of the screen and can be expanded to the full + * height using the expand() method. + */ + isExpanded: boolean; + /** + * The current height of the visible area of the Web App. Also available in + * CSS as the variable var(--tg-viewport-height). + * + * The application can display just the top part of the Web App, with its + * lower part remaining outside the screen area. From this position, the user + * can “pull” the Web App to its maximum height, while the bot can do the same + * by calling the expand() method. As the position of the Web App changes, the + * current height value of the visible area will be updated in real time. + * + * Please note that the refresh rate of this value is not sufficient to + * smoothly follow the lower border of the window. It should not be used to + * pin interface elements to the bottom of the visible area. It's more + * appropriate to use the value of the viewportStableHeight field for this + * purpose. + */ + viewportHeight: number; + /** + * The height of the visible area of the Web App in its last stable state. + * Also available in CSS as a variable var(--tg-viewport-stable-height). + * + * The application can display just the top part of the Web App, with its + * lower part remaining outside the screen area. From this position, the user + * can “pull” the Web App to its maximum height, while the bot can do the same + * by calling the expand() method. Unlike the value of viewportHeight, the + * value of viewportStableHeight does not change as the position of the Web + * App changes with user gestures or during animations. The value of + * viewportStableHeight will be updated after all gestures and animations are + * completed and the Web App reaches its final size. + * + * Note the event viewportChanged with the passed parameter + * isStateStable=true, which will allow you to track when the stable state of + * the height of the visible area changes. + */ + viewportStableHeight: number; + /** + * Current header color in the #RRGGBB format. + */ + headerColor: string; + /** + * Current background color in the #RRGGBB format. + */ + backgroundColor: string; + /** + * True, if the confirmation dialog is enabled while the user is trying to close the Web App. + * False, if the confirmation dialog is disabled. + */ + isClosingConfirmationEnabled: boolean; + /** + * An object for controlling the back button which can be displayed in the + * header of the Web App in the Telegram interface. + */ + BackButton: BackButton; + /** + * An object for controlling the main button, which is displayed at the + * bottom of the Web App in the Telegram interface. + */ + MainButton: MainButton; + /** + * An object for controlling the Settings item in the context menu of the Mini App + * in the Telegram interface. + */ + SettingsButton: SettingsButton; + /** + * An object for controlling haptic feedback. + */ + HapticFeedback: HapticFeedback; + /** + * An object for controlling cloud storage. + */ + CloudStorage: CloudStorage; + /** + * Returns true if the user's app supports a version of the Bot API that is + * equal to or higher than the version passed as the parameter. + */ + isVersionAtLeast(version: string): boolean; + /** + * A method that sets the app header color in the `#RRGGBB` format. + * You can also use keywords bg_color and secondary_bg_color. + */ + // string & {} prevents this from eagerly collapsing into just string + setHeaderColor(color: 'bg_color' | 'secondary_bg_color' | (string & any)): void; + /** + * A method that sets the app background color in the `#RRGGBB` format or + * you can use keywords bg_color, secondary_bg_color instead. + */ + setBackgroundColor(color: 'bg_color' | 'secondary_bg_color' | (string & any)): void; + /** + * A method that enables a confirmation dialog while the user is trying to close the Web App. + */ + enableClosingConfirmation(): void; + /** + * A method that disables the confirmation dialog while the user is trying to close the Web App. + */ + disableClosingConfirmation(): void; + /** + * A method that sets the app event handler. Check the list of available + * events. + */ + onEvent( + eventType: 'themeChanged' | 'mainButtonClicked' | 'backButtonClicked' | 'settingsButtonClicked', + eventHandler: () => void, + ): void; + onEvent(eventType: 'popupClosed', eventHandler: (eventData: { button_id: string | null }) => void): void; + onEvent(eventType: 'viewportChanged', eventHandler: (eventData: { isStateStable: boolean }) => void): void; + onEvent( + eventType: 'invoiceClosed', + eventHandler: (eventData: { url: string; status: 'paid' | 'cancelled' | 'failed' | 'pending' }) => void, + ): void; + onEvent(eventType: 'qrTextReceived', eventHandler: (eventData: { data: string }) => void): void; + onEvent(eventType: 'clipboardTextReceived', eventHandler: (eventData: { data: string | null }) => void): void; + onEvent( + eventType: 'writeAccessRequested', + eventHandler: (eventData: { status: 'allowed' | 'cancelled' }) => void, + ): void; + onEvent(eventType: 'contactRequested', eventHandler: (eventData: RequestContactResponse) => void): void; + + /** A method that deletes a previously set event handler. */ + offEvent( + eventType: 'themeChanged' | 'mainButtonClicked' | 'backButtonClicked' | 'settingsButtonClicked', + eventHandler: () => void, + ): void; + offEvent(eventType: 'popupClosed', eventHandler: (eventData: { button_id: string | null }) => void): void; + offEvent(eventType: 'viewportChanged', eventHandler: (eventData: { isStateStable: boolean }) => void): void; + offEvent( + eventType: 'invoiceClosed', + eventHandler: (eventData: { url: string; status: 'paid' | 'cancelled' | 'failed' | 'pending' }) => void, + ): void; + offEvent(eventType: 'qrTextReceived', eventHandler: (eventData: { data: string }) => void): void; + offEvent(eventType: 'clipboardTextReceived', eventHandler: (eventData: { data: string | null }) => void): void; + offEvent( + eventType: 'writeAccessRequested', + eventHandler: (eventData: { status: 'allowed' | 'cancelled' }) => void, + ): void; + offEvent(eventType: 'contactRequested', eventHandler: (eventData: RequestContactResponse) => void): void; + + /** + * A method used to send data to the bot. When this method is called, a + * service message is sent to the bot containing the data data of the length + * up to 4096 bytes, and the Web App is closed. See the field web_app_data in + * the class Message. + * + * This method is only available for Web Apps launched via a Keyboard button. + */ + sendData(data: string): void; + /** + * A method that inserts the bot's username and the specified inline query in the current chat's input field. + * Query may be empty, in which case only the bot's username will be inserted. + * If an optional choose_chat_types parameter was passed, the client prompts the user to choose a specific chat, + * then opens that chat and inserts the bot's username and the specified inline query in the input field. + * You can specify which types of chats the user will be able to choose from. + * It can be one or more of the following types: users, bots, groups, channels. + */ + switchInlineQuery(query: string, choose_chat_types?: Array<'users' | 'bots' | 'groups' | 'channels'>): void; + /** + * A method that opens a link in an external browser. + * The Web App will not be closed. + * If the optional options parameter is passed with the field + * @param try_instant_view the link will be opened in Instant View mode if possible. + * + * Note that this method can be called only in response to user interaction with + * the Web App interface (e.g. a click inside the Web App or on the main button) + */ + openLink(url: string, options?: { try_instant_view?: boolean }): void; + /** + * A method that opens a telegram link inside Telegram app. The Web App will + * be closed. + */ + openTelegramLink(url: string): void; + /** + * A method that opens an invoice using the link url. The Web App will + * receive the event invoiceClosed when the invoice is closed. If an + * optional callback parameter was passed, the callback function will be + * called and the invoice status will be passed as the first argument. + */ + openInvoice(url: string, callback: (url: string, status: 'paid' | 'cancelled' | 'failed' | 'pending') => void): void; + /** + * A method that shows a native popup described by the params argument of the type PopupParams. + * The Web App will receive the event popupClosed when the popup is closed. If an optional + * callback parameter was passed, the callback function will be called and the field id of the + * pressed button will be passed as the first argument. + */ + showPopup(params: PopupParams, callback?: (button_id: string) => void): void; + /** + * A method that shows message in a simple alert with a 'Close' button. If an optional callback + * parameter was passed, the callback function will be called when the popup is closed. + */ + showAlert(message: string, callback?: () => void): void; + /** + * A method that shows message in a simple confirmation window with 'OK' and 'Cancel' buttons. + * If an optional callback parameter was passed, the callback function will be called when the + * popup is closed and the first argument will be a boolean indicating whether the user + * pressed the 'OK' button. + */ + showConfirm(message: string, callback?: (ok: boolean) => void): void; + /** + * A method that shows a native popup for scanning a QR code described by the params argument of the type ScanQrPopupParams. + * The Web App will receive the event qrTextReceived every time the scanner catches a code with text data. + * If an optional callback parameter was passed, the callback function will be called and the text from the QR + * code will be passed as the first argument. Returning true inside this callback function causes the popup to be closed. + */ + showScanQrPopup(params: ScanQrPopupParams, callback?: (data: string) => void): void; + /** + * A method that closes the native popup for scanning a QR code opened with the + * showScanQrPopup method. Run it if you received valid data in the event qrTextReceived. + */ + closeScanQrPopup(): void; + /** + * A method that requests text from the clipboard. The Web App will receive the event clipboardTextReceived. + * If an optional callback parameter was passed, the callback function will be + * called and the text from the clipboard will be passed as the first argument. + * + * Note: this method can be called only for Web Apps launched from the attachment menu and only + * in response to a user interaction with the Web App interface (e.g. a click inside the Web App or on the main button). + */ + readTextFromClipboard(callback?: (data: string | null) => void): void; + /** + * A method that shows a native popup requesting permission for the bot to + * send messages to the user. + * + * @param callback If an optional callback parameter was passed, the + * callback function will be called when the popup is closed and the first + * argument will be a boolean indicating whether the user granted this + * access. + */ + requestWriteAccess(callback?: (success: boolean) => void): void; + /** + * A method that shows a native popup prompting the user for their phone + * number. + * + * @param callback If an optional callback parameter was passed, the + * callback function will be called when the popup is closed and the first + * argument will be a boolean indicating whether the user shared its + * phone number. The second argument, contingent upon success, will be + * an object detailing the shared contact information or a cancellation response. + */ + requestContact(callback?: (success: boolean, response: RequestContactResponse) => void): void; + /** + * A method that informs the Telegram app that the Web App is ready to be + * displayed. It is recommended to call this method as early as possible, as + * soon as all essential interface elements are loaded. Once this method is + * called, the loading placeholder is hidden and the Web App is shown. If the + * method is not called, the placeholder will be hidden only when the page is + * fully loaded. + */ + ready(): void; + /** + * A method that expands the Web App to the maximum available height. To find + * out if the Web App is expanded to the maximum height, refer to the value of + * the Telegram.WebApp.isExpanded parameter + */ + expand(): void; + /** A method that closes the Web App. */ + close(): void; +} + +/** + * Web Apps can adjust the appearance of the interface to match the Telegram + * user's app in real time. This object contains the user's current theme + * settings: + */ +interface ThemeParams { + /** + * Background color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-bg-color)`. + */ + bg_color?: string; + /** + * Main text color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-text-color)`. + */ + text_color?: string; + /** + * Hint text color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-hint-color)`. + */ + hint_color?: string; + /** + * Link color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-link-color)`. + */ + link_color?: string; + /** + * Button color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-button-color)`. + */ + button_color?: string; + /** + * Button text color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-button-text-color)`. + */ + button_text_color?: string; + /** + * **Bot API 6.1+** Secondary background color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-secondary-bg-color)`. + */ + secondary_bg_color?: string; + /** + * **Bot API 7.0+** Header background color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-header-bg-color)`. + */ + header_bg_color?: string; + /** + * **Bot API 7.0+** Accent text color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-accent-text-color)`. + */ + accent_text_color?: string; + /** + * **Bot API 7.0+** Background color for the section in the `#RRGGBB` format. It is + * recommended to use this in conjunction with *secondary_bg_color*. + * Also available as the CSS variable `var(--tg-theme-section-bg-color)`. + */ + section_bg_color?: string; + /** + * **Bot API 7.0+** Header text color for the section in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-section-header-text-color)`. + */ + section_header_text_color?: `#${string}`; + /** + * **Bot API 7.0+** Subtitle text color in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-subtitle-text-color)`. + */ + subtitle_text_color?: string; + /** + * **Bot API 7.0+** Text color for destructive actions in the `#RRGGBB` format. + * Also available as the CSS variable `var(--tg-theme-destructive-text-color)`. + */ + destructive_text_color?: string; +} + +/** + * This object describes the native popup. + */ +interface PopupParams { + /** + * The text to be displayed in the popup title, 0-64 characters. + */ + title?: string; + /** + * The message to be displayed in the body of the popup, 1-256 characters. + */ + message: string; + /** + * List of buttons to be displayed in the popup, 1-3 buttons. Set to [{“type”:“close”}] by default. + */ + buttons?: PopupButton[]; +} + +/** + * This object describes the native popup button. + */ +type PopupButton = { + /** + * Identifier of the button, 0-64 characters. Set to empty string by default. + * If the button is pressed, its id is returned in the callback and the popupClosed event. + */ + id?: string; + /** + * Type of the button. Set to default by default. + * Can be one of these values: + * - `default`, a button with the default style, + * - `ok`, a button with the localized text “OK”, + * - `close`, a button with the localized text “Close”, + * - `cancel`, a button with the localized text “Cancel”, + * - `destructive`, a button with a style that indicates a destructive action (e.g. “Remove”, “Delete”, etc.). + */ + type?: 'default' | 'ok' | 'close' | 'cancel' | 'destructive'; + /** + * The text to be displayed on the button, 0-64 characters. + * Required if type is default or destructive. Irrelevant for other types. + */ + text?: string; +} & ( + | { + type: 'default' | 'destructive'; + text: string; + } + | { + type: 'ok' | 'close' | 'cancel'; + text?: string; + } +); + +/** + * This object controls the back button, which can be displayed in the header of + * the Web App in the Telegram interface. + */ +interface BackButton { + /** + * Shows whether the button is visible. Set to false by default. + */ + isVisible: boolean; + /** + * A method that sets the button press event handler. An alias for + * Telegram.WebApp.onEvent('backButtonClicked', callback) + */ + onClick(callback: () => void): BackButton; + /** + * A method that removes the button press event handler. An alias for + * Telegram.WebApp.offEvent('backButtonClicked', callback) + */ + offClick(callback: () => void): BackButton; + /** + * A method to make the button active and visible. + */ + show(): void; + /** + * A method to hide the button. + */ + hide(): void; +} + +/** + * This object controls the main button, which is displayed at the bottom of the + * Web App in the Telegram interface. + */ +interface MainButton { + /** Current button text. Set to CONTINUE by default. */ + text: string; + /** Current button color. Set to themeParams.button_color by default. */ + color: string; + /** + * Current button text color. Set to themeParams.button_text_color by default. + */ + textColor: string; + /** Shows whether the button is visible. Set to false by default. */ + isVisible: boolean; + /** Shows whether the button is active. Set to true by default. */ + isActive: boolean; + /** Readonly. Shows whether the button is displaying a loading indicator. */ + isProgressVisible: boolean; + /** A method to set the button text. */ + setText(text: string): MainButton; + /** + * A method that sets the button press event handler. An alias for + * Telegram.WebApp.onEvent('mainButtonClicked', callback) + */ + onClick(callback: () => void): MainButton; + /** A method that deletes a previously set handler */ + offClick(callback: () => void): MainButton; + /** + * A method to make the button visible. Note that opening the Web App from the + * attachment menu hides the main button until the user interacts with the + * Web App interface. + */ + show(): MainButton; + /** A method to hide the button. */ + hide(): MainButton; + /** A method to enable the button. */ + enable(): MainButton; + /** A method to disable the button. */ + disable(): MainButton; + /** + * A method to show a loading indicator on the button. It is recommended to + * display loading progress if the action tied to the button may take a long + * time. By default, the button is disabled while the action is in progress. + * If the parameter leaveActive=true is passed, the button remains enabled. + */ + showProgress(leaveActive?: boolean): MainButton; + /** A method to hide the loading indicator. */ + hideProgress(): MainButton; + /** + * A method to set the button parameters. The params parameter is an object + * containing one or several fields that need to be changed: + * - text - button text; + * - color - button color; + * - text_color - button text color; + * - is_active - enable the button; + * - is_visible - show the button. + */ + setParams(params: MainButtonParams): MainButton; +} + +interface MainButtonParams { + /** button text */ + text?: string; + /** button color */ + color?: Color; + /** button text color */ + text_color?: Color; + /** enable the button */ + is_active?: boolean; + /** show the button */ + is_visible?: boolean; +} + +/** + * This object controls the Settings item in the context menu of the Mini App in the + * Telegram interface. + */ +interface SettingsButton { + /** + * Shows whether the context menu item is visible. Set to false by default. + */ + isVisible: boolean; + /** + * **Bot API 7.0+** A method that sets the press event handler for the Settings item in + * the context menu. An alias for + * `Telegram.WebApp.onEvent('settingsButtonClicked', callback)` + */ + onClick(callback: () => void): SettingsButton; + /** + * **Bot API 7.0+** A method that removes the press event handler from the Settings item + * in the context menu. An alias for + * `Telegram.WebApp.offEvent('settingsButtonClicked', callback)` + */ + offClick(callback: () => void): SettingsButton; + /** + * **Bot API 7.0+** A method to make the Settings item in the context menu visible. + */ + show(): SettingsButton; + /** + * **Bot API 7.0+** A method to hide the Settings item in the context menu. + */ + hide(): SettingsButton; +} + +/** + * This object controls haptic feedback. + */ +interface HapticFeedback { + /** + * A method tells that an impact occurred. The Telegram app may play the + * appropriate haptics based on style value passed. Style can be one of + * these values: + * - light, indicates a collision between small or lightweight UI objects, + * - medium, indicates a collision between medium-sized or medium-weight UI + * objects, + * - heavy, indicates a collision between large or heavyweight UI objects, + * - rigid, indicates a collision between hard or inflexible UI objects, + * - soft, indicates a collision between soft or flexible UI objects. + */ + impactOccurred(style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft'): () => void; + /** + * A method tells that a task or action has succeeded, failed, or produced a + * warning. The Telegram app may play the appropriate haptics based on type + * value passed. Type can be one of these values: + * - error, indicates that a task or action has failed, + * - success, indicates that a task or action has completed successfully, + * - warning, indicates that a task or action produced a warning. + */ + notificationOccurred(type: 'error' | 'success' | 'warning'): () => void; + /** + * A method tells that the user has changed a selection. The Telegram app + * may play the appropriate haptics. + * + * Do not use this feedback when the user makes or confirms a selection; use + * it only when the selection changes. + */ + selectionChanged(): void; +} + +interface CloudStorage { + /** + * A method that stores a value in the cloud storage using the + * specified key. + * + * @param key The key should contain 1-128 characters, only A-Z, a-z, 0-9, + * _ and - are allowed. + * @param value The value should contain 0-4096 characters. You can store + * up to 1024 keys in the cloud storage. + * @param callback If an optional callback parameter was passed, the + * callback function will be called. In case of an error, the first argument + * will contain the error. In case of success, the first argument will be + * null and the second argument will be a boolean indicating whether the + * value was stored. + */ + setItem(key: string, value: string, callback?: (error: string | null, success: null | true) => void): CloudStorage; + /** + * A method that receives a value from the cloud storage using + * the specified key. + * + * @param key The key should contain 1-128 characters, only A-Z, a-z, 0-9, + * _ and - are allowed. + * @param callback In case of an error, the callback function will + * be called and the first argument will contain the error. In case of + * success, the first argument will be null and the value will be passed + * as the second argument. + */ + getItem(key: string, callback?: (error: string | null, value: null | string) => void): CloudStorage; + /** + * A method that receives values from the cloud storage using the specified + * keys. + * + * @param key The keys should contain 1-128 characters, only A-Z, a-z, 0-9, + * _ and - are allowed. + * @param callback In case of an error, the callback? function will be + * called and the first argument will contain the error. In case of + * success, the first argument will be null and the values will be passed + * as the second argument. + */ + getItems( + keys: string[], + callback?: (error: string | null, values: null | Record) => void, + ): CloudStorage; + /** + * A method that removes a value from the cloud storage using the specified + * key. + * + * @param key The key should contain 1-128 characters, only A-Z, a-z, 0-9, + * _ and - are allowed. + * @param callback If an optional callback parameter was passed, the + * callback function will be called. In case of an error, the first + * argument will contain the error. In case of success, the first + * argument will be null and the second argument will be a boolean + * indicating whether the value was removed. + */ + removeItem(key: string, callback?: (error: string | null, success: null | true) => void): CloudStorage; + /** + * A method that removes values from the cloud storage using the specified + * keys. + * + * @param key The keys should contain 1-128 characters, only A-Z, a-z, 0-9, + * _ and - are allowed. + * @param callback If an optional callback parameter was passed, the + * callback function will be called. In case of an error, the first + * argument will contain the error. In case of success, the first + * argument will be null and the second argument will be a boolean + * indicating whether the values were removed. + */ + removeItems(keys: string[], callback?: (error: string | null, success: null | true) => void): CloudStorage; + /** + * A method that receives the list of all keys stored in the cloud storage. + * + * @param callback In case of an error, the callback function will be called + * and the first argument will contain the error. In case of success, the + * first argument will be null and the list of keys will be passed as the + * second argument. + */ + getKeys(callback?: (error: string | null, keys: null | string[]) => void): CloudStorage; +} + +/** + * This object contains data that is transferred to the Web App when it is + * opened. It is empty if the Web App was launched from a keyboard button. + */ +interface WebAppInitData { + /** + * A unique identifier for the Web App session, required for sending messages + * via the answerWebAppQuery method. + */ + query_id?: string; + /** An object containing data about the current user. */ + user?: WebAppUser; + /** + * An object containing data about the chat partner of the current user in the + * chat where the bot was launched via the attachment menu. Returned only for + * Web Apps launched via the attachment menu. + */ + receiver?: WebAppUser; + /** + * An object containing data about the chat where the bot was launched via + * the attachment menu. Returned for supergroups, channels and group chats – + * only for Web Apps launched via the attachment menu. + */ + chat?: WebAppChat; + /** + * Type of the chat from which the Web App was opened. + * Can be either “sender” for a private chat with the user opening the link, + * “private”, “group”, “supergroup”, or “channel”. + * Returned only for Web Apps launched from direct links. + */ + chat_type?: 'sender' | 'private' | 'group' | 'supergroup' | 'channel'; + /** + * Global identifier, uniquely corresponding to the chat from which the Web App was opened. + * Returned only for Web Apps launched from a direct link. + */ + chat_instance?: string; + /** + * The value of the startattach parameter, passed via link. Only returned for + * Web Apps when launched from the attachment menu via link. The value of the + * start_param parameter will also be passed in the GET-parameter + * tgWebAppStartParam, so the Web App can load the correct interface right + * away. + */ + start_param?: string; + /** + * Time in seconds, after which a message can be sent via the + * answerWebAppQuery method. + */ + can_send_after?: number; + /** Unix time when the form was opened. */ + auth_date: number; + /** + * A hash of all passed parameters, which the bot server can use to check + * their validity. + */ + hash: string; +} + +/** This object contains the data of the Web App user. */ +interface WebAppUser { + /** + * A unique identifier for the user or bot. This number may have more than 32 + * significant bits and some programming languages may have difficulty/silent + * defects in interpreting it. It has at most 52 significant bits, so a 64-bit + * integer or a double-precision float type is safe for storing this + * identifier. + */ + id: number; + /** True, if this user is a bot. Returns in the receiver field only. */ + is_bot?: boolean; + /** First name of the user or bot. */ + first_name: string; + /** Last name of the user or bot. */ + last_name?: string; + /** Username of the user or bot. */ + username?: string; + /** IETF language tag of the user's language. Returns in user field only. */ + language_code?: string; + /** True, if this user is a Telegram Premium user. */ + is_premium?: true; + /** True, if this user added the bot to the attachment menu. */ + added_to_attachment_menu?: true; + /** True, if this user allowed the bot to message them. */ + allows_write_to_pm?: true; + /** + * URL of the user’s profile photo. The photo can be in .jpeg or .svg formats. + * Only returned for Web Apps launched from the attachment menu. + */ + photo_url?: string; +} + +/** + * This object represents a chat. + */ +interface WebAppChat { + /** + * Unique identifier for this chat. This number may have more than 32 + * significant bits and some programming languages may have + * difficulty/silent defects in interpreting it. But it has at most 52 + * significant bits, so a signed 64-bit integer or double-precision float + * type are safe for storing this identifier. + */ + id: number; + /** + * Type of chat, can be either “group”, “supergroup” or “channel” + */ + type: 'group' | 'supergroup' | 'channel'; + /** + * Title of the chat + */ + title: string; + /** + * Username of the chat + */ + username?: string; + /** + * URL of the chat’s photo. The photo can be in .jpeg or .svg formats. Only + * returned for Web Apps launched from the attachment menu. + */ + photo_url?: string; +} + +/** + * This object describes the native popup for scanning QR codes. + */ +interface ScanQrPopupParams { + /** + * The text to be displayed under the 'Scan QR' heading, 0-64 characters. + */ + text?: string; +} + +/** + * This object describes contact information shared when requestContact was approved by the user. + */ +interface RequestContactResponseSent { + /** Status 'sent' indicates that contact information has been shared. */ + status: 'sent'; + /** A status message or result as a string. */ + response: string; + /** Contains sensitive information shared upon user consent. WARNING: Data from + * this field should not be trusted. You should only use data from `response` on + * the bot's server and only after it has been validated. */ + responseUnsafe: { + /** Authorization date for sharing contact information. */ + auth_date: string; + /** Object holding user's contact details. */ + contact: { + /** User's first name. */ + first_name: string; + /** Optional. User's last name. */ + last_name?: string; + /** User's phone number. */ + phone_number: string; + /** Unique identifier of the user. */ + user_id: number; + }; + /** Hash to verify data authenticity. */ + hash: string; + }; +} + +/** + * This object only contains a status to indicate the cancellation. + */ +interface RequestContactResponseCancelled { + /** Status 'cancelled', indicates that user cancelled the contact share request. */ + status: 'cancelled'; +} + +type RequestContactResponse = RequestContactResponseSent | RequestContactResponseCancelled; diff --git a/src/types/wallet.ts b/src/types/wallet.ts new file mode 100644 index 00000000..dd877c00 --- /dev/null +++ b/src/types/wallet.ts @@ -0,0 +1,60 @@ +import type { Accounts, IPortkeyProvider } from '@portkey/provider-types'; +import { AElfDappBridge } from '@aelf-react/types'; + +export enum PortkeyVersion { + v1 = 'v1', + v2 = 'v2', +} + +export type TAelfAccounts = { + AELF?: string; + tDVV?: string; + tDVW?: string; +}; + +export interface WebLoginWalletInfo { + name?: string; + address: string; + extraInfo: ExtraInfoForDiscover | ExtraInfoForPortkeyAA | ExtraInfoForNightElf; +} + +export interface ExtraInfoForDiscover { + accounts: Accounts; + nickName: string; + provider: IPortkeyProvider; +} + +export interface ExtraInfoForPortkeyAA { + publicKey: string; + portkeyInfo: { + accounts: TAelfAccounts; + nickName: string; + }; +} + +export interface ExtraInfoForNightElf { + publicKey: string; + nightElfInfo: { + name: string; + // appPermission; + defaultAElfBridge?: AElfDappBridge; + aelfBridges?: { + [key: string]: AElfDappBridge; + }; + nodes?: { + [key: string]: TAelfNode; + }; + }; +} +export declare type TAelfNode = { + rpcUrl: string; + chainId: string; +}; + +export enum WalletTypeEnum { + EVM = 'EVM', + SOL = 'Solana', + TON = 'TON', + TRON = 'TRON', + AELF = 'Aelf', +} diff --git a/src/utils/__tests__/aelfAuthToken.test.ts b/src/utils/__tests__/aelfAuthToken.test.ts new file mode 100644 index 00000000..41d8c58f --- /dev/null +++ b/src/utils/__tests__/aelfAuthToken.test.ts @@ -0,0 +1,227 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { + getAuthPlainText, + getLocalJWT, + resetLocalJWT, + removeOneLocalJWT, + setLocalJWT, + LocalJWTData, +} from '../aelfAuthToken'; +import { LocalStorageKey } from 'constants/storages'; + +// Mock localStorage +const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: vi.fn((key: string) => store[key] || null), + setItem: vi.fn((key: string, value: string) => { + store[key] = value.toString(); + }), + removeItem: vi.fn((key: string) => { + delete store[key]; + }), + clear: vi.fn(() => { + store = {}; + }), + }; +})(); + +// Attach the mock to the global window object +Object.defineProperty(window, 'localStorage', { + value: localStorageMock, + writable: true, +}); + +describe('JWT Local Storage Utility Functions', () => { + const TEST_KEY = 'test_key'; + const SAMPLE_DATA: LocalJWTData = { + access_token: 'test_token', + expires_in: 3600, // 1 hour in seconds + token_type: 'Bearer', + expiresTime: Date.now() + 3600 * 1000, // 1 hour in milliseconds + }; + + beforeEach(() => { + // Reset localStorage mock and clear all mocks + localStorageMock.clear(); + vi.clearAllMocks(); + vi.useFakeTimers(); // Use fake timers for time-based tests + }); + + afterEach(() => { + vi.useRealTimers(); // Restore real timers after each test + }); + + describe('getLocalJWT', () => { + it('should return undefined when no data is stored', () => { + expect(getLocalJWT(TEST_KEY)).toBeUndefined(); + }); + + it('should return undefined when data is expired', () => { + const expiredData = { + ...SAMPLE_DATA, + expiresTime: Date.now() - 1000, // Expired 1 second ago + }; + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify({ [TEST_KEY]: expiredData })); + + expect(getLocalJWT(TEST_KEY)).toBeUndefined(); + }); + + it('should return undefined when remaining time is less than half a day', () => { + const nearlyExpiredData = { + ...SAMPLE_DATA, + expiresTime: Date.now() + 0.4 * 24 * 60 * 60 * 1000, // 0.4 days remaining + }; + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify({ [TEST_KEY]: nearlyExpiredData })); + + expect(getLocalJWT(TEST_KEY)).toBeUndefined(); + }); + + it('should return valid data when conditions are met', () => { + const validData = { + ...SAMPLE_DATA, + expiresTime: Date.now() + 2 * 24 * 60 * 60 * 1000, // 2 days remaining + }; + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify({ [TEST_KEY]: validData })); + + const result = getLocalJWT(TEST_KEY); + expect(result).toMatchObject(validData); + }); + + it('should handle JSON parsing errors gracefully', () => { + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, 'invalid_json'); + + expect(getLocalJWT(TEST_KEY)).toBeUndefined(); + }); + }); + + describe('setLocalJWT', () => { + it('should create a new entry if no data exists', () => { + setLocalJWT(TEST_KEY, SAMPLE_DATA); + + const storedData = JSON.parse(localStorageMock.getItem(LocalStorageKey.ACCESS_TOKEN)!); + expect(storedData[TEST_KEY].access_token).toBe(SAMPLE_DATA.access_token); + }); + + it('should update an existing entry if data exists', () => { + const initialData = { other_key: SAMPLE_DATA }; + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify(initialData)); + + setLocalJWT(TEST_KEY, SAMPLE_DATA); + + const storedData = JSON.parse(localStorageMock.getItem(LocalStorageKey.ACCESS_TOKEN)!); + expect(storedData).toHaveProperty(TEST_KEY); + expect(storedData).toHaveProperty('other_key'); + }); + + it('should calculate expiration time correctly', () => { + const testData = { ...SAMPLE_DATA, expires_in: 7200 }; // 2 hours + setLocalJWT(TEST_KEY, testData); + + const storedData = JSON.parse(localStorageMock.getItem(LocalStorageKey.ACCESS_TOKEN)!); + const expectedExpiresTime = Date.now() + (7200 - 10) * 1000; // 10 seconds buffer + expect(storedData[TEST_KEY].expiresTime).toBeCloseTo(expectedExpiresTime, -3); // Allow 1-second tolerance + }); + }); + + describe('removeOneLocalJWT', () => { + it('should remove the specified key from storage', () => { + const initialData = { + [TEST_KEY]: SAMPLE_DATA, + other_key: SAMPLE_DATA, + }; + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify(initialData)); + + removeOneLocalJWT(TEST_KEY); + + const storedData = JSON.parse(localStorageMock.getItem(LocalStorageKey.ACCESS_TOKEN)!); + expect(storedData).not.toHaveProperty(TEST_KEY); + expect(storedData).toHaveProperty('other_key'); + }); + + it('should handle empty storage gracefully', () => { + expect(() => removeOneLocalJWT(TEST_KEY)).not.toThrow(); + }); + }); + + describe('resetLocalJWT', () => { + it('should clear all stored JWT data', () => { + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify({ [TEST_KEY]: SAMPLE_DATA })); + + resetLocalJWT(); + + expect(localStorageMock.getItem(LocalStorageKey.ACCESS_TOKEN)).toBeNull(); + }); + }); +}); + +describe('getAuthPlainText', () => { + const mockTimestamp = 1680000000000; + + it('should generate plainTextOrigin with the expected format', () => { + // Mock the Date.now() method so the value of the nonce is predictable + vi.spyOn(Date, 'now').mockReturnValue(mockTimestamp); + + const result = getAuthPlainText(); + + // Verify plainTextOrigin content + expect(result.plainTextOrigin).toContain('Welcome to eBridge!'); + expect(result.plainTextOrigin).toContain('Click to sign in and accept the eBridge Terms of Service'); + expect(result.plainTextOrigin).toContain( + 'https://ebridge-exchange.gitbook.io/docs/more-information/terms-of-service', + ); + expect(result.plainTextOrigin).toContain( + 'https://ebridge-exchange.gitbook.io/docs/more-information/privacy-policy', + ); + expect(result.plainTextOrigin).toContain( + 'This request will not trigger a blockchain transaction or cost any gas fees.', + ); + expect(result.plainTextOrigin).toContain(`Nonce:\n${mockTimestamp}`); + + // Restore original Date.now() function + vi.restoreAllMocks(); + }); + + it('should generate plainTextHex as a correct hexadecimal representation of plainTextOrigin', () => { + // Mock the Date.now() method to control the output of the nonce + vi.spyOn(Date, 'now').mockReturnValue(mockTimestamp); + + const result = getAuthPlainText(); + + // Convert plainTextOrigin to hex manually to compare + const expectedHex = Buffer.from(result.plainTextOrigin).toString('hex').replace('0x', ''); + expect(result.plainTextHex).toBe(expectedHex); + + // Restore original Date.now() function + vi.restoreAllMocks(); + }); + + it('should include the correct nonce (current timestamp) in the plainTextOrigin', () => { + // Mock the Date.now() method to control the output of the nonce + vi.spyOn(Date, 'now').mockReturnValue(mockTimestamp); + + const result = getAuthPlainText(); + + // Verify that the nonce matches the mocked timestamp + expect(result.plainTextOrigin).toContain(`Nonce:\n${mockTimestamp}`); + + // Restore original Date.now() function + vi.restoreAllMocks(); + }); + + it('should have a consistent plainTextHex length for the same input', () => { + // Mock the Date.now() to generate a fixed timestamp + vi.spyOn(Date, 'now').mockReturnValue(mockTimestamp); + + const result1 = getAuthPlainText(); + const result2 = getAuthPlainText(); + + // Both should have the same hex length because the same input is generated + expect(result1.plainTextHex.length).toBe(result2.plainTextHex.length); + + // Restore original Date.now() function + vi.restoreAllMocks(); + }); +}); diff --git a/src/utils/__tests__/aelfUtils.test.ts b/src/utils/__tests__/aelfUtils.test.ts new file mode 100644 index 00000000..4887baf1 --- /dev/null +++ b/src/utils/__tests__/aelfUtils.test.ts @@ -0,0 +1,297 @@ +import { SupportedELFChainId } from 'constants/chain'; +import { ChainId } from 'types'; +import { + getNodeByChainId, + getAElf, + recoverPubKey, + pubKeyToAddress, + getRawTx, + transformArrayToMap, + isElfChainSymbol, + isELFChain, + getELFAddress, +} from 'utils/aelfUtils'; +import { isSymbol } from 'utils/reg'; +import { beforeEach, describe, it, expect, Mock } from 'vitest'; + +// Mock `isSymbol` +vi.mock('utils/reg', () => ({ + isSymbol: vi.fn(), + isValidBase58: vi.fn(), +})); + +const plainText = '4e6f6e63653a31373136333538353032393233'; +const signature = + '05d641d117822f42f25278d8893f3a1ba9a36c32590080f84fe1d1095712819d223c3b3fa0c1de8f62b9b6ac89992c7e2f09678ad01eef1e97b0f21d6c6bd49c01'; +const pubkey = + '04671bfc20edb4cdc171bd7d20877aa64862e88dc9f52173673db9789e0dea71aca45472fd4841cad362cae8b5b6f05c55a350014f7917fe90870fd680c845edae'; +const managerAddress = '7iC6EQtt4rKsqv9vFiwpUDvZVipSoKwvPLy7pRG189qJjyVT7'; +const correctAelfAddress = 'ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_AELF'; + +describe('getNodeByChainId', () => { + it('should return correct AELF chain node', () => { + const result = getNodeByChainId('AELF' as ChainId); + + expect(result).toHaveProperty('chainId', 'AELF'); + expect(result).toHaveProperty('exploreUrl'); + expect(result).toHaveProperty('rpcUrl'); + expect(result.exploreUrl).contain('aelfscan.io/AELF'); + expect(result.rpcUrl).contain('node.aelf.io'); + }); + + it('should return undefined if input is not aelf chain', () => { + const result = getNodeByChainId('ETH' as any); + + expect(result).toBeUndefined(); + }); +}); + +describe('getAElf', () => { + it('should return correct AELF chain node', () => { + const result = getAElf('AELF' as ChainId); + + expect(result).toHaveProperty('chain'); + }); +}); + +describe('recoverPubKey', () => { + it('recover pubkey by signature', () => { + const result = recoverPubKey(plainText, signature) + ''; + + expect(result).toBe(pubkey); + }); +}); + +describe('pubKeyToAddress', () => { + it('recover manager address by pubkey', () => { + const result = pubKeyToAddress(pubkey); + + // Assertions + expect(result).toBe(managerAddress); + }); +}); + +describe('getRawTx', () => { + it('Input blockHash is not start with 0x.', () => { + const result = getRawTx({ + blockHeight: 120594862, + blockHash: 'b093de3b6a52fefb691228922b18f9c300fd2ed7f3838d10f6c5024c289bc7a5', + packedInput: + '0a220a20ba5f748422c4d882b664077f7bbdacbf5b2ce18da880cd4e6946a2eb2267dffd12220a2099ad42e68447aa3cab9b31e85531d09d5cdf5d5b8bb60e6b30941d78d32d09b91a0d5472616e73666572546f6b656e220a0a03454c4610c0e2b069', + account: 'V922RLhmJsmGHCW6zPDR7czgNYfihnAe175somxFdqXKKjweP', + contractAddress: '238X6iw1j8YKcHvkDYVtYVbuYk2gJnK8UoNpVCtssynSpVC8hb', + methodName: 'ManagerForwardCall', + }); + expect(result).toBeTruthy(); + }); + + it('Input blockHash is start with 0x.', () => { + const result = getRawTx({ + blockHeight: 120594862, + blockHash: '0xb093de3b6a52fefb691228922b18f9c300fd2ed7f3838d10f6c5024c289bc7a5', + packedInput: + '0a220a20ba5f748422c4d882b664077f7bbdacbf5b2ce18da880cd4e6946a2eb2267dffd12220a2099ad42e68447aa3cab9b31e85531d09d5cdf5d5b8bb60e6b30941d78d32d09b91a0d5472616e73666572546f6b656e220a0a03454c4610c0e2b069', + account: 'V922RLhmJsmGHCW6zPDR7czgNYfihnAe175somxFdqXKKjweP', + contractAddress: '238X6iw1j8YKcHvkDYVtYVbuYk2gJnK8UoNpVCtssynSpVC8hb', + methodName: 'ManagerForwardCall', + }); + expect(result).toBeTruthy(); + }); +}); + +describe('transformArrayToMap', () => { + it('should return an empty string when origin is null or empty', () => { + expect(transformArrayToMap(null, null as any)).toBe(''); + expect(transformArrayToMap({}, [])).toBe(''); + + expect(transformArrayToMap(undefined, ['address123'])).toEqual(['address123']); + expect(transformArrayToMap({}, ['address123'])).toEqual(['address123']); + }); + + it('should return origin if it is not an array', () => { + const input = { key: 'value' } as any; + + expect(transformArrayToMap(null, input)).toBe(input); + }); + + it('should return the first element of origin if inputType is an Address or Hash', () => { + const inputTypeAddress = { name: 'Address', fieldsArray: [{ type: 'bytes' }] }; + + expect(transformArrayToMap(inputTypeAddress, ['address123'])).toBe('address123'); + + const inputTypeHash = { name: 'Hash', fieldsArray: [{ type: 'bytes' }] }; + + expect(transformArrayToMap(inputTypeHash, ['hash123'])).toBe('hash123'); + }); + + it('should return false if inputType.fieldsArray length===2', () => { + const inputTypeAddress = { + name: 'Address', + fieldsArray: [{ type: 'bytes' }, { type: 'string' }], + }; + + expect(transformArrayToMap(inputTypeAddress, ['address123'])).toEqual({ + undefined: undefined, + }); + }); + + it('should return origin if inputType.fieldsArray is empty', () => { + const inputType = { name: 'CustomType', fieldsArray: [] }; + const origin = ['value1', 'value2']; + + expect(transformArrayToMap(inputType, origin)).toEqual(origin); + }); + + it('should return a map with one key-value pair if inputType.fieldsArray has one field', () => { + const inputType = { fieldsArray: [{ name: 'key1', type: 'string' }] }; + const origin = ['value1']; + + expect(transformArrayToMap(inputType, origin)).toEqual({ key1: 'value1' }); + }); + + it('should map origin to a map when fieldsArray has multiple fields', () => { + const inputType = { + fieldsArray: [ + { name: 'key1', type: 'string' }, + { name: 'key2', type: 'int' }, + ], + }; + const origin = ['value1', 42]; + + expect(transformArrayToMap(inputType, origin)).toEqual({ + key1: 'value1', + key2: 42, + }); + }); + + it('should handle missing keys gracefully when fieldsArray is larger than origin', () => { + const inputType = { + fieldsArray: [ + { name: 'key1', type: 'string' }, + { name: 'key2', type: 'int' }, + ], + }; + const origin = ['value1']; + + expect(transformArrayToMap(inputType, origin)).toEqual({ + key1: 'value1', + key2: undefined, + }); + }); +}); + +describe('isElfChainSymbol', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + }); + + it('should return the symbol when it meets length conditions and passes `isSymbol`', () => { + // Mock `isSymbol` to return true + (isSymbol as Mock).mockReturnValue(true); + + const validSymbol = 'ELF'; + const result = isElfChainSymbol(validSymbol); + + // Assertions + expect(result).toBe(validSymbol); + expect(isSymbol).toHaveBeenCalledWith(validSymbol); // Ensure `isSymbol` was called + }); + + it('should return false if the symbol fails the `isSymbol` test', () => { + // Mock `isSymbol` to return false + (isSymbol as Mock).mockReturnValue(false); + + const invalidSymbol = 'INVALID'; + const result = isElfChainSymbol(invalidSymbol); + + // Assertions + expect(result).toBe(false); + expect(isSymbol).toHaveBeenCalledWith(invalidSymbol); // Ensure `isSymbol` was called + }); + + it('should return false for symbols shorter than 2 characters', () => { + // Mock `isSymbol` to return true + (isSymbol as Mock).mockReturnValue(true); + + const shortSymbol = 'E'; // Too short + const result = isElfChainSymbol(shortSymbol); + + // Assertions + expect(result).toBe(false); + expect(isSymbol).not.toHaveBeenCalled(); // Ensure `isSymbol` is not called + }); + + it('should return false for symbols longer than 10 characters', () => { + // Mock `isSymbol` to return true + (isSymbol as Mock).mockReturnValue(true); + + const longSymbol = 'TOOLONGSYMBOL'; // Too long + const result = isElfChainSymbol(longSymbol); + + // Assertions + expect(result).toBe(false); + expect(isSymbol).not.toHaveBeenCalled(); // Ensure `isSymbol` is not called + }); + + it('should return false for invalid inputs (null, undefined, or empty string)', () => { + // Ensure `isSymbol` is never called in these cases + expect(isElfChainSymbol(null)).toBe(false); + expect(isElfChainSymbol(undefined)).toBe(false); + expect(isElfChainSymbol('')).toBe(false); + expect(isSymbol).not.toHaveBeenCalled(); // `isSymbol` should not be called + }); +}); + +describe('isELFChain', () => { + it('should return true for valid ELF chain IDs', () => { + expect(isELFChain('AELF' as SupportedELFChainId)).toBe(true); + expect(isELFChain('tDVW' as SupportedELFChainId)).toBe(true); + }); + + it('should return false for invalid chain IDs', () => { + // Not a valid chainId in SupportedELFChainId + expect(isELFChain('unknown' as any)).toBe(false); + // Empty string + expect(isELFChain('' as any)).toBe(false); + }); + + it('should return false for non-string chain IDs', () => { + expect(isELFChain(undefined)).toBe(false); // undefined input + }); +}); + +describe('getELFAddress', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + }); + + it('should return the ELF address when valid', () => { + const result = getELFAddress(correctAelfAddress); + + // Assertions + expect(result).toBe('Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft'); + }); + + it('should return undefined if it it not aelf address', () => { + const input = 'part1_InvalidAddress_part2'; + const result = getELFAddress(input); + + // Assertions + expect(result).toBeUndefined(); + }); + + it('should return undefined for improperly formatted address', () => { + // Input with fewer than 3 parts + expect(getELFAddress('part1_part2')).toBeUndefined(); + expect(getELFAddress('part1')).toBeUndefined(); + + // Input with more than 3 parts + expect(getELFAddress('extra_part1_ELFAddress_part2_part3')).toBeUndefined(); + }); + + it('should return undefined when input is null, undefined, or empty', () => { + expect(getELFAddress(null as unknown as string)).toBeUndefined(); + expect(getELFAddress(undefined)).toBeUndefined(); + expect(getELFAddress('')).toBeUndefined(); + }); +}); diff --git a/src/utils/__tests__/aelfUtilsWithMock.test.ts b/src/utils/__tests__/aelfUtilsWithMock.test.ts new file mode 100644 index 00000000..f5d5ddfe --- /dev/null +++ b/src/utils/__tests__/aelfUtilsWithMock.test.ts @@ -0,0 +1,997 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { afterEach, beforeEach, describe, it, expect, Mock } from 'vitest'; +import { + approveELF, + checkElfAllowanceAndApprove, + encodedTransfer, + encodeTransaction, + getAElf, + getBlockHeight, + getContractFileDescriptorSet, + getSerializedDataFromLog, + getServicesFromFileDescriptors, + getTxResult, + getWallet, + messageHTML, + MessageTxToExplore, + uint8ArrayToHex, + getContractMethods, + initContracts, +} from 'utils/aelfUtils'; +import { ChainId } from 'types'; +import { isMobileDevices } from '../isMobile'; +import { getExploreLink } from 'utils'; +import { message } from 'antd'; +import { timesDecimals } from '../calculate'; +import BigNumber from 'bignumber.js'; +import AElf from 'aelf-sdk'; +import descriptor from '@aelfqueen/protobufjs/ext/descriptor'; +import storages from 'constants/storages'; +import { baseRequest } from 'api'; + +// Mock localStorage +export const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: vi.fn((key: string) => store[key] || null), + setItem: vi.fn((key: string, value: string) => { + store[key] = value.toString(); + }), + removeItem: vi.fn((key: string) => { + delete store[key]; + }), + clear: vi.fn(() => { + store = {}; + }), + }; +})(); + +// Attach the mock to the global window object +Object.defineProperty(window, 'localStorage', { + value: localStorageMock, + writable: true, +}); + +vi.mock('utils', async (importOriginal) => { + const originalModule: any = await importOriginal(); + + return { + ...originalModule, + getExploreLink: vi.fn(), + shortenString: vi.fn(), + sleep: vi.fn(), + }; +}); + +vi.mock('../calculate', async (importOriginal) => { + const originalModule: any = await importOriginal(); + + return { + ...originalModule, + timesDecimals: vi.fn(), + }; +}); + +// Mock AElf class and providers.HttpProvider +vi.mock('aelf-sdk', async () => { + const HttpProviderMock = vi.fn().mockImplementation((rpc) => ({ + rpc: rpc, + })); + + const AElfMock: any = vi.fn().mockImplementation((provider) => ({ + provider: provider, + chain: { + getTxResult: vi.fn(), + getBlockHeight: vi.fn(), + }, + pbUtils: { + getSerializedDataFromLog: vi.fn(), + encodeTransaction: vi.fn(), + }, + utils: { + uint8ArrayToHex: vi.fn(), + transform: { + transformMapToArray: vi.fn(), + transform: vi.fn(), + }, + }, + pbjs: { + Root: { + fromDescriptor: vi.fn(), + }, + }, + })); + + AElfMock.providers = { + HttpProvider: HttpProviderMock, + }; + + AElfMock.wallet = { + createNewWallet: vi.fn().mockImplementation(() => { + return { address: managerAddress }; + }), + }; + + AElfMock.chain = { + getTxResult: vi.fn(), + getBlockHeight: vi.fn(), + }; + + AElfMock.pbUtils = { + getSerializedDataFromLog: vi.fn(), + encodeTransaction: vi.fn(), + }; + + AElfMock.utils = { + uint8ArrayToHex: vi.fn(), + transform: { + transformMapToArray: vi.fn(), + transform: vi.fn(), + }, + }; + + AElfMock.pbjs = { + Root: { + fromDescriptor: vi.fn(), + }, + }; + + return { + default: AElfMock, + }; +}); + +vi.mock('@aelfqueen/protobufjs/ext/descriptor', () => { + return { + default: { + FileDescriptorSet: { + decode: vi.fn(), + }, + }, + }; +}); + +vi.mock('api', () => { + return { + baseRequest: vi.fn(), + }; +}); + +vi.mock('../isMobile', () => ({ + isMobileDevices: vi.fn(), +})); + +// Mock the message component +vi.mock('antd', async () => { + const antd = await vi.importActual('antd'); + return { + ...antd, + message: { + success: vi.fn(), + error: vi.fn(), + info: vi.fn(), + warning: vi.fn(), + loading: vi.fn(), + destroy: vi.fn(), + }, + }; +}); + +vi.mock('components/CommonMessage', () => { + return { + default: { + error: vi.fn(), + }, + }; +}); + +const managerAddress = '7iC6EQtt4rKsqv9vFiwpUDvZVipSoKwvPLy7pRG189qJjyVT7'; + +describe('getWallet', () => { + it('should return wallet info', () => { + const result = getWallet(); + + expect(result).toHaveProperty('address', managerAddress); + }); +}); + +const TransactionId = 'b40236195c71dd207753a336ed0fd2da1263a9ba527457422a6c9608dee9e775'; +const successTxHash = TransactionId; +const errorTxHash = 'b40236195c71dd207753a336ed0fd'; +const mainChainSuccessExploreUrl = `https://testnet.aelfscan.io/AELF/tx/${successTxHash}`; +const mainChainErrorExploreUrl = `https://testnet.aelfscan.io/AELF/tx/${errorTxHash}`; +const chainId = 'AELF' as ChainId; + +describe('Message Utility Functions ', () => { + describe('messageHTML', () => { + afterEach(() => { + // Clean up all messages after each test + message.destroy(); + vi.clearAllMocks(); + }); + + it('should call message.success correctly if type is success', () => { + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(true); + (getExploreLink as Mock).mockReturnValue(mainChainSuccessExploreUrl); + + const transactionMessage = 'Transaction success'; + + messageHTML(successTxHash, 'success', transactionMessage); + + expect(isMobileDevices).toHaveBeenCalled(); + expect(message.success).toHaveBeenCalled(); + }); + + it('should call message.error correctly if type is error', () => { + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(false); + (getExploreLink as Mock).mockReturnValue(mainChainErrorExploreUrl); + + const transactionMessage = 'AElf.Sdk.CSharp.AssertionException: Transaction failed'; + + messageHTML(errorTxHash, 'error', transactionMessage); + + expect(isMobileDevices).toHaveBeenCalled(); + expect(message.error).toHaveBeenCalled(); + }); + + it('should call message.warning correctly if txHash is empty string', () => { + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(false); + (getExploreLink as Mock).mockReturnValue(mainChainErrorExploreUrl); + + const transactionMessage = 'AElf.Sdk.CSharp.AssertionException: Not find transaction hash'; + + messageHTML('', 'warning', transactionMessage); + + expect(isMobileDevices).toHaveBeenCalled(); + expect(message.warning).toHaveBeenCalled(); + }); + }); + + describe('MessageTxToExplore', () => { + afterEach(() => { + // Clean up all messages after each test + message.destroy(); + + // Clear mocks before each test + vi.clearAllMocks(); + }); + + it('should call message.success when getTxResult succeeds', async () => { + const status = 'mined'; + const txResult = { Status: status, TransactionId: successTxHash }; + + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return txResult; + }); + + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(true); + (getExploreLink as Mock).mockReturnValue(mainChainSuccessExploreUrl); + + await MessageTxToExplore(chainId, successTxHash, 'success'); + + expect(message.success).toHaveBeenCalled(); + }); + + it('should call message.error when getTxResult throws error', async () => { + // Modify the mock implementation of AElf.chain.getTxResult + const errorMessage = 'Transaction failed'; + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async (txId: string) => { + return { + txId, + status: 'failed', + error: '500', + errorMessage: { + message: { + error: errorMessage, + TransactionId: successTxHash, + }, + }, + }; + }); + + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(false); + (getExploreLink as Mock).mockReturnValue(mainChainErrorExploreUrl); + + await MessageTxToExplore(chainId, errorTxHash, 'error'); + + expect(message.error).toHaveBeenCalled(); + }); + + it('should call message.error when getTxResult throws undefined error', async () => { + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async (txId: string) => { + return { + txId, + status: 'failed', + error: '500', + errorMessage: { + message: undefined, + }, + }; + }); + + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(false); + (getExploreLink as Mock).mockReturnValue(mainChainErrorExploreUrl); + + await MessageTxToExplore(chainId, errorTxHash, 'error'); + + expect(message.error).toHaveBeenCalled(); + }); + }); +}); + +describe('approveELF', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + + // Modify the mock implementation of AElf.chain.getTxResult + const status = 'mined'; + const txResult = { Status: status, TransactionId: successTxHash }; + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return txResult; + }); + + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(true); + (getExploreLink as Mock).mockReturnValue(mainChainSuccessExploreUrl); + }); + + it('should return true when approve succeeds', async () => { + const mockTokenContract = { callSendMethod: vi.fn().mockResolvedValue(TransactionId) } as any; + + const result = await approveELF('address', mockTokenContract, 'ELF', '100', chainId); + + expect(result).toBe(true); + expect(message.success).toHaveBeenCalled(); + }); + + it('should return true when approve succeeds with result.TransactionId property', async () => { + const mockTokenContract = { callSendMethod: vi.fn().mockResolvedValue({ result: TransactionId }) } as any; + + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(true); + (getExploreLink as Mock).mockReturnValue(mainChainSuccessExploreUrl); + + const result = await approveELF('address', mockTokenContract, 'ELF', '100', chainId); + + expect(result).toBe(true); + expect(message.success).toHaveBeenCalled(); + }); + + it('should return false when approve failed with error.message property', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue({ error: { message: 'Approve Failed' } }), + } as any; + + const result = await approveELF('address', mockTokenContract, 'ELF', '100', chainId); + + expect(result).toBe(false); + }); + + it('should return false when approve failed with errorMessage.message property', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue({ error: '500', errorMessage: { message: 'Approve Failed' } }), + } as any; + + const result = await approveELF('address', mockTokenContract, 'ELF', '100', chainId); + + expect(result).toBe(false); + }); + + it('should return false when approve failed with errorMessage property', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue({ error: '500', errorMessage: 'Approve Failed' }), + } as any; + + const result = await approveELF('address', mockTokenContract, 'ELF', '100', chainId); + + expect(result).toBe(false); + }); +}); + +describe('checkElfAllowanceAndApprove', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + + // Modify the mock implementation of AElf.chain.getTxResult + const status = 'mined'; + const txResult = { Status: status, TransactionId: successTxHash }; + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return txResult; + }); + + // Mock isMobileDevices and getExploreLink method + (isMobileDevices as Mock).mockReturnValue(true); + (getExploreLink as Mock).mockReturnValue(mainChainSuccessExploreUrl); + }); + + it('should check allowance and approve successfully', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue(TransactionId), + callViewMethod: vi.fn().mockResolvedValue({ allowance: '100' }), + } as any; + + (timesDecimals as Mock).mockReturnValue(new BigNumber(1000)); + + const result = await checkElfAllowanceAndApprove( + mockTokenContract, + 'ELF', + 'address', + 'approveTargetAddress', + '100', + chainId, + ); + + expect(result).toBe(true); + }); + + it('should check allowance and approve successfully if allowance is lt amount', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue(TransactionId), + callViewMethod: vi.fn().mockResolvedValue({ allowance: '10' }), + } as any; + + (timesDecimals as Mock).mockReturnValue(new BigNumber(1)); + + const result = await checkElfAllowanceAndApprove( + mockTokenContract, + 'ELF', + 'address', + 'approveTargetAddress', + '100', + chainId, + ); + + expect(result).toBe(true); + }); + + it('should return false when GetAllowance failed with error.message property', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue({ error: { message: 'Approve Failed' } }), + callViewMethod: vi.fn().mockResolvedValue({ error: { message: 'GetAllowance Failed' } }), + } as any; + + const result = await checkElfAllowanceAndApprove( + mockTokenContract, + 'ELF', + 'address', + 'approveTargetAddress', + '100', + chainId, + ); + + expect(result).toBe(false); + }); + + it('should return false when GetAllowance failed with errorMessage.message property', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue({ error: '500', errorMessage: { message: 'Approve failed' } }), + callViewMethod: vi.fn().mockResolvedValue({ error: '500', errorMessage: { message: 'GetAllowance Failed' } }), + } as any; + + const result = await checkElfAllowanceAndApprove( + mockTokenContract, + 'ELF', + 'address', + 'approveTargetAddress', + '100', + chainId, + ); + + expect(result).toBe(false); + }); + + it('should return false when GetAllowance failed with errorMessage property', async () => { + const mockTokenContract = { + callSendMethod: vi.fn().mockResolvedValue({ error: '500', errorMessage: 'Approve failed' }), + callViewMethod: vi.fn().mockResolvedValue({ error: '500', errorMessage: 'GetAllowance Failed' }), + } as any; + + const result = await checkElfAllowanceAndApprove( + mockTokenContract, + 'ELF', + 'address', + 'approveTargetAddress', + '100', + chainId, + ); + + expect(result).toBe(false); + }); +}); + +describe('getBlockHeight', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + }); + + it('should return block height', async () => { + const aelfInstance = getAElf(chainId); + const mockResult = { + BlockHeight: 12345, + }; + aelfInstance.chain.getBlockHeight.mockReturnValue(mockResult); + + const result = await getBlockHeight(chainId); + + expect(result).toEqual(mockResult); + }); +}); + +describe('getSerializedDataFromLog', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + }); + + it('should return serialized data', () => { + // Mock AElf.pbUtils.getSerializedDataFromLog + AElf.pbUtils.getSerializedDataFromLog.mockImplementation((log: any) => { + return { + data: log, + }; + }); + + const result = getSerializedDataFromLog(chainId); + + expect(result).toEqual({ data: 'AELF' }); + }); +}); + +describe('getTxResult', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + }); + + it('if getAElf.chain.getTxResult return error, throw error', async () => { + // Modify the mock implementation of AElf.chain.getTxResult + const errorMessage = 'Transaction failed'; + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementationOnce(async (txId: string) => { + return { + txId, + status: 'failed', + error: '500', + errorMessage: { + message: errorMessage, + }, + }; + }); + + aelfInstance.chain.getTxResult.mockImplementationOnce(async (txId: string) => { + return { + txId, + status: 'failed', + error: '500', + errorMessage: { + Message: errorMessage, + }, + }; + }); + + // The first call reports an error message from errorMessage.message. + await expect(getTxResult(chainId, TransactionId)).rejects.toThrow('Transaction failed'); + + // The second call reports an error message from errorMessage.Message. + await expect(getTxResult(chainId, TransactionId)).rejects.toThrow('Transaction failed'); + }); + + it('if getAElf.chain.getTxResult return empty string, throw error', async () => { + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return ''; + }); + + await expect(getTxResult(chainId, TransactionId)).rejects.toThrow(`Can not get transaction result.`); + }); + + it('if status is NotExisted, throw error', async () => { + const status = 'NotExisted'; + + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return { Status: status }; + }); + + const result = await getTxResult(chainId, TransactionId); + + expect(result).toEqual({ Status: status }); + }); + + it('if status is always pending, throw error', async () => { + const status = 'pending'; + + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return { Status: status }; + }); + + const result = await getTxResult(chainId, TransactionId); + + expect(result).toEqual({ Status: status }); + }); + + it('if status is always pending_validation, throw error', async () => { + const status = 'pending_validation'; + + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return { Status: status }; + }); + + const result = await getTxResult(chainId, TransactionId); + + expect(result).toEqual({ Status: status }); + }); + + it('if status is mined, return correct tx result', async () => { + const status = 'mined'; + const txResult = { Status: status }; + + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return txResult; + }); + + const result = await getTxResult(chainId, TransactionId); + + expect(result).toEqual(txResult); + }); + + it('if status is unknown, throw error', async () => { + const status = 'unknown'; + const txResult = { Status: status }; + + // Modify the mock implementation of AElf.chain.getTxResult + const aelfInstance = getAElf(chainId); + aelfInstance.chain.getTxResult.mockImplementation(async () => { + return txResult; + }); + + await expect(getTxResult(chainId, TransactionId)).rejects.toThrow(`Transaction: ${status}`); + }); +}); + +describe('encodedTransfer', () => { + const correctAelfAddress = 'ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_AELF'; + + const mockEncodeValue = 'mockEncodeValue'; + + it('should return correct encoded transaction', () => { + (AElf.utils.transform.transformMapToArray as Mock).mockReturnValue({ + name: 'mockTransformMapToArray', + }); + (AElf.utils.transform.transform as Mock).mockImplementation(() => { + return ''; + }); + + const result = encodedTransfer({ + params: { symbol: 'ELF', to: correctAelfAddress, amount: '10', memo: '' }, + inputType: { + fromObject: vi.fn().mockReturnValue('mockFromObject'), + encode: vi.fn().mockImplementation(() => { + return { + finish: vi.fn().mockReturnValue(mockEncodeValue), + }; + }), + }, + }); + + expect(result).toBe(mockEncodeValue); + }); +}); + +describe('encodeTransaction', () => { + it('should return correct encoded transaction', () => { + // Mock AElf.pbUtils.encodeTransaction + AElf.pbUtils.encodeTransaction.mockReturnValue('1234'); + + const result = encodeTransaction(TransactionId); + + expect(result).toEqual('1234'); + }); +}); + +describe('uint8ArrayToHex', () => { + it('should return correct encoded transaction', () => { + const uint8Array = new Uint8Array([0, 1, 2, 3, 15, 16, 255]); + const expectedHex = '000102030f10ff'; + + // Mock AElf.utils.uint8ArrayToHex + AElf.utils.uint8ArrayToHex.mockReturnValue(expectedHex); + + const result = uint8ArrayToHex(uint8Array); + + expect(result).toEqual(expectedHex); + }); +}); + +describe('getContractFileDescriptorSet', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + }); + + it('should return contract file descriptor set if with storage', async () => { + // set localStorage + const key = storages.contractsFileDescriptorBase64 + chainId; + localStorageMock.setItem(key, JSON.stringify({ address: 'mockAddress' })); + + // Mock descriptor.FileDescriptorSet.decode + const mockedFileDescriptorSetDecodeResult = 'mockedFileDescriptorSetDecodeValue'; + (descriptor.FileDescriptorSet.decode as Mock).mockReturnValue(mockedFileDescriptorSetDecodeResult); + + const result = await getContractFileDescriptorSet(chainId, 'address'); + + expect(result).toEqual(mockedFileDescriptorSetDecodeResult); + }); + + it('should return contract file descriptor set if without storage', async () => { + // remove localStorage + localStorageMock.removeItem(storages.contractsFileDescriptorBase64 + chainId); + + // Mock baseRequest and descriptor.FileDescriptorSet.decode + (baseRequest as Mock).mockResolvedValue('mockAddress'); + (descriptor.FileDescriptorSet.decode as Mock).mockReturnValue('mockedFileDescriptorSetDecodeValue'); + + const result = await getContractFileDescriptorSet(chainId, 'address1'); + + expect(baseRequest).toHaveBeenCalled(); + expect(result).toEqual('mockedFileDescriptorSetDecodeValue'); + }); + + it('should return contract file descriptor set if without storage and fetch failed', async () => { + localStorageMock.removeItem(storages.contractsFileDescriptorBase64 + chainId); + + (baseRequest as Mock).mockRejectedValue('Fetch Failed'); + + const result = await getContractFileDescriptorSet(chainId, 'address'); + + expect(result).toBeUndefined(); + }); + + it('should return undefined if localStorage cache error', async () => { + // set localStorage + const key = storages.contractsFileDescriptorBase64 + chainId; + localStorageMock.setItem(key, JSON.stringify({ address: { error: 'error' } })); + + // Mock descriptor.FileDescriptorSet.decode + const mockedFileDescriptorSetDecodeResult = 'mockedFileDescriptorSetDecodeValue'; + (descriptor.FileDescriptorSet.decode as Mock).mockReturnValue(mockedFileDescriptorSetDecodeResult); + + const result = await getContractFileDescriptorSet(chainId, 'address'); + + expect(result).toBeUndefined(); + }); +}); + +describe('getServicesFromFileDescriptors', () => { + it('should return correct value if input has not package date', async () => { + // Mock AElf utils + (AElf.pbjs.Root.fromDescriptor as Mock).mockImplementation(() => { + return { + resolveAll: vi.fn().mockReturnValue({ + lookupService: vi.fn().mockReturnValue('1234'), + }), + }; + }); + + const result = await getServicesFromFileDescriptors({ file: [{ service: ['1', '2'] }] }); + + expect(result).toEqual(['1234']); + }); + + it('should return correct value if input has package date', async () => { + // Mock AElf utils + (AElf.pbjs.Root.fromDescriptor as Mock).mockImplementation(() => { + return { + resolveAll: vi.fn().mockReturnValue({ + lookupService: vi.fn().mockReturnValue('service1'), + }), + }; + }); + + const result = await getServicesFromFileDescriptors({ + file: [{ service: [{ name: 'service1' }], package: { service1: '' } }], + }); + + expect(result).toEqual(['service1']); + }); +}); + +describe('getContractMethods', () => { + beforeEach(() => { + vi.clearAllMocks(); // Clear mocks before each test + }); + + it('should return correct value if input has package date', async () => { + const tokenContractAddress = 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE'; + + // set localStorage + const key = storages.contractsFileDescriptorBase64 + chainId; + localStorageMock.setItem(key, JSON.stringify({ [tokenContractAddress]: tokenContractAddress })); + + // Mock descriptor.FileDescriptorSet.decode to mock getContractFileDescriptorSet + const mockedFileDescriptorSetDecodeResult = { + file: [{ service: [{ name: 'service1' }], package: { service1: '' } }], + }; + (descriptor.FileDescriptorSet.decode as Mock).mockReturnValue(mockedFileDescriptorSetDecodeResult); + + // Mock AElf utils to mock getServicesFromFileDescriptors + (AElf.pbjs.Root.fromDescriptor as Mock).mockImplementation(() => { + return { + resolveAll: vi.fn().mockReturnValue({ + lookupService: vi.fn().mockReturnValue({ + 1: 'services1', + methods: [ + { resolve: vi.fn().mockReturnValue({ name: 'Transfer', resolvedRequestType: 'Transfer' }) }, + { resolve: vi.fn().mockReturnValue({ name: 'Approve', resolvedRequestType: 'Approve' }) }, + ], + }), + }), + }; + }); + + const result = await getContractMethods(chainId, tokenContractAddress); + + expect(result).toEqual({ Transfer: 'Transfer', Approve: 'Approve' }); + }); +}); + +describe('initContracts', () => { + const mockContracts = { + contract1: 'address1', + contract2: 'address2', + }; + + const mockAelfInstance = { + chain: { + contractAt: vi.fn(), + }, + }; + + const mockAccount = 'mock_account_address'; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + mockAelfInstance.chain.contractAt.mockReset(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should initialize contracts successfully without account', async () => { + // Mock successful contract initialization + mockAelfInstance.chain.contractAt + .mockResolvedValueOnce({ address: 'address1' }) + .mockResolvedValueOnce({ address: 'address2' }); + + const result = await initContracts(mockContracts, mockAelfInstance); + + // Verify the result + expect(result).toEqual({ + address1: { address: 'address1' }, + address2: { address: 'address2' }, + }); + + // Verify contractAt was called with the correct arguments + expect(mockAelfInstance.chain.contractAt).toHaveBeenCalledWith('address1', { + address: managerAddress, + }); + expect(mockAelfInstance.chain.contractAt).toHaveBeenCalledWith('address2', { + address: managerAddress, + }); + }); + + it('should initialize contracts successfully with account', async () => { + // Mock successful contract initialization + mockAelfInstance.chain.contractAt + .mockResolvedValueOnce({ address: 'address1' }) + .mockResolvedValueOnce({ address: 'address2' }); + + const result = await initContracts(mockContracts, mockAelfInstance, mockAccount); + + // Verify the result + expect(result).toEqual({ + address1: { address: 'address1' }, + address2: { address: 'address2' }, + }); + + // Verify contractAt was called with the correct arguments + expect(mockAelfInstance.chain.contractAt).toHaveBeenCalledWith('address1', { + address: mockAccount, + }); + expect(mockAelfInstance.chain.contractAt).toHaveBeenCalledWith('address2', { + address: mockAccount, + }); + }); + + it('should handle contract initialization failures', async () => { + // Mock one successful and one failed contract initialization + mockAelfInstance.chain.contractAt + .mockResolvedValueOnce({ address: 'address1' }) + .mockRejectedValueOnce(new Error('Failed to initialize contract')); + + const result = await initContracts(mockContracts, mockAelfInstance); + + // Verify the result + expect(result).toEqual({ + address1: { address: 'address1' }, + address2: undefined, + }); + + // Verify contractAt was called + expect(mockAelfInstance.chain.contractAt).toHaveBeenCalledTimes(2); + }); + + it('should handle empty contracts object', async () => { + const result = await initContracts({}, mockAelfInstance); + + // Verify the result + expect(result).toEqual({}); + + // Verify contractAt was not called + expect(mockAelfInstance.chain.contractAt).not.toHaveBeenCalled(); + }); + + it('should handle global error', async () => { + // Mock global error + mockAelfInstance.chain.contractAt.mockRejectedValue(new Error('Global error')); + + const result = await initContracts(mockContracts, mockAelfInstance); + + // Verify the result + expect(result).toEqual({ + address1: undefined, + address2: undefined, + }); + + // Verify contractAt was called + expect(mockAelfInstance.chain.contractAt).toHaveBeenCalled(); + }); + + it('should log errors when contract initialization fails', async () => { + // Mock console.debug + const consoleDebugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {}); + + // Mock contract initialization failure + mockAelfInstance.chain.contractAt.mockRejectedValue(new Error('Failed to initialize contract')); + + await initContracts(mockContracts, mockAelfInstance); + + // Verify console.debug was called + expect(consoleDebugSpy).toHaveBeenCalledWith(expect.any(Error), mockAelfInstance, '=====contractAt'); + }); + + it('should log global errors', async () => { + // Mock Promise.all + const promiseAllSpy = vi.spyOn(Promise, 'all').mockRejectedValue(new Error('Promise error')); + + // Mock console.log + const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + + await initContracts(mockContracts, mockAelfInstance); + + // Verify Promise.all and console.log was called + expect(promiseAllSpy).toHaveBeenCalled(); + expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(Error), 'initContracts'); + }); +}); diff --git a/src/utils/__tests__/api/application.test.ts b/src/utils/__tests__/api/application.test.ts new file mode 100644 index 00000000..20d3536e --- /dev/null +++ b/src/utils/__tests__/api/application.test.ts @@ -0,0 +1,489 @@ +import { describe, it, expect, vi, Mock } from 'vitest'; +import { request } from 'api'; +import { + addApplicationChain, + commitTokenInfo, + getApplicationChainStatusList, + getApplicationDetail, + getApplicationIssue, + getApplicationTokenConfig, + getApplicationTokenDetail, + getApplicationTokenInfo, + getApplicationTokenList, + getMyApplicationList, + prepareBindIssue, +} from '../../api/application'; +import { ApplicationChainStatusEnum } from 'types/api'; + +vi.mock('api', () => ({ + request: { + application: { + getTokenList: vi.fn(), + getTokenDetail: vi.fn(), + commitTokenInfo: vi.fn(), + getTokenInfo: vi.fn(), + getChainStatus: vi.fn(), + addChain: vi.fn(), + prepareBindIssue: vi.fn(), + getIssue: vi.fn(), + getMyApplicationList: vi.fn(), + getApplicationDetail: vi.fn(), + getTokenConfig: vi.fn(), + }, + }, +})); + +// Mock API failure +const mockFormattedError = 'API request failed'; +const mockError = new Error(mockFormattedError); +const USDT_MOCK = 'USDT_MOCK'; + +describe('getApplicationTokenList', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + tokenList: [ + { symbol: 'A', tokenName: 'TokenA' }, + { symbol: 'B', tokenName: 'TokenB' }, + ], + }, + }; + (request.application.getTokenList as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getApplicationTokenList({}); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getTokenList mock was called + expect(request.application.getTokenList).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getTokenList as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getApplicationTokenList({})).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getTokenList).toHaveBeenCalledOnce(); + }); +}); + +describe('getApplicationTokenDetail', () => { + const params = { + symbol: USDT_MOCK, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + liquidityInUsd: '10000', + holders: '1000', + }, + }; + (request.application.getTokenDetail as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getApplicationTokenDetail(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getTokenList mock was called + expect(request.application.getTokenDetail).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getTokenDetail as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getApplicationTokenDetail(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getTokenDetail).toHaveBeenCalledOnce(); + }); +}); + +describe('commitTokenInfo', () => { + const params = { + symbol: USDT_MOCK, + officialWebsite: '', + officialTwitter: '', + title: '', + personName: '', + telegramHandler: '', + email: 'example@com', + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: true, + }; + (request.application.commitTokenInfo as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await commitTokenInfo(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the commitTokenInfo mock was called + expect(request.application.commitTokenInfo).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.commitTokenInfo as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(commitTokenInfo(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.commitTokenInfo).toHaveBeenCalledOnce(); + }); +}); + +describe('getApplicationTokenInfo', () => { + const params = { + symbol: USDT_MOCK, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + symbol: USDT_MOCK, + userAddress: '', + officialWebsite: '', + officialTwitter: '', + title: '', + personName: '', + telegramHandler: '', + email: 'example@com', + }, + }; + (request.application.getTokenInfo as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getApplicationTokenInfo(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getApplicationTokenInfo mock was called + expect(request.application.getTokenInfo).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getTokenInfo as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getApplicationTokenInfo(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getTokenInfo).toHaveBeenCalledOnce(); + }); +}); + +describe('getApplicationChainStatusList', () => { + const params = { + symbol: USDT_MOCK, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + chainList: [], + otherChainList: [], + }, + }; + (request.application.getChainStatus as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getApplicationChainStatusList(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getApplicationChainStatusList mock was called + expect(request.application.getChainStatus).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getChainStatus as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getApplicationChainStatusList(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getChainStatus).toHaveBeenCalledOnce(); + }); +}); + +describe('addApplicationChain', () => { + const params = { + symbol: USDT_MOCK, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: {}, + }; + (request.application.addChain as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await addApplicationChain(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the addApplicationChain mock was called + expect(request.application.addChain).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.addChain as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(addApplicationChain(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.addChain).toHaveBeenCalledOnce(); + }); +}); + +describe('prepareBindIssue', () => { + const params = { + address: '0x', + symbol: USDT_MOCK, + chainId: 'ETH', + contractAddress: '0x', + supply: '100', + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + bindingId: '', + thirdTokenId: '', + }, + }; + (request.application.prepareBindIssue as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await prepareBindIssue(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the prepareBindIssue mock was called + expect(request.application.prepareBindIssue).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.prepareBindIssue as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(prepareBindIssue(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.prepareBindIssue).toHaveBeenCalledOnce(); + }); +}); + +describe('getApplicationIssue', () => { + const params = { + bindingId: '', + thirdTokenId: '', + mintToAddress: '', + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + bindingId: '', + thirdTokenId: '', + }, + }; + (request.application.getIssue as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getApplicationIssue(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getApplicationIssue mock was called + expect(request.application.getIssue).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getIssue as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getApplicationIssue(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getIssue).toHaveBeenCalledOnce(); + }); +}); + +describe('getMyApplicationList', () => { + const params = {}; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + bindingId: '', + thirdTokenId: '', + }, + }; + (request.application.getMyApplicationList as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getMyApplicationList(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getMyApplicationList mock was called + expect(request.application.getMyApplicationList).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getMyApplicationList as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getMyApplicationList(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getMyApplicationList).toHaveBeenCalledOnce(); + }); +}); + +describe('getApplicationDetail', () => { + const params = { symbol: USDT_MOCK, id: '' }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: [ + { + id: 'id', + symbol: USDT_MOCK, + userAddress: '', + status: ApplicationChainStatusEnum.Complete, + createTime: '1234567', + updateTime: '12345678', + }, + ], + }; + (request.application.getApplicationDetail as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getApplicationDetail(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getApplicationDetail mock was called + expect(request.application.getApplicationDetail).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getApplicationDetail as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getApplicationDetail(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getApplicationDetail).toHaveBeenCalledOnce(); + }); +}); + +describe('getApplicationTokenConfig', () => { + const params = { symbol: USDT_MOCK }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + // Mock API success + const mockResponse = { + data: { + liquidityInUsd: '100', + holders: '2', + }, + }; + (request.application.getTokenConfig as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getApplicationTokenConfig(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getApplicationTokenConfig mock was called + expect(request.application.getTokenConfig).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.application.getTokenConfig as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getApplicationTokenConfig(params)).rejects.toThrow(mockFormattedError); + + // Ensure the mocks were called + expect(request.application.getTokenConfig).toHaveBeenCalledOnce(); + }); +}); diff --git a/src/utils/__tests__/api/auth.test.ts b/src/utils/__tests__/api/auth.test.ts new file mode 100644 index 00000000..8967dd55 --- /dev/null +++ b/src/utils/__tests__/api/auth.test.ts @@ -0,0 +1,142 @@ +import { describe, expect, test, vi, Mock } from 'vitest'; +import { queryAuthApi } from '../../api/auth'; +import axios from 'axios'; +import { BASE_AUTH_URL } from 'constants/index'; +import { service } from 'api/utils'; +import { AuthTokenSource } from 'utils/aelfAuthToken'; + +vi.mock('axios', () => { + const postConfig = { + url: '/test', + method: 'get', + headers: { + 'Content-Type': 'application/json', + }, + }; + + return { + default: { + create: vi.fn().mockReturnValue({ + interceptors: { + request: { + use: vi.fn().mockImplementation((config: any) => { + config(); + }), + }, + response: { + use: vi.fn().mockImplementation((response: any) => { + response({ data: { code: '200' } }); + }), + }, + }, + post: vi.fn().mockResolvedValue(postConfig as never), + }), + post: vi.fn().mockResolvedValue(postConfig as never), + }, + }; +}); + +vi.mock('api/utils', async (importOriginal) => { + const originalModule: any = await importOriginal(); + return { + ...originalModule, + service: { + defaults: { + headers: { + common: {}, + }, + }, + }, + }; +}); + +vi.mock('utils/eBridgeEventBus'); + +// Mock localStorage +export const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: vi.fn((key: string) => store[key] || null), + setItem: vi.fn((key: string, value: string) => { + store[key] = value.toString(); + }), + removeItem: vi.fn((key: string) => { + delete store[key]; + }), + clear: vi.fn(() => { + store = {}; + }), + }; +})(); + +// Attach the mock to the global window object +Object.defineProperty(window, 'localStorage', { + value: localStorageMock, + writable: true, +}); + +describe('queryAuthApi', () => { + const config = { + ca_hash: 'mockCaHash', + managerAddress: 'mockManagerAddress', + pubkey: 'mockPubkey', + signature: 'mockSignature', + plain_text: 'mockPlainText', + source: AuthTokenSource.EVM, + }; + + const mockResponse = { + data: { + token_type: 'Bearer', + access_token: 'mockAccessToken', + }, + }; + + test('should return the correct authorization token', async () => { + (axios.post as Mock).mockResolvedValue(mockResponse); + + const result = await queryAuthApi(config); + + expect(axios.post).toHaveBeenCalledWith(`${BASE_AUTH_URL}/connect/token`, expect.any(String), { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + }); + + expect(result).toBe('Bearer mockAccessToken'); + }); + + test('should set the authorization header in the service', async () => { + (axios.post as Mock).mockResolvedValue(mockResponse); + + const authTokenResult = await queryAuthApi(config); + + expect(authTokenResult).toBe('Bearer mockAccessToken'); + + expect(service.defaults.headers.common['Authorization']).toBe('Bearer mockAccessToken'); + }); + + test('should return the correct authorization token and set into storage if not ca_hash', async () => { + const config = { + ca_hash: '', + managerAddress: 'mockManagerAddress', + pubkey: 'mockPubkey', + signature: 'mockSignature', + plain_text: 'mockPlainText', + source: AuthTokenSource.Portkey, + }; + + (axios.post as Mock).mockResolvedValue(mockResponse); + + const authTokenResult = await queryAuthApi(config); + + expect(authTokenResult).toBe('Bearer mockAccessToken'); + + expect(localStorageMock.setItem).toHaveBeenCalled(); + }); + + test('should throw error if axios.post is failed', async () => { + (axios.post as Mock).mockRejectedValue('Failed to fetch'); + + await expect(queryAuthApi(config)).rejects.toThrow('Failed to fetch'); + }); +}); diff --git a/src/utils/__tests__/api/common.test.ts b/src/utils/__tests__/api/common.test.ts new file mode 100644 index 00000000..85ca4b6e --- /dev/null +++ b/src/utils/__tests__/api/common.test.ts @@ -0,0 +1,95 @@ +import { describe, it, expect, vi, Mock } from 'vitest'; +import { request } from 'api'; +import { getTokenPrice, getTokenWhiteList } from '../../api/common'; + +vi.mock('api', () => ({ + request: { + common: { + getTokenPrice: vi.fn(), + getTokenWhiteList: vi.fn(), + }, + }, +})); + +// Mock API failure +const mockError = new Error('API request failed'); + +describe('getTokenPrice', () => { + const params = { + symbol: 'USDT', + amount: '100', + }; + + // Mock API success + const mockResponse = { + data: { + symbol: 'USDT', + tokenAmountInUsd: '100.00', + }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + (request.common.getTokenPrice as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getTokenPrice(params); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getTokenPrice mock was called + expect(request.common.getTokenPrice).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.common.getTokenPrice as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getTokenPrice(params)).rejects.toThrow('API request failed'); + + // Ensure the mocks were called + expect(request.common.getTokenPrice).toHaveBeenCalledOnce(); + }); +}); + +describe('getTokenWhiteList', () => { + // Mock API success + const mockResponse = { + data: { + AELF: { + USDT: { symbol: 'USDT', decimals: 6 }, + }, + }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return token list when the API call succeeds', async () => { + (request.common.getTokenWhiteList as Mock).mockResolvedValue(mockResponse); + + // Call the function + const result = await getTokenWhiteList(); + + // Assert the result + expect(result).toEqual(mockResponse.data); + + // Ensure the getTokenWhiteList mock was called + expect(request.common.getTokenWhiteList).toHaveBeenCalledOnce(); + }); + + it('should format and throw an error when the API call fails', async () => { + (request.common.getTokenWhiteList as Mock).mockRejectedValue(mockError); + + // Assert the function throws the formatted error + await expect(getTokenWhiteList()).rejects.toThrow('API request failed'); + + // Ensure the mocks were called + expect(request.common.getTokenWhiteList).toHaveBeenCalledOnce(); + }); +}); diff --git a/src/utils/__tests__/calculate.test.ts b/src/utils/__tests__/calculate.test.ts new file mode 100644 index 00000000..b994c27f --- /dev/null +++ b/src/utils/__tests__/calculate.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect, test } from 'vitest'; +import BigNumber from 'bignumber.js'; +import { timesDecimals, divDecimals, divDecimalsStr, bigNumberToWeb3Input, valueToPercentage } from '../calculate'; + +describe('timesDecimals', () => { + test('amount is undefined, and return ZERO', () => { + const result = timesDecimals(undefined); + expect(result).toBeInstanceOf(BigNumber); + }); + test('amount is USDT, and return ZERO', () => { + const result = timesDecimals('USDT'); + expect(result).toBeInstanceOf(BigNumber); + }); + test('amount is 1.2345678 and decimals is 10000000000, and return 12345678000', () => { + const result = timesDecimals('1.2345678', '10000000000').toFixed(); + expect(result).toBe('12345678000'); + }); + test('amount is 1.2345678 and decimals is 6, return 1234567.8', () => { + const result = timesDecimals('1.2345678', 6).toFixed(); + expect(result).toBe('1234567.8'); + }); +}); + +describe('divDecimals', () => { + test('amount is undefined, and return ZERO', () => { + const result = divDecimals(undefined); + expect(result).toBeInstanceOf(BigNumber); + }); + test('amount is USDT, and return ZERO', () => { + const result = divDecimals('USDT'); + expect(result).toBeInstanceOf(BigNumber); + }); + test('amount is 12345678 and decimals is 10000000000, and return 12345678000', () => { + const result = divDecimals('12345678', '10000000000').toFixed(); + expect(result).toBe('0.0012345678'); + }); + test('amount is 12345678 and decimals is 6, and return 12.345678', () => { + const result = divDecimals('12345678', 6).toFixed(); + expect(result).toBe('12.345678'); + }); +}); + +describe('divDecimalsStr', () => { + it('should return formatted number if effective', () => { + const value = new BigNumber(1); + const decimals = 2; + + const result = divDecimalsStr(value, decimals); + + expect(result).toBe('0.01'); + }); + + it('should return default value if not effective and with decimals', () => { + const value = new BigNumber(0); + const decimals = 2; + + const result = divDecimalsStr(value, decimals, 'DEFAULT'); + + expect(result).toBe('DEFAULT'); + }); + + it('should return default value if not effective and without decimals', () => { + const value = new BigNumber(0); + + const result = divDecimalsStr(value, undefined, 'DEFAULT'); + + expect(result).toBe('DEFAULT'); + }); +}); + +describe('bigNumberToWeb3Input', () => { + it('should return a string of the number with no decimals', () => { + const num = new BigNumber(123.456); + const result = bigNumberToWeb3Input(num); + + expect(result).toBe('123'); + }); + + it('should create a new BigNumber if input is not a BigNumber', () => { + const result = bigNumberToWeb3Input(123.456 as any); + expect(result).toBe('123'); + }); +}); + +describe('valueToPercentage', () => { + it('should return correct percentage when input is a BigNumber', () => { + const num = new BigNumber(0.5); + + const result = valueToPercentage(num); + + expect(result).toEqual(new BigNumber(50)); + }); + + it('should convert non-BigNumber input using timesDecimals', () => { + const num = '0.5'; + + const result = valueToPercentage(num); + + expect(result).toEqual(new BigNumber(50)); + }); +}); diff --git a/src/utils/__tests__/chain.test.ts b/src/utils/__tests__/chain.test.ts new file mode 100644 index 00000000..0a1dac17 --- /dev/null +++ b/src/utils/__tests__/chain.test.ts @@ -0,0 +1,360 @@ +import { describe, it, expect, vi, beforeEach, Mock } from 'vitest'; +import { SupportedELFChainId } from 'constants/chain'; +import { + getIconByChainId, + getNameByChainId, + getShortNameByChainId, + getNativeTokenByChainId, + getChainIdByAPI, + getIconByAPIChainId, + getChainIdToMap, + base58ToChainId, + chainIdToBase58, + formatRestoreAddress, + shortenAddressByAPI, + formatNetworkName, + getBridgeChainInfo, + getChainName, + getChainType, + formatAddress, +} from 'utils/chain'; +import { CHAIN_ICON, CHAIN_NAME, IS_MAINNET } from 'constants/index'; +import { isELFChain } from '../aelfUtils'; +import { isTonChain, shortenAddress } from 'utils'; + +// Mock AElf +vi.mock('aelf-sdk', () => { + // Mock AElf.utils.chainIdConvertor + const mockChainIdConvertor = { + base58ToChainId: vi.fn((chainId: string) => `base58_${chainId}`), + chainIdToBase58: vi.fn((chainId: number) => `chainId_${chainId}`), + }; + return { + default: { + utils: { + chainIdConvertor: mockChainIdConvertor, + }, + }, + }; +}); + +vi.mock('utils', () => { + return { + enumToMap: vi.fn().mockReturnValue({ AELF: 'MainChain_AELF', MainChain_AELF: 'AELF', TonTest: '1100' }), + isTonChain: vi.fn(), + shortenAddress: vi.fn(), + }; +}); + +vi.mock('../aelfUtils', () => { + return { + isELFChain: vi.fn(), + }; +}); + +// Mock constants +const mockChainId = SupportedELFChainId.AELF; +const MainChain_AELF = 'MainChain_AELF'; +const mockInvalidChainId = 'invalid_chain_id' as any; +const mockChainName = 'AELF'; +const mockChainShortName = 'AELF'; +const correctAelfAddress = 'ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_AELF'; + +describe('Chain Utils', () => { + beforeEach(() => { + // Reset mocks before each test + vi.clearAllMocks(); + }); + + describe('getIconByChainId', () => { + it('should return the correct icon for a valid chainId', () => { + const result = getIconByChainId(mockChainId); + + expect(result).toEqual(CHAIN_ICON[mockChainId]); + }); + + it('should return undefined for an invalid chainId', () => { + const result = getIconByChainId(mockInvalidChainId); + + expect(result).toBeUndefined(); + }); + }); + + describe('getNameByChainId', () => { + it('should return the correct name for a valid chainId', () => { + const result = getNameByChainId(mockChainId); + + expect(result).toBe(CHAIN_NAME[mockChainId]); + }); + + it('should return "unknown chain" for an invalid chainId', () => { + const result = getNameByChainId(undefined); + + expect(result).toBe('unknown chain'); + }); + }); + + describe('getShortNameByChainId', () => { + it('should return the correct short name for a valid chainId', () => { + const result = getShortNameByChainId(mockChainId); + + expect(result).toBe(mockChainShortName); + }); + + it('should return "unknown chain" for an invalid chainId', () => { + const result = getShortNameByChainId(undefined); + + expect(result).toBe('unknown chain'); + }); + + it('should return chainId for not chainName', () => { + const result = getShortNameByChainId('ETH' as any); + + expect(result).toBe('ETH'); + }); + }); + + describe('getNativeTokenByChainId', () => { + it('should return the correct native token for a valid chainId', () => { + const result = getNativeTokenByChainId(mockChainId); + + expect(result).toBe(mockChainShortName); + }); + + it('should return "unknown chain" for an invalid chainId', () => { + const result = getNativeTokenByChainId(undefined); + + expect(result).toBe('unknown chain'); + }); + + it('should return chainId for not chainName', () => { + const result = getNativeTokenByChainId('ETH' as any); + + expect(result).toBe('ETH'); + }); + }); + + describe('getChainIdByAPI', () => { + it('should return the correct chainId for a valid API chainId', () => { + const result = getChainIdByAPI(mockChainId); + + expect(result).toBe('MainChain_AELF'); + }); + + it('should return the number chainId for an number input', () => { + const result = getChainIdByAPI('TonTest' as any); + + expect(result).toBe(1100); + }); + + it('should return the input chainId for an invalid API chainId', () => { + const result = getChainIdByAPI(mockInvalidChainId); + + expect(result).toBe('invalid_chain_id'); + }); + }); + + describe('getIconByAPIChainId', () => { + it('should return the correct icon for a valid API chainId', () => { + const result = getIconByAPIChainId(MainChain_AELF); + + expect(result).toEqual({ type: 'AELF-CHAIN' }); + }); + + it('should return undefined for an invalid API chainId', () => { + const result = getIconByAPIChainId(mockInvalidChainId); + + expect(result).toBeUndefined(); + }); + }); + + describe('getChainIdToMap', () => { + it('should return the correct chainId for a valid chainId', () => { + const result = getChainIdToMap(mockChainId); + + expect(result).toBe(MainChain_AELF); + }); + + it('should return the input chainId for an invalid chainId', () => { + const result = getChainIdToMap(mockInvalidChainId); + + expect(result).toBe('invalid_chain_id'); + }); + }); + + describe('base58ToChainId', () => { + it('should convert base58 to chainId', () => { + const result = base58ToChainId('base58_chain_id' as any); + + expect(result).toBe('base58_base58_chain_id'); + }); + }); + + describe('chainIdToBase58', () => { + it('should convert chainId to base58', () => { + const result = chainIdToBase58(123); + + expect(result).toBe('chainId_123'); + }); + }); + + describe('formatRestoreAddress', () => { + it('should format restore address correctly', () => { + const result = formatRestoreAddress(mockChainId, 'ELF_address_123_AELF'); + + expect(result).toBe('address_123'); + }); + + it('should return empty string for invalid address', () => { + const result = formatRestoreAddress(mockChainId, undefined); + + expect(result).toBe(''); + }); + }); + + describe('formatAddress', () => { + it('should format address correctly', () => { + const result = formatAddress(mockChainId, 'address_123'); + + expect(result).toBe('ELF_address_123_AELF'); + }); + + it('should return empty string for invalid address or chainId', () => { + const result1 = formatAddress(undefined, 'address_123'); + + expect(result1).toBe(''); + + const result2 = formatAddress(mockChainId, undefined); + + expect(result2).toBe(''); + }); + }); + + describe('shortenAddressByAPI', () => { + it('should shorten address correctly for ELF chain', () => { + // Mock isELFChain and shortenAddress method + (isELFChain as Mock).mockReturnValue(true); + (shortenAddress as Mock).mockReturnValue('ELF_Py2T...AELF'); + + const result = shortenAddressByAPI(correctAelfAddress, mockChainId, 6); + + expect(result).toBe('ELF_Py2T...AELF'); + }); + + it('should shorten address correctly for non-ELF chain', () => { + // Mock isELFChain and shortenAddress method + (isELFChain as Mock).mockReturnValue(false); + (shortenAddress as Mock).mockReturnValue('0x1234...'); + + const result = shortenAddressByAPI('0x12345678', 'ETH' as any, 4); + + expect(result).toBe('0x1234...'); + }); + + it('should return empty string for invalid address or chainId', () => { + const result1 = shortenAddressByAPI(undefined, mockChainId, 4); + + expect(result1).toBe(''); + + const result2 = shortenAddressByAPI('address_123', undefined, 4); + + expect(result2).toBe(''); + }); + }); + + describe('formatNetworkName', () => { + it('should format network name correctly for AELF', () => { + const result = formatNetworkName(mockChainId); + + expect(result).toBe(CHAIN_NAME[mockChainId]); + }); + + it('should format network name correctly for tDVV', () => { + const result = formatNetworkName('tDVV'); + + expect(result).toBe(CHAIN_NAME['tDVV']); + }); + + it('should format network name correctly for tDVW', () => { + const result = formatNetworkName('tDVW'); + + expect(result).toBe(CHAIN_NAME['tDVW']); + }); + + it('should return the input item for unsupported chainId', () => { + const result = formatNetworkName('unsupported_chain_id'); + + expect(result).toBe('unsupported_chain_id'); + }); + }); + + describe('getBridgeChainInfo', () => { + it('should return ELF chain info for ELF chainId', () => { + // Mock isELFChain method + (isELFChain as Mock).mockReturnValue(true); + + const result = getBridgeChainInfo(mockChainId); + + expect(result).toBeDefined(); + }); + + it('should return TON chain info for TON chainId', () => { + // Mock isELFChain and isTonChain method + (isELFChain as Mock).mockReturnValue(false); + (isTonChain as unknown as Mock).mockReturnValue(true); + + const result = getBridgeChainInfo((IS_MAINNET ? 1101 : 1100) as any); + + expect(result).toBeDefined(); + }); + + it('should return ERC chain info for ERC chainId', () => { + // Mock isELFChain and isTonChain method + (isELFChain as Mock).mockReturnValue(false); + (isTonChain as unknown as Mock).mockReturnValue(false); + + const result = getBridgeChainInfo(1 as any); + + expect(result).toBeDefined(); + }); + }); + + describe('getChainName', () => { + it('should return the correct chain name', () => { + const result = getChainName(mockChainId); + + expect(result).toBe(CHAIN_NAME[mockChainName]); + }); + }); + + describe('getChainType', () => { + it('should return "ELF" for ELF chainId', () => { + // Mock isELFChain method + (isELFChain as Mock).mockReturnValue(true); + + const result = getChainType(mockChainId); + + expect(result).toBe('ELF'); + }); + + it('should return "TON" for TON chainId', () => { + // Mock isELFChain and isTonChain method + (isELFChain as Mock).mockReturnValue(false); + (isTonChain as unknown as Mock).mockReturnValue(true); + + const result = getChainType(1100 as any); + + expect(result).toBe('TON'); + }); + + it('should return "ERC" for ERC chainId', () => { + // Mock isELFChain and isTonChain method + (isELFChain as Mock).mockReturnValue(false); + (isTonChain as unknown as Mock).mockReturnValue(false); + + const result = getChainType(1 as any); + + expect(result).toBe('ERC'); + }); + }); +}); diff --git a/src/utils/__tests__/checkAElfBridge.test.ts b/src/utils/__tests__/checkAElfBridge.test.ts new file mode 100644 index 00000000..3a284334 --- /dev/null +++ b/src/utils/__tests__/checkAElfBridge.test.ts @@ -0,0 +1,84 @@ +import { AElfDappBridge } from '@aelf-react/types'; +import { checkAElfBridge, isAElfBridge, isCurrentAElfBridge, reConnectAElfBridge } from 'utils/checkAElfBridge'; +import { describe, it, expect, vi } from 'vitest'; + +describe('isAElfBridge', () => { + it('should return true for valid AElfBridge', () => { + const aelfBridge = { options: {}, connect: vi.fn() } as unknown as AElfDappBridge; + + expect(isAElfBridge(aelfBridge)).toBe(true); + }); + + it('should return false for invalid AElfBridge', () => { + const aelfBridge = { options: {} } as unknown as AElfDappBridge; + + expect(isAElfBridge(aelfBridge)).toBe(false); + }); +}); + +describe('isCurrentAElfBridge', () => { + it('should return true if endpoint matches', () => { + const aelfBridge = { options: { endpoint: 'http://localhost:8000' } } as unknown as AElfDappBridge; + + expect(isCurrentAElfBridge(aelfBridge)).toBe(false); + }); + + it('should return false if endpoint does not match', () => { + const aelfBridge = { options: { endpoint: 'http://localhost:8001' } } as unknown as AElfDappBridge; + + expect(isCurrentAElfBridge(aelfBridge)).toBe(false); + }); +}); + +describe('reConnectAElfBridge', () => { + it('should reconnect failed if endpoint is undefined', async () => { + const aelfBridge = { + options: { endpoint: undefined }, + connect: vi.fn().mockResolvedValue(true), + } as unknown as AElfDappBridge; + + await reConnectAElfBridge(aelfBridge); + + expect(aelfBridge.connect).toHaveBeenCalled(); + }); + + it('should throw error if reconnection fails', async () => { + const aelfBridge = { + options: { endpoint: 'http://localhost:8000' }, + connect: vi.fn().mockResolvedValue(false), + } as unknown as AElfDappBridge; + + await expect(reConnectAElfBridge(aelfBridge)).rejects.toThrow('Reconnect Failed'); + }); + + it('should throw error if reconnection fails', async () => { + const aelfBridge = { + options: { endpoint: 'http://localhost:8000' }, + connect: vi.fn().mockResolvedValue(false), + } as unknown as AElfDappBridge; + + await expect(reConnectAElfBridge(aelfBridge)).rejects.toThrow('Reconnect Failed'); + }); +}); + +describe('checkAElfBridge', () => { + it('should reconnect if not current bridge', async () => { + const aelfBridge = { + options: { endpoint: 'http://localhost:8001' }, + connect: vi.fn().mockResolvedValue(true), + } as unknown as AElfDappBridge; + + await checkAElfBridge(aelfBridge); + + expect(aelfBridge.connect).toHaveBeenCalled(); + }); + + it('should not reconnect if current bridge', async () => { + const aelfBridge = { + options: { endpoint: 'http://localhost:8000' }, + connect: vi.fn(), + } as unknown as AElfDappBridge; + + await expect(checkAElfBridge(aelfBridge)).rejects.toThrow('Reconnect Failed'); + }); +}); diff --git a/src/utils/__tests__/contract.test.ts b/src/utils/__tests__/contract.test.ts new file mode 100644 index 00000000..0d83d961 --- /dev/null +++ b/src/utils/__tests__/contract.test.ts @@ -0,0 +1,1832 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { ChainId } from 'types'; +import { encodedTransfer, getContractMethods, getTxResult, isELFChain } from 'utils/aelfUtils'; +import { getTransactionResponseHash } from 'utils/ton'; +import { + AElfContractBasic, + ContractBasic, + PortkeyContractBasic, + PortkeySDKContractBasic, + TONContractBasic, + WB3ContractBasic, +} from 'utils/contract'; +import { TonContractCallData } from 'utils/tonContractCall'; +import { isTonChain } from 'utils'; +import { PortkeyDid } from '@aelf-web-login/wallet-adapter-bridge'; +import { ZERO } from 'constants/misc'; +import { + getGasPriceByWagmi, + readContractByWagmi, + waitForTransactionReceiptByWagmi, + writeContractByWagmi, +} from 'utils/wagmi'; +import BigNumber from 'bignumber.js'; + +// Mock external dependencies +vi.mock('utils/wagmi', () => { + return { + getGasPriceByWagmi: vi.fn(), + readContractByWagmi: vi.fn(), + waitForTransactionReceiptByWagmi: vi.fn(), + writeContractByWagmi: vi.fn(), + }; +}); + +vi.mock('utils/index', () => { + return { + isTonChain: vi.fn(), + sleep: vi.fn(), + }; +}); + +vi.mock('utils/aelfUtils', () => ({ + getContractMethods: vi.fn(), + transformArrayToMap: vi.fn((_, params) => params), + getTxResult: vi.fn(), + encodedTransfer: vi.fn(), + isELFChain: vi.fn(), +})); + +vi.mock('utils/checkAElfBridge', () => ({ + checkAElfBridge: vi.fn(), +})); + +vi.mock('utils/ton', () => ({ + getTransactionResponseHash: vi.fn(), +})); + +vi.mock('utils/tonContractCall', () => { + return { + CallTonContract: { + GetBalance: vi.fn().mockReturnValue({ balance: '123' }), + GetTokenInfo: vi.fn().mockImplementation(() => { + throw { message: 'Failed to get token info' }; + }), + GetAllowance: vi.fn().mockImplementation(() => { + throw { Error: 'Failed to get allowance' }; + }), + getSwapId: vi.fn().mockImplementation(() => { + throw { Status: 'Failed to get swap id' }; + }), + }, + TonContractCallData: { + createReceipt: vi.fn(), + }, + }; +}); + +// Mock ELFChainConstants and ERCChainConstants +vi.mock('constants/ChainConstants', () => { + return { + ELFChainConstants: { + aelfInstances: { + AELF: { + appName: 'AELF_appName', + options: {}, + }, + }, + }, + ERCChainConstants: { + chainId: 11155111, + }, + }; +}); + +vi.mock('constants/misc', async (importOriginal) => { + const originalModule: any = await importOriginal(); + + return { + ...originalModule, + ZERO: { + plus: vi.fn(), + }, + }; +}); + +vi.mock('@tonconnect/ui-react', () => { + return { + TonConnectUI: vi.fn(), + }; +}); + +vi.mock('@aelf-web-login/wallet-adapter-bridge', () => { + return { + PortkeyDid: { + managerApprove: vi.fn(), + }, + }; +}); + +const correctAelfAddress = 'ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_AELF'; +const AELFTestnetContractAddress = 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE'; + +describe('ContractBasic Class', () => { + const mockPortkeyProps = { + portkeyChain: { getContract: vi.fn() }, + contractAddress: 'ELF_ADDRESS', + chainId: 'AELF' as ChainId, + } as any; + + const mockAelfProps = { + contractAddress: 'ELF_ADDRESS', + chainId: 'AELF' as ChainId, + aelfContract: { someMethod: vi.fn() }, + }; + + const mockERCProps = { + contractAddress: 'ERC_ADDRESS', + chainId: 11155111 as ChainId, + }; + + const mockTONProps = { + contractAddress: 'TON_ADDRESS', + chainId: 1100 as ChainId, + }; + + describe('initialize', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should initialize portkey contract correctly', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(true); + + const contract = new ContractBasic(mockPortkeyProps); + + expect(contract.contractType).toBe('ELF'); + expect(contract.callContract).toBeInstanceOf(PortkeyContractBasic); + }); + + it('should initialize aelf contract correctly', () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(true); + + const contract = new ContractBasic(mockAelfProps); + + expect(contract.contractType).toBe('ELF'); + expect(contract.callContract).toBeInstanceOf(AElfContractBasic); + }); + + it('should initialize ERC contract correctly', () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(isTonChain).mockReturnValue(false); + + const contract = new ContractBasic(mockERCProps); + + expect(contract.contractType).toBe('ERC'); + expect(contract.callContract).toBeInstanceOf(WB3ContractBasic); + }); + + it('should initialize TON contract correctly', () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(isTonChain).mockReturnValue(true); + + const contract = new ContractBasic(mockTONProps); + + expect(contract.contractType).toBe('TON'); + expect(contract.callContract).toBeInstanceOf(TONContractBasic); + }); + }); + + describe('callViewMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle view method calls with aelf contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(true); + + const contract = new ContractBasic({ + ...mockAelfProps, + aelfContract: { + GetBalance: { + call: vi.fn().mockResolvedValue({ balance: '1000' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance', { + symbol: 'ELF', + owner: correctAelfAddress, + }); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('balance', '1000'); + }); + + it('should handle view method calls with portkey contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(true); + + const contract = new ContractBasic({ + ...mockPortkeyProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callViewMethod: vi.fn().mockResolvedValue({ data: { balance: '1000' } }), + }), + }, + }); + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('balance', '1000'); + }); + + it('should handle view method calls with web3 contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(isTonChain).mockReturnValue(false); + + const response = 'mockResult'; + vi.mocked(readContractByWagmi).mockResolvedValue(response); + + const contract = new ContractBasic(mockERCProps); + + const result = await contract.callViewMethod('testMethod'); + + expect(result).toBe(response); + }); + }); + + describe('callSendMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle send method calls with aelf contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(true); + + const contract = new ContractBasic({ + ...mockAelfProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ TransactionId: 'mockId' }), + }, + aelfInstance: {} as any, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(getTxResult).mockResolvedValue({ txResult: 'success' }); + + const result = await contract.callSendMethod('GetTransaction', correctAelfAddress, {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(getTxResult).toHaveBeenCalled(); + expect(result.txResult).toBe('success'); + }); + + it('should handle send method calls with portkey contract', async () => { + const contract = new ContractBasic({ + ...mockPortkeyProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callSendMethod: vi.fn().mockResolvedValue({ status: 'success' }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ CreateToken: {}, GetWalletInfo: {} }); + + const result = await contract.callSendMethod('CreateToken', correctAelfAddress, {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('status', 'success'); + }); + + it('should handle send method calls with web3 contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(isTonChain).mockReturnValue(false); + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + vi.mocked(waitForTransactionReceiptByWagmi).mockResolvedValue({ + status: 'success', + transactionHash: txHash, + } as any); + + const contract = new ContractBasic({ ...mockERCProps, contractABI: [] } as any); + + const result = await contract.callSendMethod('crossChainCreateToken', '0xAccount', []); + + expect(result.transactionHash).toBe(txHash); + expect(result.TransactionId).toBe(txHash); + }); + }); + + describe('callSendPromiseMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle send promise method calls with aelf contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(true); + + const contract = new ContractBasic({ + ...mockAelfProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ result: 'data' }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendPromiseMethod('GetTransaction', correctAelfAddress); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('result', 'data'); + }); + + it('should handle send promise method calls with portkey contract', async () => { + const contract = new ContractBasic(mockPortkeyProps); + + await expect(contract.callSendPromiseMethod('CreateToken', correctAelfAddress)).rejects.toThrow( + Error('Method not implemented.'), + ); + }); + + it('should handle send promise method calls with web3 contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(isTonChain).mockReturnValue(false); + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + + const contract = new ContractBasic({ ...mockERCProps, contractABI: [] } as any); + + const result = await contract.callSendPromiseMethod('createToken', '0xAccount', []); + + expect(result).toBe(txHash); + }); + }); + + describe('encodedTx', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle encode method calls with aelf contract', async () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(true); + + const contract = new ContractBasic({ + ...mockAelfProps, + aelfContract: { + GetTransaction: { + call: vi.fn().mockResolvedValue({ result: 'data' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(encodedTransfer).mockResolvedValue({ + data: 'encodedData', + inputType: 'inputType', + }); + + const result = await contract.encodedTx('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(encodedTransfer).toHaveBeenCalled(); + expect(result).toEqual({ data: 'encodedData', inputType: 'inputType' }); + }); + }); +}); + +describe('WB3ContractBasic Class', () => { + const mockProps = { + contractAddress: '0x123', + chainId: 11155111 as ChainId, + contractABI: [], + } as any; + + describe('callViewMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle view method calls', async () => { + const txHash = '0x1234567'; + vi.mocked(readContractByWagmi).mockResolvedValue(txHash); + + const contract = new WB3ContractBasic(mockProps); + + const result = await contract.callViewMethod('testMethod'); + + expect(result).toBe(txHash); + }); + + it('should handle view method calls when there is no chainId', async () => { + const txHash = '0x1234567'; + vi.mocked(readContractByWagmi).mockResolvedValue(txHash); + + const contract = new WB3ContractBasic({ ...mockProps, chainId: null }); + + const result = await contract.callViewMethod('testMethod', ['param']); + + expect(result).toBe(txHash); + }); + + it('should return error if getBalance return error', async () => { + vi.mocked(readContractByWagmi).mockRejectedValue('Failed to get balance'); + + const contract = new WB3ContractBasic(mockProps); + + const result = await contract.callViewMethod('getBalance', ['param']); + + expect(result.error).toBe('Failed to get balance'); + }); + }); + + describe('callSendMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + + vi.mocked(ZERO.plus).mockImplementation((n: BigNumber.Value, base?: number) => { + return new BigNumber(n, base); + }); + }); + + it('should handle send method and return result correctly', async () => { + // Mock + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + vi.mocked(waitForTransactionReceiptByWagmi).mockResolvedValue({ + status: 'success', + transactionHash: txHash, + } as any); + + const contract = new WB3ContractBasic(mockProps); + + const result = await contract.callSendMethod('crossChainCreateToken', '0xAccount', [], { + onMethod: 'receipt', + gas: 10, + nonce: 4567823, + }); + + expect(result.transactionHash).toBe(txHash); + expect(result.TransactionId).toBe(txHash); + }); + + it('should console error if ZERO throw error', async () => { + const contract = new WB3ContractBasic(mockProps); + + // Mock + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + vi.mocked(waitForTransactionReceiptByWagmi).mockResolvedValue({ + status: 'success', + transactionHash: txHash, + } as any); + vi.mocked(ZERO.plus).mockImplementation(() => { + throw 'ZERO throw error'; + }); + + const result = await contract.callSendMethod('crossChainCreateToken', '0xAccount', undefined, { + onMethod: 'confirmation', + }); + + expect(result.TransactionId).toEqual(txHash); + }); + + it('should return error if there is no chainId', async () => { + const contract = new WB3ContractBasic({ ...mockProps, chainId: null }); + + // Mock + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + vi.mocked(waitForTransactionReceiptByWagmi).mockResolvedValue({ + status: 'success', + transactionHash: txHash, + } as any); + + const result = await contract.callSendMethod('testMethod', '0xAccount'); + + expect(result.TransactionId).toEqual(txHash); + }); + + it('should catch error if callSendMethod rejected', async () => { + const contract = new WB3ContractBasic(mockProps); + + // Mock + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockRejectedValue('Failed'); + + const result = await contract.callSendMethod('swapToken', '0xAccount', []); + + expect(result.error).toBe('Failed'); + }); + + it('should return successful if getGasPriceByWagmi return undefined', async () => { + const contract = new WB3ContractBasic(mockProps); + + // Mock + vi.mocked(getGasPriceByWagmi).mockResolvedValue(undefined as any); + const txHash = '0x1234567'; + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + + const result = await contract.callSendMethod('swapToken', '0xAccount', [], { onMethod: 'receipt', value: 20 }); + + expect(result.TransactionId).toEqual(txHash); + }); + + it('should handle send method errors', async () => { + const contract = new WB3ContractBasic({ ...mockProps, chainId: null, contractAddress: '' }); + + // Mock + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + const txHash = '0x1234567'; + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + vi.mocked(waitForTransactionReceiptByWagmi).mockResolvedValue({ status: 'reverted' } as any); + + const result = await contract.callSendMethod('createToken', '0xAccount'); + + expect(result.error).toEqual({ message: 'Transaction is reverted', status: 'reverted' }); + }); + }); + + describe('callSendPromiseMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle createToken and return successfully', async () => { + const contract = new WB3ContractBasic(mockProps); + + // Mock + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + + const result = await contract.callSendPromiseMethod('createToken', '0xAccount', [], { + gasPrice: '10', + gas: 20, + value: '12', + nonce: 123, + onMethod: 'transactionHash', + }); + + expect(result).toBe(txHash); + }); + + it('should handle createToken and return successfully if no paramsOption', async () => { + const contract = new WB3ContractBasic(mockProps); + + // Mock + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + + const result = await contract.callSendPromiseMethod('createToken', '0xAccount'); + + expect(result).toBe(txHash); + }); + + it('should catch error if callSendPromiseMethod throw error', async () => { + const contract = new WB3ContractBasic(mockProps); + + // Mock + const error = 'Failed to create receipt'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockRejectedValue(error); + + const result = await contract.callSendPromiseMethod('createReceipt', '0xAccount'); + + expect(result.error).toBe('Failed to create receipt'); + }); + + it('should return error if there is no chainId', async () => { + const contract = new WB3ContractBasic({ ...mockProps, chainId: null }); + + // Mock + const txHash = '0x1234567'; + vi.mocked(getGasPriceByWagmi).mockResolvedValue(BigInt(1234)); + vi.mocked(writeContractByWagmi).mockResolvedValue(txHash); + + const result = await contract.callSendPromiseMethod('testMethod', '0xAccount'); + + expect(result).toEqual(txHash); + }); + }); +}); + +describe('AElfContractBasic Class', () => { + const mockProps = { + contractAddress: 'ELF_ADDRESS', + chainId: 999 as ChainId, + } as any; + + describe('getFileDescriptorsSet', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should set methods', async () => { + // Mock contract + const contract = new AElfContractBasic(mockProps); + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + await contract.getFileDescriptorsSet(AELFTestnetContractAddress); + + expect(contract.methods).toEqual({ GetBalance: {} }); + }); + + it('should set methods error if getContractMethods rejected', async () => { + const contract = new AElfContractBasic(mockProps); + + // Mock + vi.mocked(getContractMethods).mockRejectedValueOnce('Failed to get GetBalance method'); + + await expect(contract.getFileDescriptorsSet(AELFTestnetContractAddress)).rejects.toThrow( + `"Failed to get GetBalance method"address:JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaEContract:getContractMethods`, + ); + }); + }); + + describe('checkMethods', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should check methods successfully if there is no method cache', async () => { + // Mock contract + const contract = new AElfContractBasic(mockProps); + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + await contract.checkMethods(); + + expect(contract.methods).toEqual({ GetBalance: {} }); + }); + }); + + describe('callViewMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle successful view method calls', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + TestMethod: { + call: vi.fn().mockResolvedValue({ result: 'data' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ TestMethod: {} }); + + const result = await contract.callViewMethod('TestMethod'); + expect(result).toBe('data'); + }); + + it('should return null if aelfContract[method] return null', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + TestMethod: { + call: vi.fn().mockResolvedValue({ result: null }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ TestMethod: {} }); + + const result = await contract.callViewMethod('TestMethod'); + + expect(result).toBeNull(); + }); + + it('should return result if aelfContract[method] return result', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + TestMethod: { + call: vi.fn().mockResolvedValue({ status: 'success' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ TestMethod: {} }); + + const result = await contract.callViewMethod('TestMethod'); + + expect(result.status).toBe('success'); + }); + + it('should return error if aelfContract[method] return error', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + TestMethod: { + call: vi.fn().mockRejectedValue({ status: 'error' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ TestMethod: {} }); + + const result = await contract.callViewMethod('TestMethod'); + + expect(result.error).toEqual({ status: 'error' }); + }); + + it('should return error if there is no aelfContract', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ TestMethod: {} }); + + const result = await contract.callViewMethod('TestMethod'); + + expect(result).toEqual({ error: { code: 401, message: 'Contract init error1' } }); + }); + }); + + describe('callSendMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle send method transaction flow and return tx result', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ TransactionId: 'mockId' }), + }, + aelfInstance: {}, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(getTxResult).mockResolvedValue({ txResult: 'success' }); + + const result = await contract.callSendMethod('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(getTxResult).toHaveBeenCalled(); + expect(result.txResult).toBe('success'); + }); + + it('should handle send method transaction flow and return TransactionId', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ TransactionId: 'mockId' }), + }, + aelfInstance: {}, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', {}, { onMethod: 'transactionHash' }); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('TransactionId', 'mockId'); + }); + + it('should return error if there is no aelfInstance', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ TransactionId: 'mockId' }), + }, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', {}, { onMethod: 'transactionHash' }); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toEqual({ error: new Error('aelfInstance is undefined') }); + }); + + it('should return error if there is not aelfContract', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + }); + + const result = await contract.callSendMethod('GetTransaction', {}, { onMethod: 'transactionHash' }); + + expect(result).toEqual({ error: { code: 401, message: 'Contract init error2' } }); + }); + + it('should return error if getTxResult throw error.message', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ TransactionId: 'mockId' }), + }, + aelfInstance: {}, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(getTxResult).mockRejectedValue({ message: 'Failed to get tx result' }); + + const result = await contract.callSendMethod('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(getTxResult).toHaveBeenCalled(); + expect(result.error).toEqual({ message: 'Failed to get tx result' }); + }); + + it('should return error if getTxResult throw error.ErrorMessage', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ + TransactionId: 'mockId', + errorMessage: { + message: 'Failed to get tx result', + }, + }), + }, + aelfInstance: {}, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(getTxResult).mockRejectedValue({ ErrorMessage: 'Failed' }); + + const result = await contract.callSendMethod('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result.error).toEqual({ message: 'Failed to get tx result' }); + }); + + it('should return error if getTxResult throw string error', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ + TransactionId: '', + error: { + message: { Message: 'Failed to get tx result' }, + }, + }), + }, + aelfInstance: {}, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(getTxResult).mockRejectedValue('Failed'); + + const result = await contract.callSendMethod('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result.error).toEqual({ + message: 'Failed to get tx result', + code: { + message: { + Message: 'Failed to get tx result', + }, + }, + }); + }); + + it('should return error if aelfContract[method] throw error and errorMessage', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ + TransactionId: '', + error: '500', + errorMessage: { message: 'Failed to get tx result' }, + }), + }, + aelfInstance: {}, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + + expect(result.error).toEqual({ code: '500', message: 'Failed to get tx result' }); + }); + }); + + describe('encodedTx', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return encode tx successfully', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: { + call: vi.fn().mockResolvedValue({ result: 'data' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(encodedTransfer).mockResolvedValue({ + data: 'encodedData', + inputType: 'inputType', + }); + + const result = await contract.encodedTx('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(encodedTransfer).toHaveBeenCalled(); + expect(result).toEqual({ data: 'encodedData', inputType: 'inputType' }); + }); + + it('should return error if there is no aelfContract', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.encodedTx('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toEqual({ error: { code: 401, message: 'Contract init error2' } }); + }); + + it('should return error if encodedTransfer is throw error', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: { + call: vi.fn().mockResolvedValue({ result: 'data' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + vi.mocked(encodedTransfer).mockImplementation(() => { + throw 'Failed encode tx'; + }); + + const result = await contract.encodedTx('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(encodedTransfer).toHaveBeenCalled(); + expect(result).toEqual({ error: 'Failed encode tx' }); + }); + }); + + describe('callSendPromiseMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle callSendPromiseMethod method', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: vi.fn().mockResolvedValue({ result: 'data' }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendPromiseMethod('GetTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('result', 'data'); + }); + + it('should handle callSendPromiseMethod method error', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + SendTransaction: vi.fn().mockImplementation(() => { + throw 'Failed send tx'; + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ SendTransaction: {} }); + + const result = await contract.callSendPromiseMethod('SendTransaction', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('error', 'Failed send tx'); + }); + + it('should return error if there is no aelfContract', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + }); + + const result = await contract.callSendPromiseMethod('GetTransaction', {}); + + expect(result).toEqual({ error: { code: 401, message: 'Contract init error3' } }); + }); + }); +}); + +describe('PortkeyContractBasic Class', () => { + const mockProps = { + contractAddress: AELFTestnetContractAddress, + chainId: 'AELF' as ChainId, + } as any; + + describe('getFileDescriptorsSet', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should set methods', async () => { + // Mock contract + const contract = new PortkeyContractBasic({ ...mockProps, portkeyChain: { getContract: vi.fn() } }); + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + await contract.getFileDescriptorsSet(AELFTestnetContractAddress); + + expect(contract.methods).toEqual({ GetBalance: {} }); + }); + + it('should set methods error if getContractMethods rejected', async () => { + const contract = new PortkeyContractBasic({ ...mockProps, portkeyChain: { getContract: vi.fn() } }); + + // Mock + vi.mocked(getContractMethods).mockRejectedValueOnce('Failed to get GetBalance method'); + + await expect(contract.getFileDescriptorsSet(AELFTestnetContractAddress)).rejects.toThrow( + `"Failed to get GetBalance method"address:JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaEContract:getContractMethods`, + ); + }); + }); + + describe('checkMethods', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should check methods successfully if there is no method cache', async () => { + // Mock contract + const contract = new PortkeyContractBasic(mockProps); + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + await contract.checkMethods(); + + expect(contract.methods).toEqual({ GetBalance: {} }); + }); + }); + + describe('callViewMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should catch error if there is no contract method', async () => { + // Mock contract + const contract = new PortkeyContractBasic({ ...mockProps, portkeyChain: { getContract: vi.fn() } }); + vi.mocked(getContractMethods).mockResolvedValue({ TestMethod: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toEqual({ + error: { + code: 401, + message: 'Contract init error1', + }, + }); + }); + + it('should handle portkey view methods', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callViewMethod: vi.fn().mockResolvedValue({ balance: '1000' }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toHaveProperty('balance', '1000'); + }); + + it('should handle portkey view methods if callViewMethod return data', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callViewMethod: vi.fn().mockResolvedValue({ data: { balance: '1000' } }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toHaveProperty('balance', '1000'); + }); + + it('should handle portkey view methods if callViewMethod return data', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callViewMethod: vi.fn().mockResolvedValue({ error: 'Failed to get balance' }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toHaveProperty('error', 'Failed to get balance'); + }); + + it('should handle portkey view methods if callViewMethod rejected', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callViewMethod: vi.fn().mockRejectedValue('Failed to get balance'), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toHaveProperty('error', 'Failed to get balance'); + }); + }); + + describe('callSendMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle callSendMethod method', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callSendMethod: vi.fn().mockResolvedValue({ status: 'success' }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ CreateToken: {}, GetWalletInfo: {} }); + + const result = await contract.callSendMethod('CreateToken', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('status', 'success'); + }); + + it('should handle callSendMethod method if callSendMethod return data', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callSendMethod: vi.fn().mockResolvedValue({ data: { status: 'success' } }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ CreateToken: {}, GetWalletInfo: {} }); + + const result = await contract.callSendMethod('CreateToken', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('status', 'success'); + }); + + it('should return error if there is no contract', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn(), + }, + }); + + const result = await contract.callSendMethod('CreateToken', {}); + + expect(result).toHaveProperty('error', { code: 401, message: 'Contract init error2' }); + }); + + it('should return error if callSendMethod return string error', async () => { + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callSendMethod: vi.fn().mockResolvedValue({ error: 'Failed' }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ CreateToken: {}, GetWalletInfo: {} }); + + const result = await contract.callSendMethod('CreateToken', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('error', 'Failed'); + }); + + it('should return error if callSendMethod return error.message', async () => { + const error = { message: 'Failed to create token' }; + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callSendMethod: vi.fn().mockRejectedValue(error), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ CreateToken: {} }); + + const result = await contract.callSendMethod('CreateToken', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('error', error); + }); + + it('should return error if callSendMethod return error.Error', async () => { + const errorMessage = 'Failed to create token'; + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callSendMethod: vi.fn().mockRejectedValue({ Error: errorMessage }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ CreateToken: {} }); + + const result = await contract.callSendMethod('CreateToken', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('error', { message: errorMessage }); + }); + + it('should return error if callSendMethod return error.Status', async () => { + const errorMessage = 'Failed to create token'; + const contract = new PortkeyContractBasic({ + ...mockProps, + portkeyChain: { + getContract: vi.fn().mockReturnValue({ + callSendMethod: vi.fn().mockRejectedValue({ Status: errorMessage }), + }), + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ CreateToken: {} }); + + const result = await contract.callSendMethod('CreateToken', {}); + + expect(getContractMethods).toHaveBeenCalled(); + expect(result).toHaveProperty('error', { message: errorMessage }); + }); + }); + + describe('callSendPromiseMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle callSendPromiseMethod method', async () => { + const contract = new PortkeyContractBasic(mockProps); + + await expect(contract.callSendPromiseMethod('CreateToken', correctAelfAddress)).rejects.toThrow( + Error('Method not implemented.'), + ); + }); + }); +}); + +describe('PortkeySDKContractBasic', () => { + const mockProps = { + contractAddress: AELFTestnetContractAddress, + chainId: 'AELF' as ChainId, + } as any; + + describe('initialize', () => { + it('initialization class successful', () => { + const contract = new PortkeySDKContractBasic(mockProps); + + expect(contract.contractType).toBe('ELF'); + }); + }); + + describe('getFileDescriptorsSet', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should set methods', async () => { + // Mock contract + const contract = new PortkeySDKContractBasic(mockProps); + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + await contract.getFileDescriptorsSet(AELFTestnetContractAddress); + + expect(contract.methods).toEqual({ GetBalance: {} }); + }); + + it('should set methods error if getContractMethods rejected', async () => { + const contract = new PortkeySDKContractBasic(mockProps); + + // Mock + vi.mocked(getContractMethods).mockRejectedValueOnce('Failed to get GetBalance method'); + + await expect(contract.getFileDescriptorsSet(AELFTestnetContractAddress)).rejects.toThrow( + `"Failed to get GetBalance method"address:JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaEContract:getContractMethods`, + ); + }); + }); + + describe('checkMethods', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should check methods successfully if there is no method cache', async () => { + // Mock contract + const contract = new PortkeySDKContractBasic(mockProps); + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + await contract.checkMethods(); + + expect(contract.methods).toEqual({ GetBalance: {} }); + }); + }); + + describe('callViewMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should catch error if there is no contract method', async () => { + // Mock contract + const contract = new PortkeySDKContractBasic({ ...mockProps, aelfContract: vi.fn() }); + vi.mocked(getContractMethods).mockResolvedValue({ TestMethod: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toEqual({ + error: { + code: 401, + message: 'Contract init error1', + }, + }); + }); + + it('should handle portkey view methods', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + viewContract: { + GetBalance: { + call: vi.fn().mockResolvedValue({ balance: '1000' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toHaveProperty('balance', '1000'); + }); + + it('should handle portkey view methods if callViewMethod return data', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + viewContract: { + GetBalance: { + call: vi.fn().mockResolvedValue({ result: { balance: '1000' } }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(getContractMethods).toHaveBeenCalled(); + + expect(result).toHaveProperty('balance', '1000'); + }); + + it('should handle portkey view methods if callViewMethod return data', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + viewContract: { + GetBalance: { + call: vi.fn().mockResolvedValue({ error: 'Failed to get balance' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toHaveProperty('error', 'Failed to get balance'); + }); + + it('should handle portkey view methods if callViewMethod rejected', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + viewContract: { + GetBalance: { + call: vi.fn().mockRejectedValue('Failed to get balance'), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const result = await contract.callViewMethod('GetBalance'); + + expect(result).toHaveProperty('error', 'Failed to get balance'); + }); + }); + + describe('callSendMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle send method transaction flow and return tx id', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + sdkContract: { + callSendMethod: vi.fn().mockResolvedValue({ TransactionId: 'mockId' }), + }, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', correctAelfAddress, {}); + + expect(getContractMethods).toHaveBeenCalled(); + + expect(result.TransactionId).toBe('mockId'); + }); + + it('should handle send method transaction flow and return tx id', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + sdkContract: { + callSendMethod: vi.fn().mockResolvedValue({ error: 'Failed' }), + }, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', correctAelfAddress, {}); + + expect(getContractMethods).toHaveBeenCalled(); + + expect(result.error).toBe('Failed'); + }); + + it('should return error if callSendMethod return error.Error', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + sdkContract: { + callSendMethod: vi.fn().mockRejectedValue({ Error: 'Failed' }), + }, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', correctAelfAddress, {}); + + expect(getContractMethods).toHaveBeenCalled(); + + expect(result.error.message).toBe('Failed'); + }); + + it('should return error if callSendMethod return error.Status', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + sdkContract: { + callSendMethod: vi.fn().mockRejectedValue({ Status: 'Failed' }), + }, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', correctAelfAddress, {}); + + expect(getContractMethods).toHaveBeenCalled(); + + expect(result.error.message).toBe('Failed'); + }); + + it('should return error if callSendMethod return error.message', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + sdkContract: { + callSendMethod: vi.fn().mockRejectedValue({ message: 'Failed' }), + }, + }); + + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendMethod('GetTransaction', correctAelfAddress, {}); + + expect(getContractMethods).toHaveBeenCalled(); + + expect(result.error.message).toBe('Failed'); + }); + + it('should throw error if there is no sdkContract', async () => { + const contract = new PortkeySDKContractBasic(mockProps); + + const result = await contract.callSendMethod('GetTransaction', correctAelfAddress, {}); + + expect(result.error).toEqual({ code: 401, message: 'Contract init error2' }); + }); + + it('should handle send approve method return successfully', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + caContract: { + callSendMethod: vi.fn().mockResolvedValue({ result: 'success' }), + }, + portkeyWallet: { + extraInfo: { + portkeyInfo: { + caInfo: { + caHash: 'mockCaHash', + }, + chainId: 'AELF', + }, + }, + }, + }); + + // Mock managerApprove + vi.mocked(PortkeyDid.managerApprove).mockResolvedValue({ amount: '1000', guardiansApproved: [], symbol: 'ELF' }); + + const result = await contract.callSendMethod('approve', correctAelfAddress, {}); + + expect(PortkeyDid.managerApprove).toHaveBeenCalled(); + + expect(result.result).toBe('success'); + }); + + it('should return error when called approve method and callSendMethod return error', async () => { + const contract = new PortkeySDKContractBasic({ + ...mockProps, + caContract: { + callSendMethod: vi.fn().mockResolvedValue({ error: 'Failed to approve' }), + }, + }); + + // Mock managerApprove + vi.mocked(PortkeyDid.managerApprove).mockResolvedValue({ amount: '1000', guardiansApproved: [], symbol: 'ELF' }); + + const result = await contract.callSendMethod('approve', correctAelfAddress, {}); + + expect(PortkeyDid.managerApprove).toHaveBeenCalled(); + + expect(result.error).toBe('Failed to approve'); + }); + + it('should throw error if called approve method and there is no sdkContract', async () => { + const contract = new PortkeySDKContractBasic(mockProps); + + const result = await contract.callSendMethod('approve', correctAelfAddress, {}); + + expect(result.error).toEqual({ code: 401, message: 'Contract init error2' }); + }); + }); +}); + +describe('TONContractBasic Class', () => { + const mockProps = { + contractAddress: 'TON_ADDRESS', + chainId: 1007 as ChainId, + tonConnectUI: { + account: { address: 'TON_ACCOUNT' }, + sendTransaction: vi.fn().mockResolvedValue({ boc: 'TON_TX' }), + }, + } as any; + + describe('callViewMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should call GetBalance method and return correct result', async () => { + // Mock methods + const contract = new TONContractBasic(mockProps); + + const result = await contract.callViewMethod('GetBalance', []); + + expect(result).toHaveProperty('balance', '123'); + }); + + it('should call GetTokenInfo method and return error.message', async () => { + // Mock methods + const contract = new TONContractBasic(mockProps); + + const result = await contract.callViewMethod('GetTokenInfo', []); + + expect(result).toHaveProperty('error', { message: 'Failed to get token info' }); + }); + + it('should call GetAllowance method and return error.Error', async () => { + // Mock methods + const contract = new TONContractBasic(mockProps); + + const result = await contract.callViewMethod('GetAllowance', []); + + expect(result).toHaveProperty('error', { message: 'Failed to get allowance' }); + }); + + it('should call GetSwapId method and return error.Status', async () => { + // Mock methods + const contract = new TONContractBasic(mockProps); + + const result = await contract.callViewMethod('getSwapId', []); + + expect(result).toHaveProperty('error', { message: 'Failed to get swap id' }); + }); + }); + + describe('callSendMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle TON transactions', async () => { + // Mock methods + vi.mocked(getTransactionResponseHash).mockResolvedValue('TON_HASH'); + vi.mocked(TonContractCallData).createReceipt = vi.fn().mockResolvedValue({}); + + const contract = new TONContractBasic(mockProps); + + const result = await contract.callSendMethod('createReceipt', '', []); + + expect(result.TransactionId).toBe('TON_HASH'); + }); + + it('should catch error if getTransactionResponseHash rejected error.Error', async () => { + // Mock methods + vi.mocked(getTransactionResponseHash).mockRejectedValue({ + Error: 'Failed to get hash', + }); + vi.mocked(TonContractCallData).createReceipt = vi.fn().mockResolvedValue({}); + + const contract = new TONContractBasic(mockProps); + + const result = await contract.callSendMethod('createReceipt', '', []); + + expect(result).toHaveProperty('error', { message: 'Failed to get hash' }); + }); + + it('should catch error if getTransactionResponseHash rejected error.Error', async () => { + // Mock methods + vi.mocked(getTransactionResponseHash).mockRejectedValue({ + Status: 'Failed to get hash', + }); + vi.mocked(TonContractCallData).createReceipt = vi.fn().mockResolvedValue({}); + + const contract = new TONContractBasic(mockProps); + + const result = await contract.callSendMethod('createReceipt', '', []); + + expect(result).toHaveProperty('error', { message: 'Failed to get hash' }); + }); + + it('should catch error if getTransactionResponseHash rejected error.message', async () => { + // Mock methods + vi.mocked(getTransactionResponseHash).mockRejectedValue({ + message: 'Failed to get hash', + }); + vi.mocked(TonContractCallData).createReceipt = vi.fn().mockResolvedValue({}); + + const contract = new TONContractBasic(mockProps); + + const result = await contract.callSendMethod('createReceipt', '', []); + + expect(result).toHaveProperty('error', { message: 'Failed to get hash' }); + }); + + it('should handle missing TON connection', async () => { + const contract = new TONContractBasic({ ...mockProps, tonConnectUI: undefined }); + + const result = await contract.callSendMethod('createReceipt', '', []); + + expect(result.error).toBeDefined(); + }); + }); + + describe('callSendPromiseMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should call createReceipt correctly and return undefined', async () => { + const contract = new TONContractBasic({ ...mockProps, tonConnectUI: undefined }); + + const result = await contract.callSendPromiseMethod('createReceipt', '', []); + + expect(result).toBeUndefined(); + }); + }); +}); + +// Boundary and edge case tests +describe('Boundary Cases', () => { + const mockAelfProps = { + contractAddress: 'ELF_ADDRESS', + chainId: 999 as ChainId, + } as any; + + const mockERCProps = { + contractAddress: 'ERC_ADDRESS', + chainId: 11155111 as ChainId, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle invalid chain IDs', () => { + // Setup chain environment + vi.mocked(isELFChain).mockReturnValue(false); + + vi.mocked(getContractMethods).mockResolvedValue({ GetBalance: {} }); + + const contract = new ContractBasic({ contractAddress: '0x123', chainId: 11155111 as ChainId }); + + expect(contract.contractType).toBe('ERC'); + }); + + it('should handle empty contract addresses', () => { + const contract = new WB3ContractBasic({ ...mockERCProps, contractAddress: '' }); + expect(contract.address).toBe(''); + }); + + it('should handle non-object parameters', async () => { + const contract = new AElfContractBasic(mockAelfProps); + const result = await contract.callSendMethod('testMethod', 'invalid' as any); + expect(result.error).toBeDefined(); + }); + + // it('should handle network failures', async () => { + // const contract = new WB3ContractBasic(mockERCProps); + // // vi.spyOn(contract.contractForView.methods, 'testMethod').mockReturnValue({ + // // call: vi.fn().mockRejectedValue(new Error('Network error')), + // // }); + + // const result = await contract.callViewMethod('testMethod'); + // expect(result.error).toBeDefined(); + // }); +}); diff --git a/src/utils/__tests__/contract2.test.ts b/src/utils/__tests__/contract2.test.ts new file mode 100644 index 00000000..a1785ef2 --- /dev/null +++ b/src/utils/__tests__/contract2.test.ts @@ -0,0 +1,107 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { ChainId } from 'types'; +import { getContractMethods } from 'utils/aelfUtils'; +import { AElfContractBasic } from 'utils/contract'; + +// Mock external dependencies +vi.mock('utils/index', () => { + return { + isTonChain: vi.fn(), + sleep: vi.fn(), + }; +}); + +vi.mock('utils/aelfUtils', () => ({ + getContractMethods: vi.fn(), + transformArrayToMap: vi.fn((_, params) => params), + getTxResult: vi.fn(), + encodedTransfer: vi.fn(), + isELFChain: vi.fn(), +})); + +vi.mock('utils/tonContractCall', () => { + return { + CallTonContract: { + GetBalance: vi.fn().mockReturnValue({ balance: '123' }), + GetTokenInfo: vi.fn().mockImplementation(() => { + throw { message: 'Failed to get token info' }; + }), + GetAllowance: vi.fn().mockImplementation(() => { + throw { Error: 'Failed to get allowance' }; + }), + getSwapId: vi.fn().mockImplementation(() => { + throw { Status: 'Failed to get swap id' }; + }), + }, + TonContractCallData: { + createReceipt: vi.fn(), + }, + }; +}); + +vi.mock('utils/wagmi', () => { + return { + getGasPriceByWagmi: vi.fn(), + readContractByWagmi: vi.fn(), + waitForTransactionReceiptByWagmi: vi.fn(), + writeContractByWagmi: vi.fn(), + }; +}); + +// Mock ELFChainConstants and ERCChainConstants +vi.mock('constants/ChainConstants', () => { + return { + ELFChainConstants: { + aelfInstances: { + AELF: { + // appName: 'AELF_appName', + // options: {}, + appName: undefined, + options: undefined, + }, + }, + }, + }; +}); + +vi.mock('@aelf-web-login/wallet-adapter-bridge', () => { + return { + PortkeyDid: vi.fn(), + }; +}); + +describe('AElfContractBasic Class', () => { + const mockProps = { + contractAddress: 'ELF_ADDRESS', + chainId: 999 as ChainId, + } as any; + + describe('callSendPromiseMethod', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should catch 402 error', async () => { + const contract = new AElfContractBasic({ + ...mockProps, + aelfContract: { + GetTransaction: { + call: vi.fn().mockResolvedValue({ result: 'data' }), + }, + }, + }); + + // Mock + vi.mocked(getContractMethods).mockResolvedValue({ GetTransaction: {} }); + + const result = await contract.callSendPromiseMethod('GetTransaction', {}); + + expect(result).toEqual({ + error: { + code: 402, + message: 'connect aelf', + }, + }); + }); + }); +}); diff --git a/src/utils/__tests__/converter.test.ts b/src/utils/__tests__/converter.test.ts new file mode 100644 index 00000000..ddbf5653 --- /dev/null +++ b/src/utils/__tests__/converter.test.ts @@ -0,0 +1,265 @@ +import BigNumber from 'bignumber.js'; +import { + fixedDecimal, + fixedDecimalToFormat, + unitConverter, + unitConverterToFormat, + getShareOfPool, + percentConverter, + showUSDConverter, +} from 'utils/converter'; +import { describe, it, expect } from 'vitest'; + +describe('Chain Utils', () => { + describe('fixedDecimal', () => { + it('should return fixed decimal value', () => { + const result = fixedDecimal({ num: '123.456789', decimals: 2 }); + + expect(result).toBe('123.45'); + }); + + it('should return fixed decimal value if num is number', () => { + const result = fixedDecimal({ num: 123, decimals: 2 }); + + expect(result).toBe('123'); + }); + + it('should return fixed decimal value if num is undefined', () => { + const result = fixedDecimal({ num: undefined, decimals: 2 }); + + expect(result).toBe('0'); + }); + + it('should return fixed decimal value if num is negative', () => { + const result = fixedDecimal({ num: -123, decimals: 2 }); + + expect(result).toBe('-123'); + }); + + it('should return "0" for invalid input', () => { + const result = fixedDecimal({ num: 'invalid', decimals: 2 }); + + expect(result).toBe('0'); + }); + }); + + describe('fixedDecimalToFormat', () => { + it('should return formatted decimal value', () => { + const result = fixedDecimalToFormat({ num: '123.456789', decimals: 2 }); + + expect(result).toBe('123.45'); + }); + + it('should return formatted decimal value if num is undefined', () => { + const result = fixedDecimalToFormat({ num: undefined, decimals: 2 }); + + expect(result).toBe('0'); + }); + + it('should return formatted decimal value if num is negative', () => { + const result = fixedDecimalToFormat({ num: -123, decimals: 2 }); + + expect(result).toBe('-123'); + }); + + it('should return "0" for invalid input', () => { + const result = fixedDecimalToFormat({ num: 'invalid', decimals: 2 }); + + expect(result).toBe('0'); + }); + }); + + describe('unitConverter', () => { + it('should convert number to unit format', () => { + const result = unitConverter({ num: '123456789', decimals: 2 }); + + expect(result).toBe('123.45M'); + }); + + it('should return formatted decimal value if num is negative string', () => { + const result = unitConverter({ num: '-123456789', decimals: 2 }); + + expect(result).toBe('-123.456789M'); + }); + + it('should return formatted decimal value if num is negative', () => { + const result = unitConverter({ num: -123, decimals: 2 }); + + expect(result).toBe('-123'); + }); + + it('should return formatted decimal value if num is undefined', () => { + const result = unitConverter({ num: undefined, decimals: 2 }); + + expect(result).toBe('0'); + }); + + it('should return formatted decimal value if num is number', () => { + const result = unitConverter(123); + + expect(result).toBe('123'); + }); + + it('should return formatted decimal value if num is BigNumber', () => { + const result = unitConverter(new BigNumber(123)); + + expect(result).toBe('123'); + }); + + it('should return formatted decimal value if num is > 1e3', () => { + const result = unitConverter({ num: '123456789', skipConverterIndex: 10 }); + + expect(result).toBe('123456789'); + }); + + it('should return default value for invalid input', () => { + const result = unitConverter({ num: 'invalid', decimals: 2 }); + expect(result).toBe('0'); + }); + }); + + describe('unitConverterToFormat', () => { + it('should convert number to formatted unit format', () => { + const result = unitConverterToFormat({ num: '123456789', decimals: 2 }); + + expect(result).toBe('123.45M'); + }); + + it('should convert number to formatted unit format if num is negative number', () => { + const result = unitConverterToFormat({ num: '-123456789', decimals: 2 }); + + expect(result).toBe('-123.456789M'); + }); + + it('should convert number to formatted unit format if num is negative', () => { + const result = unitConverterToFormat({ num: -123, decimals: 2 }); + + expect(result).toBe('-123'); + }); + + it('should convert number to formatted unit format if num is undefined', () => { + const result = unitConverterToFormat({ num: undefined, decimals: 2 }); + + expect(result).toBe('0'); + }); + + it('should convert number to formatted unit format if num is number', () => { + const result = unitConverterToFormat(123); + + expect(result).toBe('123'); + }); + + it('should convert number to formatted unit format if num is BigNumber', () => { + const result = unitConverterToFormat(new BigNumber(123)); + + expect(result).toBe('123'); + }); + + it('should return default value for invalid input', () => { + const result = unitConverterToFormat({ num: 'invalid', decimals: 2 }); + + expect(result).toBe('0'); + }); + }); + + describe('getShareOfPool', () => { + it('should return share of pool', () => { + const result = getShareOfPool('100', '200'); + + expect(result).toBe('33.33'); + }); + + it('should return share of pool if input is Bignumber', () => { + const result = getShareOfPool(new BigNumber(100), '200'); + + expect(result).toBe('33.33'); + }); + + it('should return share of pool if input is undefined', () => { + const result = getShareOfPool(undefined, '200'); + + expect(result).toBe('0.00'); + }); + + it('should return share of pool if not input total', () => { + const result = getShareOfPool(undefined); + + expect(result).toBe('0.00'); + }); + + it('should return "0.00" for invalid input', () => { + const result = getShareOfPool('invalid', '200'); + + expect(result).toBe('0.00'); + }); + }); + + describe('percentConverter', () => { + it('should convert number to percentage', () => { + const result = percentConverter('0.5'); + + expect(result).toBe('50.00'); + }); + + it('should convert number to percentage if input is a negative number', () => { + const result = percentConverter(-0.5); + + expect(result).toBe('0.00'); + }); + + it('should convert number to percentage if input is a BigNumber', () => { + const result = percentConverter(new BigNumber('0.5')); + + expect(result).toBe('50.00'); + }); + + it('should convert number to percentage if input is > 100', () => { + const result = percentConverter(new BigNumber(10000)); + + expect(result).toBe('100.00'); + }); + + it('should convert number to percentage if input is undefined', () => { + const result = percentConverter(undefined as any); + + expect(result).toBe('0.00'); + }); + + it('should return "0.00" for invalid input', () => { + const result = percentConverter('invalid'); + + expect(result).toBe('0.00'); + }); + }); + + describe('showUSDConverter', () => { + it('should convert number to USD format', () => { + const result = showUSDConverter({ num: '123456789', decimals: 2 }); + + expect(result).toBe('$123.45M'); + }); + + it('should convert number to USD format if input is a BigNumber', () => { + const result = showUSDConverter({ num: new BigNumber('123456789'), decimals: 2 }); + + expect(result).toBe('$123.45M'); + }); + + it('should convert number to USD format if input is a undefined', () => { + const result = showUSDConverter({ num: undefined, decimals: 2 }); + + expect(result).toBe('--'); + }); + + it('should convert number to USD format if input is a string', () => { + const result = showUSDConverter('123456'); + + expect(result).toBe('$123456'); + }); + + it('should return default value for invalid input', () => { + const result = showUSDConverter({ num: 'invalid', decimals: 2 }); + expect(result).toBe('--'); + }); + }); +}); diff --git a/src/utils/__tests__/crossChain.test.ts b/src/utils/__tests__/crossChain.test.ts new file mode 100644 index 00000000..0c496091 --- /dev/null +++ b/src/utils/__tests__/crossChain.test.ts @@ -0,0 +1,1428 @@ +import { describe, it, expect, vi, beforeEach, Mock } from 'vitest'; +import { + CreateReceipt, + CrossChainCreateToken, + CrossChainReceive, + CrossChainTransfer, + getReceiptLimit, + getSwapId, + getSwapLimit, + LockToken, + SwapToken, + ValidateTokenInfoExists, +} from 'utils/crossChain'; +import { ContractBasic } from 'utils/contract'; +import { ChainId, TokenInfo } from 'types'; +import { base58ToChainId, formatAddress, getChainIdToMap } from 'utils/chain'; +import { getTokenInfoByWhitelist } from 'utils/whitelist'; +import { SupportedChainId, SupportedELFChainId } from 'constants/chain'; +import CommonMessage from 'components/CommonMessage'; +import BigNumber from 'bignumber.js'; +import { timesDecimals } from 'utils/calculate'; +import { isIncludesChainId, isTonChain } from 'utils/index'; +import { checkApprove } from 'contracts/index'; +import { CrossFeeToken, REQ_CODE } from 'constants/misc'; +import { encodeTransaction, getAElf, uint8ArrayToHex } from 'utils/aelfUtils'; +import { TransactionResult } from '@aelf-react/types'; +import AElf from 'aelf-sdk'; +import { CrossChainItem } from 'types/api'; + +// Mock external dependencies +vi.mock('components/CommonMessage', () => { + return { + default: { + error: vi.fn(), + }, + }; +}); + +vi.mock('utils/contract', () => { + // Mock ContractBasic + class MockContractBasic { + callSendMethod = vi.fn(); + callViewMethod = vi.fn(); + } + + return { + ContractBasic: MockContractBasic, + }; +}); + +// Mock formatAddress, base58ToChainId and getChainIdToMap +vi.mock('utils/chain', async (importOriginal) => { + const actual: any = await importOriginal(); + + return { + ...actual, + base58ToChainId: vi.fn(), + formatAddress: vi.fn(), + getChainIdToMap: vi.fn(), + }; +}); + +vi.mock('utils/index', async (importOriginal) => { + const actual: any = await importOriginal(); + + return { + ...actual, + isIncludesChainId: vi.fn(), + isTonChain: vi.fn().mockReturnValue(false), + }; +}); + +vi.mock('utils/whitelist', () => ({ + getTokenInfoByWhitelist: vi.fn(), +})); + +vi.mock('utils/calculate', () => ({ + timesDecimals: vi.fn().mockReturnValue({ + toFixed: () => '1000000000000000000', + }), +})); + +vi.mock('contracts/index', async (importOriginal) => { + const actual: any = await importOriginal(); + return { + ...actual, + checkApprove: vi.fn().mockResolvedValue(1), + }; +}); + +vi.mock('constants/index', async (importOriginal) => { + const actual: any = await importOriginal(); + + return { + ...actual, + FormatTokenList: [ + { + fromChainId: [56], + toChainId: ['AELF', 'tDVV', 'tDVW'], + fromSymbol: 'WBNB', + toSymbol: 'BNB', + }, + { + fromChainId: [1, 8453], + toChainId: ['AELF', 'tDVV', 'tDVW'], + fromSymbol: 'WETH', + toSymbol: 'ETH', + }, + ], + }; +}); + +vi.mock('aelf-sdk'); + +vi.mock('utils/aelfUtils', () => ({ + encodeTransaction: vi.fn(), + getAElf: vi.fn((_, params) => params), + uint8ArrayToHex: vi.fn(), +})); + +const mainChainId = 'AELF' as ChainId; +const dappChainId = 'tDVV' as ChainId; + +// Mock TokenInfo +const mockToken: TokenInfo = { + symbol: 'ELF', + issueChainId: 1100, + decimals: 8, + address: 'mockAddress', + // Add other required properties if necessary +}; + +// Mock ChainId +const mockToChainId = dappChainId; + +// Mock account and to address +const mockAccount = 'AELF1234567890123456789012345678901234567890123456789'; +const mockToAddress = 'AELF9876543210987654321098765432109876543210987654321'; + +// Mock amount +const mockAmount = '10'; + +// Mock TokenInfo +const mockTokenInfo: TokenInfo = { + symbol: 'ELF', + issueChainId: 1100, + decimals: 8, + // Add other required properties if necessary +}; + +describe('CrossChainTransfer', () => { + let mockContract: ContractBasic; + + beforeEach(() => { + vi.clearAllMocks(); + + mockContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + }); + + it('should call callSendMethod with correct parameters', async () => { + (base58ToChainId as any).mockReturnValue('base58ChainId'); + + await CrossChainTransfer({ + contract: mockContract, + account: mockAccount, + to: mockToAddress, + token: mockToken, + toChainId: mockToChainId, + amount: mockAmount, + }); + + expect(mockContract.callSendMethod).toHaveBeenCalledWith( + 'CrossChainTransfer', + mockAccount, + [mockToAddress, mockToken.symbol, mockAmount, ' ', 'base58ChainId', mockToken.issueChainId], + { onMethod: 'receipt' }, + ); + }); + + it('should handle errors from callSendMethod', async () => { + const errorMessage = 'Transaction failed'; + (mockContract.callSendMethod as Mock).mockRejectedValue(new Error(errorMessage)); + + await expect( + CrossChainTransfer({ + contract: mockContract, + account: mockAccount, + to: mockToAddress, + token: mockToken, + toChainId: mockToChainId, + amount: mockAmount, + }), + ).rejects.toThrow(errorMessage); + }); +}); + +describe('CrossChainReceive', () => { + const mockTxId = 'mock_tx_123'; + const mockCrossChainItem: CrossChainItem = { + transferTransactionId: mockTxId, + fromChainId: SupportedELFChainId.AELF, + }; + let mockSendCrossChainContract: ContractBasic; + let mockSendTokenContract: ContractBasic; + let mockReceiveTokenContract: ContractBasic; + + beforeEach(() => { + vi.clearAllMocks(); + + mockSendCrossChainContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + mockSendTokenContract = new ContractBasic({ contractAddress: '0xSend' }); + mockReceiveTokenContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + + // Default mock implementations + vi.mocked(getAElf).mockReturnValue({ + chain: { + getTxResult: vi.fn().mockResolvedValue({ + BlockNumber: 1000, + Transaction: { + Params: JSON.stringify({ symbol: 'ELF' }), + From: 'from', + To: 'to', + MethodName: 'sign', + RefBlockNumber: 1000, + RefBlockPrefix: 'refBlockPrefix', + Signature: 'signature', + }, + } as unknown as TransactionResult), + getMerklePathByTxId: vi.fn().mockResolvedValue({ + MerklePathNodes: [ + { Hash: '1', IsLeftChildNode: true }, + { Hash: '2', IsLeftChildNode: false }, + ], + }), + }, + }); + + // Mock AElf.pbUtils.getSerializedDataFromLog + AElf.pbUtils.getTransaction.mockReturnValue({}); + }); + + // Test 1: Main success flow for AELF chain + test('should process AELF chain transactions without parent chain lookup', async () => { + // Mock encodeTransaction + vi.mocked(encodeTransaction).mockReturnValue(Buffer.from(mockTxId)); + + const receiveTokenContractEncodedTx = vi.fn().mockResolvedValue('mock_encode_value'); + const receiveTokenContractCallSendMethod = vi.fn().mockResolvedValue({ result: 'success' }); + + const result = await CrossChainReceive({ + sendChainID: SupportedELFChainId.AELF, + receiveItem: mockCrossChainItem, + sendCrossChainContract: mockSendCrossChainContract, + sendTokenContract: { + ...mockSendTokenContract, + encodedTx: receiveTokenContractEncodedTx, + }, + receiveTokenContract: { + ...mockReceiveTokenContract, + callSendMethod: receiveTokenContractCallSendMethod, + }, + }); + + expect(result).toEqual({ result: 'success' }); + + // Verify transaction lookup + expect(getAElf).toHaveBeenCalledWith('AELF'); + + // Verify final contract call parameters + expect(receiveTokenContractCallSendMethod).toHaveBeenCalledWith( + 'CrossChainReceiveToken', + '', + expect.objectContaining({ + parentChainHeight: 1000, // Original BlockNumber + transferTransactionBytes: 'bW9ja190eF8xMjM=', + }), + ); + }); + + // Test 2: Non-AELF chain with parent chain processing + test('should merge merkle paths and update parent height for non-AELF chains', async () => { + // Mock encodeTransaction + vi.mocked(encodeTransaction).mockReturnValue(Buffer.from(mockTxId)); + + const sendCrossChainContractCallViewMethod = vi.fn().mockResolvedValue({ + merklePathFromParentChain: { + merklePathNodes: [ + { hash: '1', sLeftChildNode: true }, + { hash: '2', isLeftChildNode: false }, + ], + }, + boundParentChainHeight: 12345, + }); + const receiveTokenContractEncodedTx = vi.fn().mockResolvedValue('mock_encode_value'); + const receiveTokenContractCallSendMethod = vi.fn().mockResolvedValue({ result: 'success' }); + + const result = await CrossChainReceive({ + sendChainID: 'NON_AELF' as ChainId, + receiveItem: mockCrossChainItem, + sendCrossChainContract: { + ...mockSendCrossChainContract, + callViewMethod: sendCrossChainContractCallViewMethod, + }, + sendTokenContract: { + ...mockSendTokenContract, + encodedTx: receiveTokenContractEncodedTx, + }, + receiveTokenContract: { + ...mockReceiveTokenContract, + callSendMethod: receiveTokenContractCallSendMethod, + }, + }); + + expect(result).toEqual({ result: 'success' }); + + // Verify receiveTokenContractCallSendMethod called + const callArgs = receiveTokenContractCallSendMethod.mock.calls[0]; + + expect(callArgs).toEqual(expect.arrayContaining(['CrossChainReceiveToken'])); + }); + + // Test 3: Main success flow for AELF chain with Uint8Array signature + test('should handle AELF chain correctly with Uint8Array signature', async () => { + // Mock encodeTransaction + const uint8Array = new Uint8Array([0, 1, 2, 3, 15, 16, 255]); + vi.mocked(encodeTransaction).mockReturnValue(uint8Array); + + const expectedHex = '000102030f10ff'; + vi.mocked(uint8ArrayToHex).mockReturnValue(expectedHex); + + const receiveTokenContractEncodedTx = vi.fn().mockResolvedValue('mock_encode_value'); + const receiveTokenContractCallSendMethod = vi.fn().mockResolvedValue({ result: 'success' }); + + const result = await CrossChainReceive({ + sendChainID: SupportedELFChainId.AELF, + receiveItem: mockCrossChainItem, + sendCrossChainContract: mockSendCrossChainContract, + sendTokenContract: { + ...mockSendTokenContract, + encodedTx: receiveTokenContractEncodedTx, + }, + receiveTokenContract: { + ...mockReceiveTokenContract, + callSendMethod: receiveTokenContractCallSendMethod, + }, + }); + + expect(result.result).toBe('success'); + + // Verify chain interactions + expect(getAElf).toHaveBeenCalledWith(SupportedELFChainId.AELF); + + // Verify final contract call parameters + expect(receiveTokenContractCallSendMethod).toHaveBeenCalledWith( + 'CrossChainReceiveToken', + '', + expect.objectContaining({ + fromChainId: 'base58ChainId', + transferTransactionBytes: 'AAECAw8Q/w==', + }), + ); + }); + + // Test 4: Error handling in transaction lookup + test('should propagate errors from getTxResult', async () => { + // Default mock implementations + vi.mocked(getAElf).mockReturnValue({ + chain: { + getTxResult: vi.fn().mockRejectedValueOnce(new Error('Blockchain timeout')), + }, + }); + + await expect( + CrossChainReceive({ + sendChainID: SupportedELFChainId.AELF, + receiveItem: mockCrossChainItem, + sendCrossChainContract: mockSendCrossChainContract, + sendTokenContract: mockSendTokenContract, + receiveTokenContract: mockReceiveTokenContract, + }), + ).rejects.toThrow('Blockchain timeout'); + }); +}); + +describe('ValidateTokenInfoExists', () => { + let mockContract: ContractBasic; + + beforeEach(() => { + vi.clearAllMocks(); + mockContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + }); + + it('should call callSendMethod with correct parameters', async () => { + await ValidateTokenInfoExists({ + contract: mockContract, + tokenInfo: mockTokenInfo, + account: mockAccount, + }); + + expect(mockContract.callSendMethod).toHaveBeenCalledWith('ValidateTokenInfoExists', mockAccount, mockTokenInfo); + }); + + it('should handle errors from callSendMethod', async () => { + const errorMessage = 'Validation failed'; + (mockContract.callSendMethod as Mock).mockRejectedValue(new Error(errorMessage)); + + await expect( + ValidateTokenInfoExists({ + contract: mockContract, + tokenInfo: mockTokenInfo, + account: mockAccount, + }), + ).rejects.toThrow(errorMessage); + }); +}); + +describe('CrossChainCreateToken', () => { + const mockTxId = 'mock_tx_123'; + let mockSendCrossChainContract: ContractBasic; + let mockSendTokenContract: ContractBasic; + let mockReceiveTokenContract: ContractBasic; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + + mockSendCrossChainContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + mockSendTokenContract = new ContractBasic({ contractAddress: '0xSend' }); + mockReceiveTokenContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + + // Default mock implementations + vi.mocked(getAElf).mockReturnValue({ + chain: { + getTxResult: vi.fn().mockResolvedValue({ + BlockNumber: 1000, + Transaction: { + Params: JSON.stringify({ symbol: 'ELF' }), + From: 'from', + To: 'to', + MethodName: 'sign', + RefBlockNumber: 1000, + RefBlockPrefix: 'refBlockPrefix', + Signature: 'signature', + }, + } as unknown as TransactionResult), + getMerklePathByTxId: vi.fn().mockResolvedValue({ + MerklePathNodes: [ + { Hash: '1', IsLeftChildNode: true }, + { Hash: '2', IsLeftChildNode: false }, + ], + }), + }, + }); + + // Mock AElf.pbUtils.getSerializedDataFromLog + AElf.pbUtils.getTransaction.mockReturnValue({}); + }); + + // Test case 1: Main success scenario for AELF chain + test('should handle AELF chain correctly', async () => { + // Mock encodeTransaction + vi.mocked(encodeTransaction).mockReturnValue(Buffer.from(mockTxId)); + + const receiveTokenContractEncodedTx = vi.fn().mockResolvedValue('mock_encode_value'); + const receiveTokenContractCallSendMethod = vi.fn().mockResolvedValue({ result: 'success' }); + + const result = await CrossChainCreateToken({ + sendChainID: SupportedELFChainId.AELF, + transactionId: mockTxId, + sendCrossChainContract: mockSendCrossChainContract, + sendTokenContract: { + ...mockSendTokenContract, + encodedTx: receiveTokenContractEncodedTx, + }, + receiveTokenContract: { + ...mockReceiveTokenContract, + callSendMethod: receiveTokenContractCallSendMethod, + }, + }); + + expect(result.result).toBe('success'); + + // Verify chain interactions + expect(getAElf).toHaveBeenCalledWith(SupportedELFChainId.AELF); + + // Verify final contract call parameters + expect(receiveTokenContractCallSendMethod).toHaveBeenCalledWith( + 'CrossChainCreateToken', + '', + expect.objectContaining({ + fromChainId: 'base58ChainId', + transactionBytes: 'bW9ja190eF8xMjM=', + }), + ); + }); + + // Test case 2: Non-AELF chain with parent chain lookup + test('should merge merkle paths for non-AELF chains', async () => { + // Mock encodeTransaction + vi.mocked(encodeTransaction).mockReturnValue(Buffer.from(mockTxId)); + + const sendCrossChainContractCallViewMethod = vi.fn().mockResolvedValue({ + merklePathFromParentChain: { + merklePathNodes: [ + { hash: '1', sLeftChildNode: true }, + { hash: '2', isLeftChildNode: false }, + ], + }, + boundParentChainHeight: 12345, + }); + + const receiveTokenContractEncodedTx = vi.fn().mockResolvedValue('mock_encode_value'); + const receiveTokenContractCallSendMethod = vi.fn().mockResolvedValue({ result: 'success' }); + + const result = await CrossChainCreateToken({ + sendChainID: 'NON_AELF' as ChainId, + transactionId: mockTxId, + sendCrossChainContract: { + ...mockSendCrossChainContract, + callViewMethod: sendCrossChainContractCallViewMethod, + }, + sendTokenContract: { + ...mockSendTokenContract, + encodedTx: receiveTokenContractEncodedTx, + }, + receiveTokenContract: { + ...mockReceiveTokenContract, + callSendMethod: receiveTokenContractCallSendMethod, + }, + }); + + expect(result.result).toBe('success'); + + // Verify receiveTokenContractCallSendMethod called + const callArgs = receiveTokenContractCallSendMethod.mock.calls[0]; + + expect(callArgs).toEqual(expect.arrayContaining(['CrossChainCreateToken'])); + }); + + test('should handle AELF chain correctly with Uint8Array signature', async () => { + // Mock encodeTransaction + const uint8Array = new Uint8Array([0, 1, 2, 3, 15, 16, 255]); + vi.mocked(encodeTransaction).mockReturnValue(uint8Array); + + const expectedHex = '000102030f10ff'; + vi.mocked(uint8ArrayToHex).mockReturnValue(expectedHex); + + const receiveTokenContractEncodedTx = vi.fn().mockResolvedValue('mock_encode_value'); + const receiveTokenContractCallSendMethod = vi.fn().mockResolvedValue({ result: 'success' }); + + const result = await CrossChainCreateToken({ + sendChainID: SupportedELFChainId.AELF, + transactionId: mockTxId, + sendCrossChainContract: mockSendCrossChainContract, + sendTokenContract: { + ...mockSendTokenContract, + encodedTx: receiveTokenContractEncodedTx, + }, + receiveTokenContract: { + ...mockReceiveTokenContract, + callSendMethod: receiveTokenContractCallSendMethod, + }, + }); + + expect(result.result).toBe('success'); + + // Verify chain interactions + expect(getAElf).toHaveBeenCalledWith(SupportedELFChainId.AELF); + + // Verify final contract call parameters + expect(receiveTokenContractCallSendMethod).toHaveBeenCalledWith( + 'CrossChainCreateToken', + '', + expect.objectContaining({ + parentChainHeight: 1000, + transactionBytes: 'AAECAw8Q/w==', + }), + ); + }); +}); + +describe('CreateReceipt', () => { + const mockBridgeContract = { + contractType: 'ELF', + address: 'bridge-address', + callSendMethod: vi.fn().mockResolvedValue({ transactionId: 'tx123' }), + }; + + const baseParams = { + fromToken: 'ELF', + account: 'user-account', + bridgeContract: mockBridgeContract, + amount: '100', + fromChainId: SupportedChainId.BSC_MAINNET, + toChainId: SupportedELFChainId.AELF, + to: 'recipient-address', + tokenContract: {} as ContractBasic, + } as any; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(checkApprove).mockReset().mockResolvedValue(REQ_CODE.Success); + mockBridgeContract.contractType = 'ELF'; + }); + + // Test address formatting for non-ELF chains + test('should format address for non-ELF chains', async () => { + mockBridgeContract.contractType = 'OTHER'; + const result = await CreateReceipt(baseParams); + + expect(result.transactionId).toBe('tx123'); + expect(formatAddress).not.toHaveBeenCalled(); + }); + + // Test CrossFee approval flow + test('should handle CrossFee approval for ELF chains', async () => { + const params = { ...baseParams, fromToken: 'OTHER_TOKEN', crossFee: '1' }; + + const result = await CreateReceipt(baseParams); + + expect(result.transactionId).toBe('tx123'); + + // Verify CrossFee approval check + expect(checkApprove).toHaveBeenCalledWith( + 56, + CrossFeeToken, + params.account, + mockBridgeContract.address, + '100', + undefined, + params.tokenContract, + ); + }); + + // Test approval failure handling + test('should throw error when approval fails', async () => { + vi.mocked(checkApprove).mockResolvedValue(REQ_CODE.Fail); + + await expect( + CreateReceipt({ + ...baseParams, + fromToken: 'OTHER_TOKEN', + crossFee: '1', + }), + ).rejects.toBe(REQ_CODE.Fail); + }); + + // Test amount calculation with CrossFee + test('should calculate correct amount with CrossFee', async () => { + const result = await CreateReceipt({ + ...baseParams, + fromToken: CrossFeeToken, + crossFee: '1', + bridgeContract: { ...mockBridgeContract, address: '' }, + }); + + expect(result.transactionId).toBe('tx123'); + // Verify amount calculation + expect(timesDecimals).toHaveBeenCalledWith('1', 8); + expect(checkApprove).toHaveBeenCalledWith( + 56, + CrossFeeToken, + 'user-account', + '', + '1000000000000000100', + undefined, + {}, + ); + }); + + test('should calculate correct amount with aelf chain USDT', async () => { + const result = await CreateReceipt({ + ...baseParams, + fromToken: 'USDT', + crossFee: '1', + bridgeContract: { ...mockBridgeContract, address: '' }, + }); + + expect(result.transactionId).toBe('tx123'); + // Verify amount calculation + expect(timesDecimals).toHaveBeenCalledWith('1', 8); + expect(checkApprove).toHaveBeenCalledWith(56, 'ELF', 'user-account', '', '1000000000000000000', undefined, {}); + }); + + test('should throw error if return checkApprove failed', async () => { + vi.mocked(checkApprove).mockResolvedValue(REQ_CODE.Fail); + + await expect( + CreateReceipt({ + ...baseParams, + fromToken: CrossFeeToken, + crossFee: '1', + bridgeContract: { ...mockBridgeContract, address: '' }, + }), + ).rejects.toThrow(); + }); + + // Test TON chain special handling + test('should skip approval for TON chains', async () => { + mockBridgeContract.contractType = 'TON'; + const result = await CreateReceipt(baseParams); + + expect(result.transactionId).toBe('tx123'); + expect(checkApprove).not.toHaveBeenCalled(); + }); + + // Test ELF chain method signature + test('should use ELF-specific method parameters', async () => { + const result = await CreateReceipt(baseParams); + + expect(result.transactionId).toBe('tx123'); + + expect(mockBridgeContract.callSendMethod).toHaveBeenCalledWith('createReceipt', baseParams.account, [ + baseParams.fromToken, + baseParams.account, + baseParams.to, + baseParams.amount, + undefined, + 0, + ]); + }); + + // Test non-ELF chain method signature + test('should use standard method parameters for non-ELF chains', async () => { + mockBridgeContract.contractType = 'OTHER'; + const result = await CreateReceipt(baseParams); + + expect(result.transactionId).toBe('tx123'); + + expect(mockBridgeContract.callSendMethod).toHaveBeenCalledWith( + 'createReceipt', + baseParams.account, + [baseParams.fromToken, baseParams.amount, undefined, 'recipient-address'], + { onMethod: 'transactionHash' }, + ); + }); + + // Test TON chain flag handling + test('should handle TON chain destination', async () => { + vi.mocked(isTonChain).mockReturnValue(true); + const result = await CreateReceipt(baseParams); + + expect(result.transactionId).toBe('tx123'); + + expect(mockBridgeContract.callSendMethod).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.arrayContaining([1]), + ); + }); + + // Test parameter validation + test('should handle missing required parameters', async () => { + // @ts-ignore Test invalid input + await expect(CreateReceipt({})).rejects.toThrow(); + }); + + // Test decimal conversion edge cases + test('should handle zero crossFee', async () => { + const result = await CreateReceipt({ + ...baseParams, + fromToken: CrossFeeToken, + crossFee: '0', + }); + + expect(result.transactionId).toBe('tx123'); + + expect(checkApprove).toHaveBeenCalledWith( + 56, + 'ELF', + 'user-account', + 'bridge-address', + '1000000000000000100', + undefined, + {}, + ); + }); + + // Test console logging + test('should log correct parameters', async () => { + const consoleSpy = vi.spyOn(console, 'log'); + const result = await CreateReceipt(baseParams); + + expect(result.transactionId).toBe('tx123'); + + expect(consoleSpy).toHaveBeenCalledWith( + true, + baseParams.toChainId, + expect.arrayContaining([baseParams.fromToken]), + '======toChainId', + ); + }); +}); + +describe('LockToken', () => { + // Mock data + const mockAccount = 'AELF1234567890123456789012345678901234567890123456789'; + const mockBridgeContract = new ContractBasic({ contractAddress: 'mockBridgeContractAddress' }); + const mockAmount = '10'; + const mockToChainId = 'AELF' as ChainId; + const mockTo = 'AELF9876543210987654321098765432109876543210987654321'; + const mockFormattedAddress = 'formattedAddress'; + + beforeEach(() => { + vi.clearAllMocks(); + (formatAddress as any).mockReturnValue(mockFormattedAddress); + (getChainIdToMap as any).mockReturnValue(mainChainId); + }); + + it('should call callSendMethod with correct parameters', async () => { + await LockToken({ + account: mockAccount, + bridgeContract: mockBridgeContract, + amount: mockAmount, + toChainId: mockToChainId, + to: mockTo, + }); + + expect(getChainIdToMap).toHaveBeenCalledWith(mockToChainId); + expect(mockBridgeContract.callSendMethod).toHaveBeenCalled(); + }); + + it('should handle errors from callSendMethod', async () => { + const errorMessage = 'Lock failed'; + (mockBridgeContract.callSendMethod as Mock).mockRejectedValue(new Error(errorMessage)); + + await expect( + LockToken({ + account: mockAccount, + bridgeContract: mockBridgeContract, + amount: mockAmount, + toChainId: mockToChainId, + to: mockTo, + }), + ).rejects.toThrow(errorMessage); + }); +}); + +describe('SwapToken', () => { + let mockContract: ContractBasic; + + const baseParams = { + toAccount: 'mock-account', + receiveItem: { + transferAmount: 100, + receiptId: '123', + toAddress: '0x123', + transferToken: { symbol: 'WETH', decimals: 18 }, + toChainId: SupportedELFChainId.AELF, + fromChainId: SupportedChainId.MAINNET, + }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + + mockContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + + (getChainIdToMap as any).mockReturnValue(mainChainId); + + vi.mocked(getTokenInfoByWhitelist).mockReturnValue(mockTokenInfo); + }); + + // Test early return conditions + test('should return undefined when missing required parameters', async () => { + // Test missing toChainId + const result1 = await SwapToken({ + ...baseParams, + bridgeOutContract: { ...mockContract, contractType: 'ERC' as any }, + receiveItem: { ...baseParams.receiveItem, toChainId: undefined }, + }); + expect(result1).toBeUndefined(); + + // Test missing transferToken.symbol + const result2 = await SwapToken({ + ...baseParams, + bridgeOutContract: { ...mockContract, contractType: 'ERC' as any }, + receiveItem: { + ...baseParams.receiveItem, + transferToken: { ...baseParams.receiveItem.transferToken, symbol: '' }, // symbol: undefined + }, + }); + expect(result2).toBeUndefined(); + + // Test missing receiveItem + const result3 = await SwapToken({ + ...baseParams, + bridgeOutContract: { ...mockContract, contractType: 'ERC' as any }, + receiveItem: undefined as any, + }); + expect(result3).toBeUndefined(); + }); + + // Test token symbol conversion + test('should convert symbol using FormatTokenList', async () => { + await SwapToken({ + ...baseParams, + bridgeOutContract: { ...mockContract, contractType: 'ERC' as any, callViewMethod: vi.fn().mockResolvedValue({}) }, + }); + + // Verify token info lookup uses converted symbol + expect(getTokenInfoByWhitelist).toHaveBeenCalledWith( + 'AELF', + 'WETH', // Converted symbol + ); + }); + + // Test ELF contract type handling + test('should handle ELF contract type swap ID', async () => { + vi.mocked(isIncludesChainId).mockReturnValue(true); + + const result = await SwapToken({ + ...baseParams, + bridgeOutContract: { + ...mockContract, + contractType: 'ELF' as any, + callViewMethod: vi.fn().mockResolvedValueOnce({ result: 'elf-swap-id' }), + callSendMethod: vi.fn().mockResolvedValueOnce({ result: 'success' }), + }, + }); + + expect(result).toEqual({ result: 'success' }); + }); + + // Test ERC contract type handling + test('should handle ERC contract type swap ID', async () => { + const result = await SwapToken({ + ...baseParams, + bridgeOutContract: { + ...mockContract, + contractType: 'ERC' as any, + callViewMethod: vi.fn().mockResolvedValueOnce({ result: 'erc-swap-id' }), + callSendMethod: vi.fn().mockResolvedValueOnce({ result: 'success' }), + }, + }); + + expect(result).toEqual({ result: 'success' }); + }); + + // Test swap ID error handling + test('should return error when swap ID call fails', async () => { + vi.mocked(mockContract.callViewMethod).mockResolvedValueOnce({ error: 'SWAP_ID_ERROR' }); + + const result = await SwapToken({ + ...baseParams, + bridgeOutContract: { + ...mockContract, + contractType: 'ERC' as any, + callViewMethod: vi.fn().mockResolvedValueOnce({ error: 'SWAP_ID_ERROR' }), + }, + }); + expect(result).toEqual({ error: 'SWAP_ID_ERROR' }); + }); + + // Test successful swap execution + test('should execute swap with correct parameters', async () => { + const result = await SwapToken({ + ...baseParams, + bridgeOutContract: { + ...mockContract, + contractType: 'ERC' as any, + callViewMethod: vi.fn().mockResolvedValueOnce({ result: 'swap-id-123' }), + callSendMethod: vi.fn().mockResolvedValueOnce({ transactionId: 'tx1' }), + }, + }); + + expect(result).toEqual({ transactionId: 'tx1' }); + }); + + // Test missing token info handling + test('should handle missing token address', async () => { + vi.mocked(getTokenInfoByWhitelist).mockReturnValue(undefined); + + await expect(SwapToken(baseParams as any)).rejects.toThrow(); + + expect(mockContract.callSendMethod).not.toHaveBeenCalled(); + }); + + // Test decimal conversion + test('should apply correct decimal conversion', async () => { + const result = await SwapToken({ + ...baseParams, + bridgeOutContract: { + ...mockContract, + contractType: 'ERC' as any, + callViewMethod: vi.fn().mockResolvedValueOnce({ result: 'swap-id-123' }), + callSendMethod: vi.fn().mockResolvedValueOnce({ transactionId: 'tx1' }), + }, + }); + + expect(result).toEqual({ transactionId: 'tx1' }); + expect(timesDecimals).toHaveBeenCalledWith(100, 18); + }); + + // Test chain ID mapping + test('should use mapped chain ID in contract calls', async () => { + const result = await SwapToken({ + ...baseParams, + bridgeOutContract: { + ...mockContract, + contractType: 'ERC' as any, + callViewMethod: vi.fn().mockResolvedValueOnce({ result: 'swap-id-123' }), + callSendMethod: vi.fn().mockResolvedValueOnce({ transactionId: 'tx1' }), + }, + }); + + expect(result).toEqual({ transactionId: 'tx1' }); + expect(getChainIdToMap).toHaveBeenCalledWith(1); + }); +}); + +describe('getSwapId', () => { + let mockContract: ContractBasic; + + beforeEach(() => { + vi.clearAllMocks(); + + mockContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + + (getChainIdToMap as any).mockReturnValue(mainChainId); + }); + + it('should return undefined if any parameter is missing', async () => { + (getTokenInfoByWhitelist as any).mockReturnValue(mockTokenInfo); + + const result = await getSwapId({ + bridgeOutContract: mockContract, + toChainId: dappChainId, + fromChainId: mainChainId, + // symbol is missing + }); + + expect(result).toBeUndefined(); + }); + + it('should call callViewMethod with correct parameters for ELF contractType', async () => { + (getTokenInfoByWhitelist as any).mockReturnValue(mockTokenInfo); + + mockContract.contractType = 'ELF'; + + await getSwapId({ + bridgeOutContract: mockContract, + toChainId: dappChainId, + fromChainId: mainChainId, + symbol: 'ELF', + }); + + expect(mockContract.callViewMethod).toHaveBeenCalledWith('GetSwapIdByToken', [mainChainId, 'ELF']); + }); + + it('should call callViewMethod with correct parameters for non-ELF contractType', async () => { + (getTokenInfoByWhitelist as any).mockReturnValue(mockTokenInfo); + + mockContract.contractType = 'nonELF' as any; + + await getSwapId({ + bridgeOutContract: mockContract, + toChainId: dappChainId, + fromChainId: mainChainId, + symbol: 'ELF', + }); + + expect(mockContract.callViewMethod).toHaveBeenCalledWith('getSwapId', [mockTokenInfo.address, mainChainId]); + }); + + it('should return swapId from callViewMethod', async () => { + (getTokenInfoByWhitelist as any).mockReturnValue(mockTokenInfo); + + const mockSwapId = 'mockSwapId'; + (mockContract.callViewMethod as Mock).mockResolvedValue(mockSwapId); + + const result = await getSwapId({ + bridgeOutContract: mockContract, + toChainId: dappChainId, + fromChainId: mainChainId, + symbol: 'ELF', + }); + + expect(result).toBe(mockSwapId); + }); + + it('should handle errors from callViewMethod', async () => { + (getTokenInfoByWhitelist as any).mockReturnValue(mockTokenInfo); + + const errorMessage = 'Failed to get swapId'; + (mockContract.callViewMethod as Mock).mockRejectedValue(new Error(errorMessage)); + + await expect( + getSwapId({ + bridgeOutContract: mockContract, + toChainId: dappChainId, + fromChainId: mainChainId, + symbol: 'ELF', + }), + ).rejects.toThrow(errorMessage); + }); + + it('should call callViewMethod with correct parameters for non-ELF contractType and not tokenInfo', async () => { + (getTokenInfoByWhitelist as any).mockReturnValue(undefined); + + mockContract.contractType = 'nonELF' as any; + + await getSwapId({ + bridgeOutContract: mockContract, + toChainId: dappChainId, + fromChainId: mainChainId, + symbol: 'ELF', + }); + + expect(mockContract.callViewMethod).toHaveBeenCalledWith('getSwapId', [mockTokenInfo.address, mainChainId]); + }); +}); + +describe('getReceiptLimit', () => { + let mockContract: ContractBasic; + + beforeEach(() => { + vi.clearAllMocks(); + + mockContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + + (getChainIdToMap as any).mockReturnValue(mainChainId); + }); + + // Test parameter boundary cases + test.each([ + { param: 'limitContract', value: undefined }, + { param: 'fromChainId', value: undefined }, + { param: 'toChainId', value: undefined }, + { param: 'fromSymbol', value: undefined }, + ])('should return undefined when $param is missing', async ({ param, value }) => { + const params = { + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + toChainId: SupportedChainId.MAINNET, + fromSymbol: 'ETH', + [param]: value, + }; + + const result = await getReceiptLimit(params); + expect(result).toBeUndefined(); + }); + + // Test successful data retrieval + test('should return formatted limit data when all parameters valid', async () => { + // Mock contract responses + vi.mocked(mockContract.callViewMethod) + .mockResolvedValueOnce({ + error: undefined, + tokenAmount: '1000', + }) + .mockResolvedValueOnce({ + error: undefined, + tokenCapacity: '2000', + currentTokenAmount: '500', + rate: '100', + isEnabled: true, + }); + + const result = await getReceiptLimit({ + limitContract: { ...mockContract, contractType: 'ERC' }, + fromChainId: SupportedELFChainId.AELF, + toChainId: SupportedChainId.MAINNET, + fromSymbol: 'ETH', + }); + + // Verify BigNumber conversions + expect(result).toEqual({ + remain: expect.any(BigNumber), + maxCapcity: expect.any(BigNumber), + currentCapcity: expect.any(BigNumber), + fillRate: expect.any(BigNumber), + isEnable: undefined, + }); + + // Verify numerical values + expect(result?.remain.toString()).toBe('1000'); + expect(result?.maxCapcity.toString()).toBe('2000'); + expect(result?.currentCapcity.toString()).toBe('500'); + expect(result?.fillRate.toString()).toBe('100'); + }); + + // Test error handling in contract calls + test('should throw error when contract returns error', async () => { + vi.mocked(mockContract.callViewMethod) + .mockResolvedValueOnce({ error: 'LIMIT_ERROR' }) + .mockResolvedValueOnce({ error: 'TOKEN_BUCKET_ERROR' }); + + await getReceiptLimit({ + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + toChainId: SupportedChainId.MAINNET, + fromSymbol: 'ETH', + }); + + expect(CommonMessage.error).toHaveBeenCalledWith('LIMIT_ERROR'); + }); + + // Test error handling in contract calls + test('should throw error when contract returns error', async () => { + vi.mocked(mockContract.callViewMethod) + .mockResolvedValueOnce({ error: '' }) + .mockResolvedValueOnce({ error: 'TOKEN_BUCKET_ERROR' }); + + await getReceiptLimit({ + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + toChainId: SupportedChainId.MAINNET, + fromSymbol: 'ETH', + }); + + expect(CommonMessage.error).toHaveBeenCalledWith('TOKEN_BUCKET_ERROR'); + }); + + // Test missing token info scenario + test('should handle missing token address', async () => { + vi.mocked(getTokenInfoByWhitelist).mockReturnValue(undefined); + + const result = await getReceiptLimit({ + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + toChainId: SupportedChainId.MAINNET, + fromSymbol: 'ETH', + }); + + expect(result).toBeUndefined(); + expect(CommonMessage.error).toHaveBeenCalledWith(expect.stringContaining('Cannot read properties of undefined')); + }); + + // Test exception handling + test('should handle unexpected errors', async () => { + const testError = new Error('Network failure'); + vi.mocked(mockContract.callViewMethod).mockRejectedValue(testError); + + const result = await getReceiptLimit({ + limitContract: { ...mockContract, contractType: 'ERC' }, + fromChainId: SupportedChainId.MAINNET, + toChainId: SupportedELFChainId.AELF, + fromSymbol: 'ETH', + }); + + expect(result).toBeUndefined(); + expect(CommonMessage.error).toHaveBeenCalledWith('Network failure'); + }); + + // // Test chain ID mapping + test('should use mapped chain ID in contract call', async () => { + vi.mocked(mockContract.callViewMethod).mockResolvedValue({ + tokenAmount: '1000', + tokenCapacity: '2000', + currentCapcity: '500', + fillRate: '100', + isEnable: true, + }); + + await getReceiptLimit({ + limitContract: { ...mockContract, contractType: 'ERC' }, + fromChainId: SupportedChainId.MAINNET, + toChainId: SupportedELFChainId.tDVV, + fromSymbol: 'ETH', + }); + + expect(getChainIdToMap).toHaveBeenCalledWith(SupportedELFChainId.tDVV); + expect(mockContract.callViewMethod).toHaveBeenCalledWith('getCurrentReceiptTokenBucketState', [undefined, 'AELF']); + }); +}); + +describe('getSwapLimit', () => { + let mockContract: ContractBasic; + + beforeEach(() => { + vi.clearAllMocks(); + + mockContract = new ContractBasic({ contractAddress: 'mockContractAddress' }); + + (getChainIdToMap as any).mockReturnValue(mainChainId); + }); + + // Test parameter boundary cases + test.each([ + { param: 'limitContract', value: undefined }, + { param: 'fromChainId', value: undefined }, + { param: 'swapId', value: undefined }, + { param: 'toChainId', value: undefined }, + { param: 'toSymbol', value: undefined }, + ])('should return undefined when $param is missing', async ({ param, value }) => { + const params = { + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + swapId: 'swap-1', + toChainId: SupportedChainId.MAINNET, + toSymbol: 'ETH', + [param]: value, + }; + + const result = await getSwapLimit(params); + expect(result).toBeUndefined(); + }); + + // Test successful data retrieval + test('should return formatted limit data when all parameters valid', async () => { + // Mock contract responses + vi.mocked(mockContract.callViewMethod) + .mockResolvedValueOnce({ + error: undefined, + tokenAmount: '1000', + }) + .mockResolvedValueOnce({ + error: undefined, + tokenCapacity: '2000', + currentTokenAmount: '500', + rate: '100', + isEnabled: true, + }); + + const result = await getSwapLimit({ + limitContract: { ...mockContract, contractType: 'ERC' }, + fromChainId: SupportedELFChainId.AELF, + swapId: 'swap-1', + toChainId: SupportedChainId.MAINNET, + toSymbol: 'ETH', + }); + + // Verify BigNumber conversions + expect(result).toEqual({ + remain: expect.any(BigNumber), + maxCapcity: expect.any(BigNumber), + currentCapcity: expect.any(BigNumber), + fillRate: expect.any(BigNumber), + isEnable: true, + }); + + // Verify numerical values + expect(result?.remain.toString()).toBe('1000'); + expect(result?.maxCapcity.toString()).toBe('2000'); + expect(result?.currentCapcity.toString()).toBe('500'); + expect(result?.fillRate.toString()).toBe('100'); + }); + + // Test error handling in contract calls + test('should throw error when contract returns error', async () => { + vi.mocked(mockContract.callViewMethod) + .mockResolvedValueOnce({ error: 'LIMIT_ERROR' }) + .mockResolvedValueOnce({ error: 'TOKEN_BUCKET_ERROR' }); + + await getSwapLimit({ + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + swapId: 'swap-1', + toChainId: SupportedChainId.MAINNET, + toSymbol: 'ETH', + }); + + expect(CommonMessage.error).toHaveBeenCalledWith('LIMIT_ERROR'); + }); + + // Test error handling in contract calls + test('should throw error when contract returns error', async () => { + vi.mocked(mockContract.callViewMethod) + .mockResolvedValueOnce({ error: '' }) + .mockResolvedValueOnce({ error: 'TOKEN_BUCKET_ERROR' }); + + await getSwapLimit({ + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + swapId: 'swap-1', + toChainId: SupportedChainId.MAINNET, + toSymbol: 'ETH', + }); + + expect(CommonMessage.error).toHaveBeenCalledWith('TOKEN_BUCKET_ERROR'); + }); + + // Test missing token info scenario + test('should handle missing token address', async () => { + vi.mocked(getTokenInfoByWhitelist).mockReturnValue(undefined); + + const result = await getSwapLimit({ + limitContract: mockContract, + fromChainId: SupportedELFChainId.AELF, + swapId: 'swap-1', + toChainId: SupportedChainId.MAINNET, + toSymbol: 'ETH', + }); + + expect(result).toBeUndefined(); + expect(CommonMessage.error).toHaveBeenCalledWith(expect.stringContaining('Cannot read properties of undefined')); + }); + + // Test exception handling + test('should handle unexpected errors', async () => { + const testError = new Error('Network failure'); + vi.mocked(mockContract.callViewMethod).mockRejectedValue(testError); + + const result = await getSwapLimit({ + limitContract: { ...mockContract, contractType: 'ERC' }, + fromChainId: SupportedChainId.MAINNET, + swapId: 'swap-1', + toChainId: SupportedELFChainId.AELF, + toSymbol: 'ETH', + }); + + expect(result).toBeUndefined(); + expect(CommonMessage.error).toHaveBeenCalledWith('Network failure'); + }); + + // // Test chain ID mapping + test('should use mapped chain ID in contract call', async () => { + vi.mocked(mockContract.callViewMethod).mockResolvedValue({ + tokenAmount: '1000', + tokenCapacity: '2000', + currentCapcity: '500', + fillRate: '100', + isEnable: true, + }); + + await getSwapLimit({ + limitContract: { ...mockContract, contractType: 'ERC' }, + fromChainId: SupportedChainId.MAINNET, + swapId: 'swap-1', + toChainId: SupportedELFChainId.tDVV, + toSymbol: 'ETH', + }); + + expect(getChainIdToMap).toHaveBeenCalledWith(1); + expect(mockContract.callViewMethod).toHaveBeenCalledWith('getCurrentSwapTokenBucketState', [undefined, 'AELF']); + }); +}); diff --git a/src/utils/__tests__/eBridgeEventBus.test.ts b/src/utils/__tests__/eBridgeEventBus.test.ts new file mode 100644 index 00000000..40680d6a --- /dev/null +++ b/src/utils/__tests__/eBridgeEventBus.test.ts @@ -0,0 +1,154 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import eBridgeEventBus, { eventBus } from '../eBridgeEventBus'; + +describe('Event System', () => { + let callbackMock: ReturnType; + + // Before each test, reset mocks and initialize the callback + beforeEach(() => { + callbackMock = vi.fn(); + + // Ensure no listeners from previous tests + eventBus.removeAllListeners(); + }); + + afterEach(() => { + // Clean up listeners after every test + eventBus.removeAllListeners(); + }); + + /** + * Test eventBus functionality separately + */ + describe('eventBus', () => { + it('should emit and listen for a custom event', () => { + const testEvent = 'TEST_EVENT'; + + // Register a listener + eventBus.on(testEvent, callbackMock); + + // Emit the event + eventBus.emit(testEvent, 'payload'); + + // Assert callback was called with the correct payload + expect(callbackMock).toHaveBeenCalledOnce(); + expect(callbackMock).toHaveBeenCalledWith('payload'); + }); + + it('should allow removing a listener', () => { + const testEvent = 'TEST_EVENT'; + + // Register a listener + const listener = (data: any) => { + callbackMock(data); + }; + eventBus.on(testEvent, listener); + + // Remove the listener + eventBus.removeListener(testEvent, listener); + + // Emit the event (should no longer be handled) + eventBus.emit(testEvent, 'payload'); + + // Assert callback was not called + expect(callbackMock).not.toHaveBeenCalled(); + }); + }); + + /** + * Test eventsServer generated events + */ + describe('eventsServer-generated events', () => { + it('should emit and handle `Unauthorized` event via eBridgeEventBus', () => { + // Register a listener + const listener = eBridgeEventBus.Unauthorized.addListener(callbackMock); + + // Emit the event + eBridgeEventBus.Unauthorized.emit('unauthorized_payload'); + + // Assert that the listener was called + expect(callbackMock).toHaveBeenCalledOnce(); + expect(callbackMock).toHaveBeenCalledWith('unauthorized_payload'); + + // Remove the listener + listener.remove(); + + // Emit the event again (should have no effect) + eBridgeEventBus.Unauthorized.emit('second_payload'); + expect(callbackMock).toHaveBeenCalledTimes(1); + }); + + it('should emit and handle multiple events', () => { + // Register listeners for multiple events + const authSuccessListener = vi.fn(); + const AelfLogoutSuccessListener = vi.fn(); + const AelfLoginSuccessListener = vi.fn(); + + const authListener = eBridgeEventBus.AelfAuthTokenSuccess.addListener(authSuccessListener); + const logoutListener = eBridgeEventBus.AelfLogoutSuccess.addListener(AelfLogoutSuccessListener); + const loginListener = eBridgeEventBus.AelfLoginSuccess.addListener(AelfLoginSuccessListener); + + // Emit both events + eBridgeEventBus.AelfAuthTokenSuccess.emit('auth_token_payload'); + eBridgeEventBus.AelfLogoutSuccess.emit('logout_payload'); + eBridgeEventBus.AelfLoginSuccess.emit('login_payload'); + + // Assert both listeners were called with respective payloads + expect(authSuccessListener).toHaveBeenCalledOnce(); + expect(authSuccessListener).toHaveBeenCalledWith('auth_token_payload'); + + expect(AelfLogoutSuccessListener).toHaveBeenCalledOnce(); + expect(AelfLogoutSuccessListener).toHaveBeenCalledWith('logout_payload'); + + expect(AelfLoginSuccessListener).toHaveBeenCalledOnce(); + expect(AelfLoginSuccessListener).toHaveBeenCalledWith('login_payload'); + + // Remove both listeners + authListener.remove(); + logoutListener.remove(); + loginListener.remove(); + + // Emit events again (should not trigger listeners) + eBridgeEventBus.AelfAuthTokenSuccess.emit('second_payload'); + eBridgeEventBus.AelfLogoutSuccess.emit('second_payload'); + eBridgeEventBus.AelfLoginSuccess.emit('second_payload'); + expect(authSuccessListener).toHaveBeenCalledTimes(1); + expect(AelfLogoutSuccessListener).toHaveBeenCalledTimes(1); + expect(AelfLoginSuccessListener).toHaveBeenCalledTimes(1); + }); + + it('should store event names in uppercase', () => { + expect(eBridgeEventBus.Unauthorized.name).toBe('UNAUTHORIZED'); + expect(eBridgeEventBus.AelfAuthTokenSuccess.name).toBe('AELFAUTHTOKENSUCCESS'); + expect(eBridgeEventBus.AelfLogoutSuccess.name).toBe('AELFLOGOUTSUCCESS'); + expect(eBridgeEventBus.AelfLoginSuccess.name).toBe('AELFLOGINSUCCESS'); + expect(eBridgeEventBus.GlobalLoading.name).toBe('GLOBALLOADING'); + }); + }); + + /** + * Edge case tests + */ + describe('Edge cases', () => { + it('should not crash when emitting an event with no listeners', () => { + expect(() => eBridgeEventBus.Unauthorized.emit('no_listeners_payload')).not.toThrow(); + }); + + it('should behave correctly when adding multiple listeners to the same event', () => { + const listener1 = vi.fn(); + const listener2 = vi.fn(); + + eBridgeEventBus.Unauthorized.addListener(listener1); + eBridgeEventBus.Unauthorized.addListener(listener2); + + // Emit the event + eBridgeEventBus.Unauthorized.emit('payload'); + + // Assert both listeners were called + expect(listener1).toHaveBeenCalledOnce(); + expect(listener1).toHaveBeenCalledWith('payload'); + expect(listener2).toHaveBeenCalledOnce(); + expect(listener2).toHaveBeenCalledWith('payload'); + }); + }); +}); diff --git a/src/utils/__tests__/eBridgeInstance.test.ts b/src/utils/__tests__/eBridgeInstance.test.ts new file mode 100644 index 00000000..37a2b23b --- /dev/null +++ b/src/utils/__tests__/eBridgeInstance.test.ts @@ -0,0 +1,41 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { EBridgeInstance } from '../eBridgeInstance'; + +describe('EBridgeInstance', () => { + let instance: EBridgeInstance; + + beforeEach(() => { + // Recreate a fresh instance for each test + instance = new EBridgeInstance(); + }); + + /** + * Test Initialization + */ + it('should initialize with default values', () => { + expect(instance.unauthorized).toBe(false); + expect(instance.obtainingSignature).toBe(false); + }); + + /** + * Test setUnauthorized + */ + it('should set unauthorized correctly', () => { + instance.setUnauthorized(true); + expect(instance.unauthorized).toBe(true); + + instance.setUnauthorized(false); + expect(instance.unauthorized).toBe(false); + }); + + /** + * Test setObtainingSignature + */ + it('should set obtainingSignature correctly', () => { + instance.setObtainingSignature(true); + expect(instance.obtainingSignature).toBe(true); + + instance.setObtainingSignature(false); + expect(instance.obtainingSignature).toBe(false); + }); +}); diff --git a/src/utils/__tests__/error.test.ts b/src/utils/__tests__/error.test.ts new file mode 100644 index 00000000..ebcbb9c7 --- /dev/null +++ b/src/utils/__tests__/error.test.ts @@ -0,0 +1,135 @@ +import { describe, it, expect } from 'vitest'; +import { + handleError, + handleContractError, + handleErrorMessage, + handleWebLoginErrorMessage, + handleListingErrorMessage, +} from '../error'; + +describe('handleError', () => { + it('should return error.error if exists', () => { + const error = { error: 'Custom error' }; + expect(handleError(error)).toBe('Custom error'); + }); + + it('should return the error itself if error.error does not exist', () => { + const error = 'Generic error'; + expect(handleError(error)).toBe('Generic error'); + }); +}); + +describe('handleContractError', () => { + it('should return message if error is string', () => { + expect(handleContractError('Error message')).toEqual({ message: 'Error message' }); + }); + + it('should return error if error.message exists', () => { + const error = { message: 'Error message' }; + expect(handleContractError(error)).toEqual(error); + }); + + it('should return error if error.shortMessage exists', () => { + const error = { shortMessage: 'EVM contract error message' }; + const result = handleContractError(error); + expect(result.message).toEqual(error.shortMessage); + }); + + it('should return error if error.details exists', () => { + const error = { details: 'User rejected request' }; + const result = handleContractError(error); + expect(result.message).toEqual(error.details); + }); + + it('should return detailed message and code if error.Error exists', () => { + const error = { Error: { Details: 'Detailed error', Message: 'Error message', Code: 500 } }; + expect(handleContractError(error)).toEqual({ + message: 'Detailed error', + code: 500, + }); + }); + + it('should return detailed message and code if error.Error is string', () => { + const error = { Error: 'Error message' }; + expect(handleContractError(error)).toEqual({ + code: undefined, + message: 'Error message', + }); + }); + + it('should return code and message from req if no other conditions match', () => { + const req = { error: { message: { Code: 400 } }, errorMessage: { message: 'Request error' } }; + expect(handleContractError({}, req)).toEqual({ + code: 400, + message: 'Request error', + }); + }); + + it('should return code and message from req if no other conditions match and error is 400', () => { + const req = { error: 400, errorMessage: { message: 'Request error' } }; + expect(handleContractError({}, req)).toEqual({ + code: 400, + message: 'Request error', + }); + }); + + it('should return code and message from req if no other conditions match and have error.message.Message', () => { + const req = { error: { message: { Code: 400, Message: 'Request error' } } }; + expect(handleContractError({}, req)).toEqual({ + code: 400, + message: 'Request error', + }); + }); +}); + +describe('handleErrorMessage', () => { + it('should return errorText if error status is 500 and passing errorText in parameters', () => { + const error = { status: 500 }; + expect(handleErrorMessage(error, 'Failed to request')).toBe('Failed to request'); + }); + + it('should return errorText if error status is 500', () => { + const error = { status: 500 }; + expect(handleErrorMessage(error)).toBe('Failed to fetch data'); + }); + + it('should return error message from handleContractError', () => { + const error = { Error: { Message: 'Contract error' } }; + expect(handleErrorMessage(error, 'Default error')).toBe('Contract error'); + }); + + it('should return empty string if no error message is found', () => { + expect(handleErrorMessage({}, 'Default error')).toBe('Default error'); + }); + + it('should return empty string if no error message is found and errorText is empty', () => { + expect(handleErrorMessage({})).toBe(''); + }); +}); + +describe('handleWebLoginErrorMessage', () => { + it('should handle nativeError and return error message', () => { + const error = { nativeError: { Error: { Message: 'Login error' } } }; + expect(handleWebLoginErrorMessage(error, 'Default error')).toBe('Login error'); + }); + + it('should return default error if no nativeError exists', () => { + const error = { message: 'Web login error' }; + expect(handleWebLoginErrorMessage(error, 'Default error')).toBe('Web login error'); + }); +}); + +describe('handleListingErrorMessage', () => { + it('should return error message from response data', () => { + const error = { response: { data: { error: { message: 'Listing error' } } } }; + expect(handleListingErrorMessage(error, 'Default error')).toBe('Listing error'); + }); + + it('should return default error if no response data exists', () => { + expect(handleListingErrorMessage({}, 'Default error')).toBe('Default error'); + }); + + it('should return default error if no response data exists and errorText is empty', () => { + expect(handleListingErrorMessage({})).toBe(''); + }); +}); diff --git a/src/utils/__tests__/events.test.ts b/src/utils/__tests__/events.test.ts new file mode 100644 index 00000000..49952d4a --- /dev/null +++ b/src/utils/__tests__/events.test.ts @@ -0,0 +1,49 @@ +import { describe, it, expect, vi } from 'vitest'; +import { emitLoading } from '../events'; +import eBridgeEventBus from '../eBridgeEventBus'; + +// Mock Event Bus +vi.mock('../eBridgeEventBus', () => ({ + default: { + GlobalLoading: { + emit: vi.fn(), + }, + }, +})); + +// Mock Event Bus +vi.mock('../eBridgeEventBus', () => ({ + default: { + GlobalLoading: { + emit: vi.fn(), + }, + }, +})); + +// Mock Event Bus +vi.mock('../eBridgeEventBus', () => { + return { + default: { + GlobalLoading: { + emit: vi.fn(), + }, + }, + }; +}); + +describe('emitLoading', () => { + it('should emit the global loading event with `loading` true', () => { + emitLoading(true); + + // Assert that `eBridgeEventBus.GlobalLoading.emit` was called with the correct arguments + expect(eBridgeEventBus.GlobalLoading.emit).toHaveBeenCalledOnce(); + expect(eBridgeEventBus.GlobalLoading.emit).toHaveBeenCalledWith(true); + }); + + it('should emit the global loading event with `loading` false', () => { + emitLoading(false); + + // Assert that `eBridgeEventBus.GlobalLoading.emit` was called with correct arguments + expect(eBridgeEventBus.GlobalLoading.emit).toHaveBeenCalledWith(false); + }); +}); diff --git a/src/utils/__tests__/format.test.ts b/src/utils/__tests__/format.test.ts new file mode 100644 index 00000000..8493ea73 --- /dev/null +++ b/src/utils/__tests__/format.test.ts @@ -0,0 +1,175 @@ +import { describe, it, expect, Mock } from 'vitest'; +import { + AmountSign, + formatListWithAnd, + formatSymbolDisplay, + formatWithCommas, + parseWithCommas, + parseWithStringCommas, + replaceCharacter, +} from '../format'; +import { divDecimals } from 'utils/calculate'; + +// Mock the `divDecimals` function (since it's external) +vi.mock('../calculate', () => ({ + divDecimals: vi.fn(), +})); + +/** + * Test `parseWithCommas` + */ +describe('parseWithCommas', () => { + it('should parse a comma-separated value into plain numbers', () => { + const result = parseWithCommas('12,345,678.90'); + + expect(result).toBe('12345678.9'); + }); + + it('should return an empty string when input is null or undefined', () => { + expect(parseWithCommas(null)).toBe(''); + + expect(parseWithCommas(undefined)).toBe(''); + }); +}); + +/** + * Test `parseWithStringCommas` + */ +describe('parseWithStringCommas', () => { + it('should remove commas from a string', () => { + const result = parseWithStringCommas('12,345,678.90'); + + expect(result).toBe('12345678.90'); + }); + + it('should return an empty string for empty input', () => { + expect(parseWithStringCommas(null)).toBe(''); + + expect(parseWithStringCommas('')).toBe(''); + }); +}); + +/** + * Test `replaceCharacter` + */ +describe('replaceCharacter', () => { + it('should replace matched characters', () => { + const result = replaceCharacter('abc-def', '-', '+'); + + expect(result).toBe('abc+def'); + }); + + it('should handle strings without matching characters', () => { + const result = replaceCharacter('abcdef', '-', '+'); + + expect(result).toBe('abcdef'); + }); +}); + +/** + * Test `formatSymbolDisplay` + */ +describe('formatSymbolDisplay', () => { + it('should replace "-1" for "SGR-1"', () => { + const result = formatSymbolDisplay('SGR-1'); + + expect(result).toBe('SGR'); + }); + + it('should return the input string if it does not contain "-1"', () => { + const result = formatSymbolDisplay('ABC'); + + expect(result).toBe('ABC'); + }); + + it('should return an empty string for falsy input', () => { + expect(formatSymbolDisplay('')).toBe(''); + }); +}); + +/** + * Test `formatListWithAnd` + */ +describe('formatListWithAnd', () => { + it('should format a list with commas and "and"', () => { + const result = formatListWithAnd(['Alice', 'Bob', 'Charlie']); + + expect(result).toBe('Alice, Bob and Charlie'); + }); + + it('should format a list with one item correctly', () => { + const result = formatListWithAnd(['Alice']); + + expect(result).toBe('Alice'); + }); + + it('should return an empty string for an empty list', () => { + expect(formatListWithAnd([])).toBe(''); + }); +}); + +/** + * Test `formatWithCommas` + */ +describe('formatWithCommas', () => { + it('should format the amount with commas and decimal places', () => { + (divDecimals as Mock).mockImplementation(() => { + return { + decimalPlaces: () => ({ + toFormat: vi.fn().mockReturnValue('123,456.78'), + }), + }; + }); + + const result = formatWithCommas({ + amount: '12345678.1234', + decimals: 2, + digits: 2, + sign: AmountSign.PLUS, + }); + + expect(result).toBe('+123,456.78.1234'); + }); + + it('should handle zero amounts with the EMPTY sign', () => { + (divDecimals as Mock).mockImplementation(() => { + return { + decimalPlaces: () => ({ + toFormat: vi.fn().mockReturnValue('0'), + }), + }; + }); + + const result = formatWithCommas({ amount: 0, sign: AmountSign.EMPTY }); + + expect(result).toBe('0'); + }); + + it('should properly format negative amounts', () => { + (divDecimals as Mock).mockImplementation(() => { + return { + decimalPlaces: () => ({ + toFormat: vi.fn().mockReturnValue('-12,345'), + }), + }; + }); + + const result = formatWithCommas({ amount: '-12345.6', digits: 1, sign: AmountSign.MINUS }); + + expect(result).toBe('--12,345.6'); + }); + + it('should handle non-decimal amounts', () => { + (divDecimals as Mock).mockImplementation(() => { + return { + decimalPlaces: () => ({ + toFormat: vi.fn().mockReturnValue('123,456'), + }), + }; + }); + + const result = formatWithCommas({ amount: 123456, decimals: 0 }); + + expect(result).toBe('123,456'); + }); +}); diff --git a/src/utils/__tests__/index.test.ts b/src/utils/__tests__/index.test.ts new file mode 100644 index 00000000..7e5a09f4 --- /dev/null +++ b/src/utils/__tests__/index.test.ts @@ -0,0 +1,454 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { SupportedChainId, SupportedELFChainId, SupportedTONChainId } from 'constants/chain'; +import AElf from 'aelf-sdk'; +import { getAddress } from '@ethersproject/address'; +import { + sleep, + getExploreLink, + shortenAddress, + shortenString, + unityTokenInfo, + getTokenLogoURL, + enumToMap, + isERCAddress, + isELFAddress, + formatAddress, + formatNativeToken, + isIncludesChainId, + isTonChain, + isAddress, + isChainAddress, +} from 'utils/index'; +import { isELFChain } from '../aelfUtils'; +import { isTonAddress } from 'utils/ton'; + +// Mock external dependencies +vi.mock('@ethersproject/address', () => ({ + getAddress: vi.fn((address: string) => address), +})); + +vi.mock('aelf-sdk', () => { + return { + default: { + utils: { + decodeAddressRep: vi.fn((address: string) => address), + }, + }, + }; +}); + +vi.mock('../ton', () => ({ + isTonAddress: vi.fn((address: string) => address.startsWith('TON_')), +})); + +vi.mock('../aelfUtils', () => { + return { + isELFChain: vi.fn(), + }; +}); + +// Mock ELFChainConstants and ERCChainConstants +vi.mock('constants/ChainConstants', () => { + const mockExploreUrl = 'https://testnet.aelfscan.io/AELF/'; + const mockBaseScanUrl = 'https://basescan.org/'; + + return { + ELFChainConstants: { + constants: { + [SupportedELFChainId.AELF]: { + CHAIN_INFO: { + exploreUrl: mockExploreUrl, + }, + }, + }, + }, + ERCChainConstants: { + constants: { + CHAIN_INFO: { + exploreUrl: mockBaseScanUrl, + }, + }, + }, + }; +}); + +// Mock SupportedExternalChain +vi.mock('constants/index', async (importOriginal) => { + const actual: any = await importOriginal(); + const mockEtherscanUrl = 'https://etherscan.io/'; + const mockTestnetTonViewerUrl = 'https://testnet.tonviewer.com/'; + + return { + ...actual, + SupportedExternalChain: { + [SupportedChainId.MAINNET]: { + CHAIN_INFO: { + exploreUrl: mockEtherscanUrl, + }, + }, + [SupportedTONChainId.TESTNET]: { + CHAIN_INFO: { + exploreUrl: mockTestnetTonViewerUrl, + }, + }, + }, + }; +}); + +// Mock constants +const mockChainId = SupportedELFChainId.AELF; +const mockAELFExplorerUrl = 'https://testnet.aelfscan.io/AELF/'; +const mockEtherscanUrl = 'https://etherscan.io/'; +const mockBaseScanUrl = 'https://basescan.org/'; +const mockTestnetTonViewerUrl = 'https://testnet.tonviewer.com/'; +const mockTokenInfo = { + decimals: 18, + symbol: 'ELF', + tokenName: 'AELF Token', + address: 'ELF_address', + issueChainId: 1, + issuer: 'AELF', + isBurnable: true, + totalSupply: 1000000000, +}; +const correctAelfAddress = 'ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_AELF'; +const correctTDVVAddress = 'ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_tDVV'; +const correctTDVWAddress = 'ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_tDVW'; +const assetsUrl = 'https://raw.githubusercontent.com/eBridgeCrosschain/assets/master/blockchains/AELF/assets'; + +describe('Index Utils Functions', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + describe('sleep', () => { + it('should resolve after the specified time', async () => { + const time = 1000; + const result = await sleep(time); + expect(result).toBe('sleep'); + }); + }); + + describe('getExploreLink', () => { + it('should return transaction link for ELF chain', () => { + // Mock the isELFChain function return true + vi.mocked(isELFChain).mockReturnValueOnce(true); + + const link = getExploreLink('tx_hash', 'transaction', mockChainId); + expect(link).toBe(`${mockAELFExplorerUrl}tx/tx_hash`); + }); + + it('should return token link for ERC chain if type is token', () => { + // Mock the isELFChain function return false + vi.mocked(isELFChain).mockReturnValueOnce(false); + + const link = getExploreLink('erc_token_address', 'token', SupportedChainId.MAINNET); + expect(link).toBe(`${mockEtherscanUrl}token/erc_token_address`); + }); + + it('should return token link for ERC chain if type is address', () => { + // Mock the isELFChain function return false + vi.mocked(isELFChain).mockReturnValueOnce(false); + + const link = getExploreLink('erc_token_address', 'address', SupportedChainId.MAINNET); + expect(link).toBe(`${mockEtherscanUrl}address/erc_token_address`); + }); + + it('should return token link for BASE chain', () => { + // Mock the isELFChain function return false + vi.mocked(isELFChain).mockReturnValueOnce(false); + + const link = getExploreLink('base_token_address', 'token', SupportedChainId.BASE); + expect(link).toBe(`${mockBaseScanUrl}token/base_token_address`); + }); + + it('should return address link for TON chain if type is address', () => { + // Mock the isELFChain function return false + vi.mocked(isELFChain).mockReturnValueOnce(false); + + const link = getExploreLink('TON_address', 'address', SupportedTONChainId.TESTNET); + expect(link).toBe(`${mockTestnetTonViewerUrl}TON_address`); + }); + + it('should return address link for TON chain if type is transaction', () => { + // Mock the isELFChain function return false + vi.mocked(isELFChain).mockReturnValueOnce(false); + + const link = getExploreLink('txid', 'transaction', SupportedTONChainId.TESTNET); + expect(link).toBe(`${mockTestnetTonViewerUrl}transaction/txid`); + }); + + it('should return block link for default chain', () => { + // Mock the isELFChain function return false + vi.mocked(isELFChain).mockReturnValueOnce(false); + + const link = getExploreLink('block_hash', 'block', SupportedChainId.MAINNET); + expect(link).toBe(`${mockEtherscanUrl}block/block_hash`); + }); + }); + + describe('shortenAddress', () => { + it('should shorten address correctly', () => { + const address = '0x1234567890123456789012345678901234567890'; + const shortened = shortenAddress(address, 6); + expect(shortened).toBe('0x123456...567890'); + }); + + it('should throw error for invalid address', () => { + expect(() => shortenAddress(null, 4)).toThrow("Invalid 'address' parameter 'null'."); + }); + }); + + describe('shortenString', () => { + it('should shorten string correctly', () => { + const str = 'This is a long string'; + const shortened = shortenString(str, 5); + expect(shortened).toBe('This ...tring'); + }); + + it('should return empty string for invalid input', () => { + expect(shortenString(null, 5)).toBe(''); + }); + }); + + describe('unityTokenInfo', () => { + it('should return unified token info', () => { + const result = unityTokenInfo(mockTokenInfo); + expect(result).toEqual(mockTokenInfo); + }); + + it('should return undefined for invalid input', () => { + const result = unityTokenInfo(undefined); + expect(result).toBeUndefined(); + }); + }); + + describe('getTokenLogoURL', () => { + it('should return token logo URL for aelf MainChain address', () => { + const url = getTokenLogoURL(correctAelfAddress, mockChainId); + expect(url).toBe(`${assetsUrl}/${correctAelfAddress}/logo.png`); + }); + + it('should return token logo URL for testnet aelf dappChain address', () => { + const url = getTokenLogoURL(correctTDVWAddress, 'tDVV' as any); + expect(url).toBe(`${assetsUrl}/${correctTDVWAddress}/logo.png`); + }); + + it('should return token logo URL for aelf dappChain address', () => { + const url = getTokenLogoURL(correctTDVVAddress, 'tDVV' as any); + expect(url).toBe(`${assetsUrl}/${correctTDVVAddress}/logo.png`); + }); + + it('should return token logo URL for KOVAN address', () => { + const address = 'mock_kovan_address'; + const url = getTokenLogoURL(address, SupportedChainId.KOVAN); + expect(url).toBe(`${assetsUrl}/${address}/logo.png`); + }); + + it('should return token logo URL for SEPOLIA address', () => { + const address = 'mock_sepolia_address'; + const url = getTokenLogoURL(address, SupportedChainId.SEPOLIA); + expect(url).toBe(`${assetsUrl}/${address}/logo.png`); + }); + + it('should return token logo URL for BSC_TESTNET address', () => { + const address = 'mock_bsc_testnet_address'; + const url = getTokenLogoURL(address, SupportedChainId.BSC_TESTNET); + expect(url).toBe(`${assetsUrl}/${address}/logo.png`); + }); + + it('should return token logo URL for default chain address', () => { + const address = 'mock_default_address'; + const url = getTokenLogoURL(address); + + expect(url).toBe(`${assetsUrl}/${address}/logo.png`); + }); + + it('should return empty string for invalid address', () => { + const result = getTokenLogoURL(undefined, mockChainId); + expect(result).toBe(''); + }); + }); + + describe('enumToMap', () => { + it('should convert enum to map', () => { + const enumObj = { A: 1, B: 2 }; + const map = enumToMap(enumObj); + + expect(map).toEqual({ A: 1, B: 2, 1: 'A', 2: 'B' }); + }); + }); + + describe('isERCAddress', () => { + it('should return true for valid ERC address', () => { + const result = isERCAddress('0x123'); + expect(result).toBe(true); + }); + + it('should return false for invalid ERC address', () => { + // Mock getAddress throw error + vi.mocked(getAddress).mockImplementationOnce(() => { + throw new Error('Invalid address'); + }); + + const result = isERCAddress('invalid'); + expect(result).toBe(false); + }); + }); + + describe('isELFAddress', () => { + it('should return true for valid ELF address', () => { + const result = isELFAddress(correctAelfAddress); + expect(result).toBe(true); + }); + + it('should return false for invalid ELF address only with prefix', () => { + const result = isELFAddress('ELF_Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft'); + expect(result).toBe(true); + }); + + it('should return false for invalid ELF address only with suffix', () => { + const result = isELFAddress('Py2TJpjTtt29zAtqLWyLEU1DEzBFPz1LJU594hy6evPF8Cvft_AELF'); + expect(result).toBe(true); + }); + + it('should return false for invalid ELF address', () => { + // Mock AElf.utils.decodeAddressRep throw error + vi.mocked(AElf.utils.decodeAddressRep).mockImplementationOnce(() => { + throw new Error('Invalid address'); + }); + const result = isELFAddress('invalidELFAddress'); + expect(result).toBe(false); + }); + + it('should return false for addresses containing Chinese characters', () => { + const result = isELFAddress('invalid漢'); + expect(result).toBe(false); + }); + }); + + describe('isAddress', () => { + it('should return true for valid ELF address', () => { + const result = isAddress(correctAelfAddress, mockChainId); + expect(result).toBe(true); + }); + + it('should return false for valid ERC address', () => { + const result = isAddress('0x123', SupportedChainId.MAINNET); + expect(result).toBe(true); + }); + + it('should return false for invalid address', () => { + const result = isAddress('invalid', mockChainId); + expect(result).toBe(true); + }); + }); + + describe('isChainAddress', () => { + it('should return true for valid ELF address', () => { + // Mock the isELFChain function return true + vi.mocked(isELFChain).mockReturnValueOnce(true); + + const result = isChainAddress(correctAelfAddress, mockChainId); + expect(result).toBe(true); + }); + + it('should return false for valid ERC address', () => { + // Mock the isELFChain function return false + vi.mocked(isELFChain).mockReturnValueOnce(false); + + const result = isChainAddress('0x123', SupportedChainId.MAINNET); + expect(result).toBe(true); + }); + + it('should return false for valid TON address', () => { + // Mock isTonAddress function return true + vi.mocked(isTonAddress).mockReturnValueOnce(true); + + const result = isChainAddress('ton_address', SupportedTONChainId.TESTNET); + expect(result).toBe(true); + }); + + it('should return false for not match aelf chain address', () => { + // Mock the isELFChain function return true + vi.mocked(isELFChain).mockReturnValueOnce(true); + + const result = isChainAddress(correctTDVWAddress, mockChainId); + expect(result).toBe(false); + }); + + it('should return false for invalid address', () => { + const result = isChainAddress('invalid', mockChainId); + expect(result).toBe(true); + }); + + it('should return false if not params', () => { + const result = isChainAddress(); + expect(result).toBe(false); + }); + }); + + describe('formatAddress', () => { + it('should format address correctly', () => { + const address = 'ELF_123_AELF'; + + const result = formatAddress(address); + expect(result).toBe('123'); + }); + }); + + describe('formatNativeToken', () => { + it('should format native token symbol', () => { + expect(formatNativeToken('ELF')).toBe('ELF'); + expect(formatNativeToken('SGR-1')).toBe('SGR'); + expect(formatNativeToken('USDT')).toBe('USDT'); + expect(formatNativeToken('USDT-1')).toBe('USDT-1'); + expect(formatNativeToken('WETH')).toBe('ETH'); + }); + + it('should return undefined for invalid input', () => { + const result = formatNativeToken(undefined); + expect(result).toBeUndefined(); + }); + }); + + describe('isIncludesChainId', () => { + it('should return true if chainId is included in list', () => { + const result = isIncludesChainId([mockChainId], mockChainId); + expect(result).toBe(true); + }); + + it('should return false if chainId is undefined', () => { + const result = isIncludesChainId([mockChainId]); + expect(result).toBe(false); + }); + + it('should return false if chainId is not included in list', () => { + const result = isIncludesChainId([SupportedChainId.MAINNET], mockChainId); + expect(result).toBe(false); + }); + + it('should return false if chainId !== list', () => { + const result = isIncludesChainId(SupportedChainId.MAINNET, mockChainId); + expect(result).toBe(false); + }); + + it('should return true if chainId === list', () => { + const result = isIncludesChainId(SupportedChainId.MAINNET, SupportedChainId.MAINNET); + expect(result).toBe(true); + }); + }); + + describe('isTonChain', () => { + it('should return true for TON chainId', () => { + const result = isTonChain(SupportedTONChainId.TESTNET); + expect(result).toBe(true); + }); + + it('should return false for non-TON chainId', () => { + const result = isTonChain(mockChainId); + expect(result).toBe(false); + }); + }); +}); diff --git a/src/utils/__tests__/input.test.ts b/src/utils/__tests__/input.test.ts new file mode 100644 index 00000000..31e89779 --- /dev/null +++ b/src/utils/__tests__/input.test.ts @@ -0,0 +1,246 @@ +import { describe, it, expect, vi, beforeEach, Mock } from 'vitest'; +import BigNumber from 'bignumber.js'; +import { SupportedChainId, SupportedELFChainId } from 'constants/chain'; +import { parseInputChange, parseMAXInputChange, sliceDecimals, getMaxAmount, handleInputFocus } from 'utils/input'; +import { isAndroid } from '../isMobile'; +import { TelegramPlatform } from '../telegram/telegram'; +import { isELFChain } from '../aelfUtils'; + +vi.mock('../isMobile', () => ({ + isAndroid: vi.fn(() => true), +})); + +vi.mock('../aelfUtils', () => { + return { + isELFChain: vi.fn(), + }; +}); + +vi.mock('../telegram/telegram', () => ({ + TelegramPlatform: { + isTelegramPlatform: vi.fn(() => false), + }, +})); + +vi.mock('../index', () => { + return { + sleep: vi.fn(), + }; +}); + +// Mock constants +const mockChainId = SupportedELFChainId.AELF; + +// Attach the mock to the global document object +vi.mock('react-dom'); + +describe('Input Utils Functions', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + describe('parseInputChange', () => { + it('should parse input change correctly if input is greater than min', () => { + const result = parseInputChange('123.456789', new BigNumber(100), 2); + + expect(result).toBe('123.45'); + }); + + it('should parse input change correctly if input is not min', () => { + const result = parseInputChange('123'); + + expect(result).toBe('123'); + }); + + it('should parse input change correctly if input is not maxLength', () => { + const result = parseInputChange('123.456789', new BigNumber(100)); + + expect(result).toBe('123.456789'); + }); + + it('should parse input change correctly if input is negative', () => { + const result = parseInputChange('-123.456789', new BigNumber(100)); + + expect(result).toBe('-123.456789'); + }); + + it('should parse input change correctly if input length is > 18 decimals', () => { + const result = parseInputChange('123.12345678901234567890', new BigNumber(100)); + + expect(result).toBe('123.123456789012345678'); + }); + + it('should return min value if input is less than min', () => { + const result = parseInputChange('50', new BigNumber(100)); + + expect(result).toBe('100'); + }); + }); + + describe('parseMAXInputChange', () => { + it('should parse max input change correctly', () => { + const result = parseMAXInputChange('123.456789', new BigNumber(200), new BigNumber(100), 2); + + expect(result).toBe('123.45'); + }); + + it('should parse max input change correctly if input is not decimals', () => { + const result = parseMAXInputChange('123', new BigNumber(200), new BigNumber(100), 2); + + expect(result).toBe('123'); + }); + + it('should parse max input change correctly if input is negative', () => { + const result = parseMAXInputChange('-123', new BigNumber(-100), new BigNumber(-200), 2); + + expect(result).toBe('-123'); + }); + + it('should parse max input change correctly is input length is > 18 decimals', () => { + const result = parseMAXInputChange('123.1234567890123456789', new BigNumber(200), new BigNumber(100)); + + expect(result).toBe('123.123456789012345678'); + }); + + it('should return max value if input is greater than max', () => { + const result = parseMAXInputChange('300', new BigNumber(200)); + + expect(result).toBe('200'); + }); + + it('should return max value if input is greater than max and max is 0', () => { + const result = parseMAXInputChange('300', new BigNumber(0)); + + expect(result).toBe('0'); + }); + + it('should return max value if input is less than min', () => { + const result = parseMAXInputChange('100', new BigNumber(400), new BigNumber(200)); + + expect(result).toBe('200'); + }); + }); + + describe('sliceDecimals', () => { + it('should slice decimals correctly', () => { + const result = sliceDecimals('123.456789', 2); + + expect(result).toBe('123.45'); + }); + + it('should slice decimals correctly', () => { + const result = sliceDecimals('123', 2); + + expect(result).toBe('123'); + }); + + it('should slice decimals correctly', () => { + const result = sliceDecimals('-123'); + + expect(result).toBe('-123'); + }); + + it('should return empty string for invalid input', () => { + const result = sliceDecimals(undefined, 2); + + expect(result).toBe(''); + }); + }); + + describe('getMaxAmount', () => { + it('should return max amount for ELF chain', () => { + // Mock isELFChain return true + (isELFChain as Mock).mockReturnValue(true); + + const result = getMaxAmount({ + chainId: mockChainId, + symbol: 'ELF', + balance: new BigNumber(100), + crossFee: '0.1', + }); + + expect(result.toFixed()).toBe('99.8959'); + }); + + it('should return max amount for ELF chain and not params', () => { + // Mock isELFChain return true + (isELFChain as Mock).mockReturnValue(true); + + const result = getMaxAmount({ symbol: 'ELF' }); + + expect(result.toFixed()).toBe('0'); + }); + + it('should return balance for non-ELF chain', () => { + // Mock isELFChain return false + (isELFChain as Mock).mockReturnValue(false); + + const result = getMaxAmount({ + chainId: SupportedChainId.MAINNET, + symbol: 'ETH', + balance: new BigNumber(100), + }); + + expect(result.toFixed()).toBe('100'); + }); + + it('should return balance for non-ELF chain and not params', () => { + // Mock isELFChain return false + (isELFChain as Mock).mockReturnValue(false); + + const result = getMaxAmount({}); + + expect(result.toFixed()).toBe('0'); + }); + }); + + describe('handleInputFocus', () => { + // Mock document object + document.getElementById = vi.fn().mockImplementation(() => { + return { + scrollIntoView: vi.fn(), + }; + }); + + it('should scroll input into view on Android', async () => { + // Mock TelegramPlatform.isTelegramPlatform and isAndroid method + (TelegramPlatform.isTelegramPlatform as Mock).mockReturnValue(true); + (isAndroid as Mock).mockReturnValue(true); + + await handleInputFocus('input-is-android'); + + expect(isAndroid).toHaveBeenCalled(); + }); + + it('should scroll input into view on Android', async () => { + // Mock TelegramPlatform.isTelegramPlatform and isAndroid method + (TelegramPlatform.isTelegramPlatform as Mock).mockReturnValue(true); + (isAndroid as Mock).mockReturnValue(false); + + await handleInputFocus('input-not-android'); + + expect(isAndroid).toHaveBeenCalled(); + }); + + it('should scroll input into view on Android', async () => { + // Mock TelegramPlatform.isTelegramPlatform and isAndroid method + (TelegramPlatform.isTelegramPlatform as Mock).mockReturnValue(false); + (isAndroid as Mock).mockReturnValue(true); + + await handleInputFocus('input-is-android'); + + expect(isAndroid).toHaveBeenCalled(); + }); + + it('should scroll input into view on Android', async () => { + // Mock TelegramPlatform.isTelegramPlatform and isAndroid method + (TelegramPlatform.isTelegramPlatform as Mock).mockReturnValue(false); + (isAndroid as Mock).mockReturnValue(false); + + await handleInputFocus('input-not-android'); + + expect(isAndroid).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/utils/__tests__/isMobile.test.ts b/src/utils/__tests__/isMobile.test.ts new file mode 100644 index 00000000..42160081 --- /dev/null +++ b/src/utils/__tests__/isMobile.test.ts @@ -0,0 +1,356 @@ +import { afterEach, describe, it, expect, vi } from 'vitest'; +import isMobile, { isAndroid, isIOS, isMobileDevices } from '../isMobile'; + +const operaAndroidAgent = 'Opera/9.80 (Android; Opera Mini/36.2.2254/120.147; U; en) Presto/2.12.423 Version/12.16'; +const facebookIOSAgent = `Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone14,2;FBMD/iPhone;FBSN/iOS;FBSV/16.3;FBSS/3;FBCR/T-Mobile;FBLC/en_US;FBOP/5]`; +const twitterIOSAgent = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Twitter/9.59.1'; + +// Utility function to mock `navigator` for tests +function mockNavigator(userAgent: string, platform?: string, maxTouchPoints?: number) { + Object.defineProperty(global, 'navigator', { + value: { + userAgent: userAgent, + platform: platform || '', + maxTouchPoints: maxTouchPoints || 0, + }, + writable: true, + }); +} + +describe('isMobile', () => { + afterEach(() => { + // Reset any mocks after each test + vi.resetModules(); + }); + + /** + * Apple Devices Tests + */ + describe('Apple devices', () => { + it('should detect an iPhone', () => { + mockNavigator( + 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148', + ); + + const result = isMobile(); + + expect(result.apple.phone).toBe(true); + expect(result.apple.device).toBe(true); + expect(result.phone).toBe(true); + expect(result.tablet).toBe(false); + }); + + it('should detect an iPad running iOS 13', () => { + mockNavigator( + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15', + 'MacIntel', + 5, + ); + + const result = isMobile(); + + expect(result.apple.tablet).toBe(true); + expect(result.apple.device).toBe(true); + expect(result.phone).toBe(false); + expect(result.tablet).toBe(true); + }); + + it('should detect an iPod', () => { + mockNavigator( + 'Mozilla/5.0 (iPod; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14E277', + ); + + const result = isMobile(); + + expect(result.apple.ipod).toBe(true); + expect(result.apple.device).toBe(true); + expect(result.phone).toBe(true); + expect(result.tablet).toBe(false); + }); + + it('should not falsely identify other devices as Apple', () => { + mockNavigator( + 'Mozilla/5.0 (Linux; Android 10; Pixel 4 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36', + ); + + const result = isMobile(); + + expect(result.apple.device).toBe(false); + }); + }); + + /** + * Android Devices Tests + */ + describe('Android devices', () => { + it('should detect an Android mobile phone', () => { + mockNavigator( + 'Mozilla/5.0 (Linux; Android 10; Pixel 4 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Mobile Safari/537.36', + ); + + const result = isMobile(); + + expect(result.android.phone).toBe(true); + expect(result.android.device).toBe(true); + expect(result.phone).toBe(true); + expect(result.tablet).toBe(false); + }); + + it('should detect an Android tablet', () => { + mockNavigator( + 'Mozilla/5.0 (Linux; Android 9; SM-T720 Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Safari/537.36', + ); + + const result = isMobile(); + + expect(result.android.tablet).toBe(true); + expect(result.android.device).toBe(true); + expect(result.phone).toBe(false); + expect(result.tablet).toBe(true); + }); + + it('should not falsely identify non-Android devices as Android', () => { + mockNavigator( + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + ); + + const result = isMobile(); + + expect(result.android.device).toBe(false); + }); + }); + + /** + * Windows Devices Tests + */ + describe('Windows devices', () => { + it('should detect a Windows phone', () => { + mockNavigator( + 'Mozilla/5.0 (Windows Phone 10.0; Android 6.0.1; Microsoft; Lumia 950 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Mobile Safari/537.36 Edge/15.14900', + ); + + const result = isMobile(); + + expect(result.windows.phone).toBe(true); + expect(result.windows.device).toBe(true); + expect(result.phone).toBe(true); + }); + + it('should detect a Windows tablet', () => { + mockNavigator( + 'Mozilla/5.0 (Windows NT 10.0; ARM; Surface Pro X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36', + ); + + const result = isMobile(); + + expect(result.windows.tablet).toBe(true); + expect(result.windows.device).toBe(true); + expect(result.tablet).toBe(true); + }); + }); + + /** + * Windows Devices Tests + */ + describe('Windows devices', () => { + it('should detect a Windows phone', () => { + mockNavigator( + 'Mozilla/5.0 (Windows Phone 10.0; Android 6.0.1; Microsoft; Lumia 950 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Mobile Safari/537.36 Edge/15.14900', + ); + + const result = isMobile(); + + expect(result.windows.phone).toBe(true); + expect(result.windows.device).toBe(true); + expect(result.phone).toBe(true); + }); + + it('should detect a Windows tablet', () => { + mockNavigator( + 'Mozilla/5.0 (Windows NT 10.0; ARM; Surface Pro X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36', + ); + + const result = isMobile(); + + expect(result.windows.tablet).toBe(true); + expect(result.windows.device).toBe(true); + expect(result.tablet).toBe(true); + }); + }); + + /** + * Other Devices Tests + */ + describe('Other devices', () => { + it('should detect an Opera Mini browser', () => { + mockNavigator('Opera/9.80 (Android; Opera Mini/36.2.2254/120.147; U; en) Presto/2.12.423 Version/12.16'); + + const result = isMobile(); + + expect(result.other.opera).toBe(true); + expect(result.other.device).toBe(true); + }); + + it('should detect a Firefox mobile browser', () => { + mockNavigator('Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0'); + + const result = isMobile(); + + expect(result.other.firefox).toBe(true); + expect(result.other.device).toBe(true); + }); + + it('should detect a Chrome mobile browser', () => { + mockNavigator( + 'Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PQ3A.190705.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36', + ); + + const result = isMobile(); + + expect(result.other.chrome).toBe(true); + expect(result.other.device).toBe(true); + }); + }); + + /** + * Custom Params Tests + */ + describe('Custom Params', () => { + it('should detect an Opera Mini browser with string params', () => { + const result = isMobile(operaAndroidAgent); + + expect(result.android.device).toBe(true); + expect(result.other.opera).toBe(true); + expect(result.other.device).toBe(true); + }); + + it('should detect an Opera Mini browser with object params', () => { + const params = { + userAgent: operaAndroidAgent, + platform: 'MacIntel', + maxTouchPoints: 5, + }; + + const result = isMobile(params); + + expect(result.android.device).toBe(true); + expect(result.other.opera).toBe(true); + expect(result.other.device).toBe(true); + }); + + it('should detect an Opera Mini browser with object params and without maxTouchPoints', () => { + const params = { + userAgent: operaAndroidAgent, + platform: 'MacIntel', + }; + + const result = isMobile(params); + + expect(result.android.device).toBe(true); + expect(result.other.opera).toBe(true); + expect(result.other.device).toBe(true); + }); + + it('should detect an Facebook iOS with object params and without maxTouchPoints', () => { + const result = isMobile(facebookIOSAgent); + + expect(result.apple.phone).toBe(true); + expect(result.apple.device).toBe(true); + expect(result.phone).toBe(true); + }); + + it('should detect an Twitter iOS with object params and without maxTouchPoints', () => { + const result = isMobile(twitterIOSAgent); + + expect(result.apple.phone).toBe(true); + expect(result.apple.device).toBe(true); + expect(result.phone).toBe(true); + }); + }); +}); + +/** + * isMobileDevices Tests + */ +describe('isMobileDevices', () => { + afterEach(() => { + // Reset any mocks after each test + vi.resetModules(); + }); + + it('should return true for Apple or Android devices', () => { + mockNavigator( + 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148', + ); + + const result = isMobileDevices(); + + expect(result).toBe(true); + }); + + it('should return false for non-mobile devices', () => { + mockNavigator( + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + ); + + const result = isMobileDevices(); + + expect(result).toBe(false); + }); +}); + +/** + * isAndroid Tests + */ +describe('isAndroid', () => { + afterEach(() => { + // Reset any mocks after each test + vi.resetModules(); + }); + + it('should return true for Apple or Android devices', () => { + mockNavigator(operaAndroidAgent); + + const result = isAndroid(); + + expect(result).toBe(true); + }); + + it('should return false for non-mobile devices', () => { + mockNavigator(facebookIOSAgent); + + const result = isAndroid(); + + expect(result).toBe(false); + }); +}); + +/** + * isIOS Tests + */ +describe('isIOS', () => { + const operaAndroidAgent = 'Opera/9.80 (Android; Opera Mini/36.2.2254/120.147; U; en) Presto/2.12.423 Version/12.16'; + const facebookIOSAgent = `Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone14,2;FBMD/iPhone;FBSN/iOS;FBSV/16.3;FBSS/3;FBCR/T-Mobile;FBLC/en_US;FBOP/5]`; + + afterEach(() => { + // Reset any mocks after each test + vi.resetModules(); + }); + + it('should return true for Apple or Android devices', () => { + mockNavigator(facebookIOSAgent); + + const result = isIOS(); + + expect(result).toBe(true); + }); + + it('should return false for non-mobile devices', () => { + mockNavigator(operaAndroidAgent); + + const result = isIOS(); + + expect(result).toBe(false); + }); +}); diff --git a/src/utils/__tests__/link.test.ts b/src/utils/__tests__/link.test.ts new file mode 100644 index 00000000..c0dd06f3 --- /dev/null +++ b/src/utils/__tests__/link.test.ts @@ -0,0 +1,87 @@ +import { ChainId } from 'types'; +import { isMobileDevices } from 'utils/isMobile'; +import { getTXLink, openWithBlank } from 'utils/link'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('utils/isMobile', () => ({ + isMobileDevices: vi.fn(), +})); + +vi.mock('utils', () => ({ + getExploreLink: vi.fn((data: string, type: string, _chainId: ChainId) => `https://explore.com/${type}/${data}`), + shortenString: vi.fn((str: string, chars: number) => `${str.slice(0, chars)}...`), +})); + +describe('getTXLink', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + const txId = '0x1234567890123456789012345678901234567890'; + const chainId = 'AELF' as ChainId; + const hrefResult = 'https://explore.com/transaction/0x1234567890123456789012345678901234567890'; + + it('should return a link with shortened transaction ID if isMobileDevices is false', () => { + // Mock isMobileDevices function return false + vi.mocked(isMobileDevices).mockReturnValue(false); + + const link = getTXLink(txId, chainId, 6); + + expect(link.props.href).toBe(hrefResult); + expect(link.props.children).toBe('0x1234...'); + }); + + it('should return a link with shortened transaction ID if isMobileDevices is true', () => { + // Mock isMobileDevices function return true + vi.mocked(isMobileDevices).mockReturnValue(true); + + const link = getTXLink(txId, chainId, 6); + + expect(link.props.href).toBe(hrefResult); + expect(link.props.children).toBe('0x1234...'); + }); + + it('should return a link with shortened transaction ID id not input shortenChars', () => { + // Mock isMobileDevices function return true + vi.mocked(isMobileDevices).mockReturnValue(true); + + const link = getTXLink(txId, chainId); + + expect(link.props.href).toBe(hrefResult); + expect(link.props.children).toBe('0x1234567890123456789012345678901234567890...'); + }); +}); + +describe('openWithBlank', () => { + const mockTargetUrl = 'https://example.com'; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should open a new window with the provided URL', () => { + // Attach the mock to the global window object + const openMock = vi.fn(); + Object.defineProperty(window, 'open', { + value: openMock, + writable: true, + }); + + openWithBlank(mockTargetUrl); + expect(openMock).toHaveBeenCalledWith(mockTargetUrl, '_blank'); + }); + + it('should open a new window with the provided URL and opener is null', () => { + // Attach the mock to the global window object + const openMock = vi.fn().mockReturnValue({ opener: mockTargetUrl }); + Object.defineProperty(window, 'open', { + value: openMock, + writable: true, + }); + + openWithBlank(mockTargetUrl); + expect(openMock).toHaveBeenCalledWith(mockTargetUrl, '_blank'); + }); +}); diff --git a/src/utils/__tests__/listingApplication.test.ts b/src/utils/__tests__/listingApplication.test.ts new file mode 100644 index 00000000..8a4391f1 --- /dev/null +++ b/src/utils/__tests__/listingApplication.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect } from 'vitest'; +import queryString from 'query-string'; +import { ListingStep } from 'constants/listingApplication'; +import { getListingUrl } from 'utils/listingApplication'; + +describe('getListingUrl', () => { + it('should generate the correct URL for TOKEN_INFORMATION with a symbol param', () => { + const step = ListingStep.TOKEN_INFORMATION; + const params = { symbol: 'ABC' }; + + const result = getListingUrl(step, params); + const expectedSearch = queryString.stringify({ symbol: 'ABC' }); + + expect(result).toBe(`/listing-application/token-information?${expectedSearch}`); + }); + + it('should generate the correct URL for SELECT_CHAIN with a symbol param', () => { + const step = ListingStep.SELECT_CHAIN; + const params = { symbol: 'XYZ' }; + + const result = getListingUrl(step, params); + const expectedSearch = queryString.stringify({ symbol: 'XYZ' }); + + expect(result).toBe(`/listing-application/select-chain?${expectedSearch}`); + }); + + it('should generate the correct URL for INITIALIZE_TOKEN_POOL with a symbol param', () => { + const step = ListingStep.INITIALIZE_TOKEN_POOL; + const params = { networks: 'ETH' }; + + const result = getListingUrl(step, params); + const expectedSearch = queryString.stringify({ networks: 'ETH' }); + + expect(result).toBe(`/listing-application/initialize-token-pool?${expectedSearch}`); + }); + + it('should return the correct base path when no params are provided for TOKEN_INFORMATION', () => { + const step = ListingStep.TOKEN_INFORMATION; + + const result = getListingUrl(step); + + expect(result).toBe('/listing-application/token-information'); + }); + + it('should return the correct base path when no params are provided for SELECT_CHAIN', () => { + const step = ListingStep.SELECT_CHAIN; + + const result = getListingUrl(step); + + expect(result).toBe('/listing-application/select-chain'); + }); + + it('should return the correct base path when no params are provided for INITIALIZE_TOKEN_POOL', () => { + const step = ListingStep.INITIALIZE_TOKEN_POOL; + + const result = getListingUrl(step); + + expect(result).toBe('/listing-application/initialize-token-pool'); + }); +}); diff --git a/src/utils/__tests__/localStorage.test.ts b/src/utils/__tests__/localStorage.test.ts new file mode 100644 index 00000000..e677768b --- /dev/null +++ b/src/utils/__tests__/localStorage.test.ts @@ -0,0 +1,74 @@ +import { clearWCStorageByDisconnect } from 'utils/localStorage'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; + +describe('clearWCStorageByDisconnect', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('if there are no WC-related items in the storage, there is no need to remove', () => { + // Mock window and localStorage + const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: vi.fn((key: string) => store[key] || null), + setItem: vi.fn((key: string, value: string) => { + store[key] = value.toString(); + }), + removeItem: vi.fn((key: string) => { + delete store[key]; + }), + clear: vi.fn(() => { + store = {}; + }), + }; + })(); + + Object.defineProperty(window, 'localStorage', { + value: localStorageMock, + writable: true, + }); + + clearWCStorageByDisconnect(); + + expect(localStorageMock.removeItem).not.toHaveBeenCalledWith('wc-session'); + expect(localStorageMock.removeItem).not.toHaveBeenCalledWith('wc-topic'); + expect(localStorageMock.removeItem).not.toHaveBeenCalledWith('other-key'); + }); + + it('should remove all WC-related items from localStorage', () => { + // Mock window and localStorage + const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: vi.fn((key: string) => store[key] || null), + setItem: vi.fn((key: string, value: string) => { + store[key] = value.toString(); + }), + removeItem: vi.fn((key: string) => { + delete store[key]; + }), + clear: vi.fn(() => { + store = {}; + }), + ['wc-session']: 'value1', + ['wc-topic']: 'value2', + ['other-key']: 'value3', + }; + })(); + + Object.defineProperty(window, 'localStorage', { + value: localStorageMock, + writable: true, + }); + + clearWCStorageByDisconnect(); + + expect(localStorageMock.removeItem).toHaveBeenCalledWith('wc-session'); + expect(localStorageMock.removeItem).toHaveBeenCalledWith('wc-topic'); + expect(localStorageMock.removeItem).not.toHaveBeenCalledWith('other-key'); + }); +}); diff --git a/src/utils/__tests__/message.test.tsx b/src/utils/__tests__/message.test.tsx new file mode 100644 index 00000000..5d4bb675 --- /dev/null +++ b/src/utils/__tests__/message.test.tsx @@ -0,0 +1,437 @@ +import { describe, it, expect, vi, beforeEach, Mock } from 'vitest'; +import { txSuccess, txError, txMessage, copyTxMessage } from '../message'; +import { message as antMessage } from 'antd'; +import writeText from 'copy-to-clipboard'; +import CommonMessage from 'components/CommonMessage'; +import { ChainId } from 'types'; +import { render, screen } from '@testing-library/react'; + +// Mock external dependencies +vi.mock('antd', () => ({ + message: { + config: vi.fn(), + open: vi.fn(), + success: vi.fn(), + }, +})); + +vi.mock('i18next', () => ({ + default: { t: (str: string) => str }, +})); + +vi.mock('react-i18next', () => ({ + Trans: ({ children }: any) => children, +})); + +vi.mock('copy-to-clipboard', () => ({ + default: vi.fn(), +})); + +// Mock IconFont component +vi.mock('components/IconFont', () => ({ + default: ({ type, onClick }: { type: string; onClick: () => void }) => ( + + ), +})); + +// Mock getTXLink component +vi.mock('utils/link', () => ({ + getTXLink: (txId: string, chainId: ChainId, chars: number) => ( + {`${txId}-${chainId}-${chars}`} + ), +})); + +vi.mock('components/CommonMessage', () => { + return { + default: { + error: vi.fn(), + }, + }; +}); + +const mockChainId = 'AELF' as ChainId; + +describe('txSuccess', () => { + const mockChainId = 1 as ChainId; + const mockMessage = 'Transaction completed successfully'; + + // Test 1: When TransactionId exists + it('should call copyTxMessage with correct parameters', () => { + const mockReq = { TransactionId: '0x12345' }; + + txSuccess({ req: mockReq, chainId: mockChainId, message: mockMessage }); + + expect(antMessage.success).not.toHaveBeenCalled(); + }); + + // Test 2: When TransactionId is missing + it('should show antd message when TransactionId missing', () => { + txSuccess({ req: {}, chainId: mockChainId, message: mockMessage }); + + expect(antMessage.success).toHaveBeenCalledWith(mockMessage); + }); + + // Test 3: Boundary case - empty TransactionId string + it('should handle empty TransactionId string', () => { + txSuccess({ req: { TransactionId: '' }, chainId: mockChainId, message: mockMessage }); + + expect(antMessage.success).toHaveBeenCalledWith(mockMessage); + }); + + // Test 4: Boundary case - null request object + it('should handle null request object', () => { + txSuccess({ req: null, chainId: mockChainId, message: mockMessage }); + + expect(antMessage.success).toHaveBeenCalledWith(mockMessage); + }); + + // Test 5: Boundary case - undefined parameters + it('should handle undefined parameters', () => { + // @ts-ignore: Test undefined case + txSuccess({ chainId: mockChainId, message: mockMessage }); + + expect(antMessage.success).toHaveBeenCalledWith(mockMessage); + }); + + // Test 6: Different ChainId types + it('should handle different ChainId types', () => { + const altChainId = 4 as ChainId; + const mockReq = { TransactionId: '0xabc' }; + + txSuccess({ req: mockReq, chainId: altChainId, message: mockMessage }); + }); +}); + +describe('txError', () => { + const mockChainId = 'AELF' as ChainId; + const baseParams = { + chainId: mockChainId, + decimals: 8, + message: 'default_error', + req: { TransactionId: '0x12345', error: {} }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle transaction hash display correctly', () => { + const params = { + ...baseParams, + req: { ...baseParams.req, isTransactionHash: true }, + }; + + txError(params); + + // Verify CommonMessage.error to be called + expect(CommonMessage.error).toHaveBeenCalled(); + }); + + it('should handle transaction hash display correctly', () => { + const params = { + ...baseParams, + req: { ...baseParams.req, isTransactionHash: false }, + }; + + txError(params); + + // Verify CommonMessage.error to be called + expect(CommonMessage.error).toHaveBeenCalled(); + }); + + it('should display basic error message', () => { + txError({ ...baseParams, req: { error: {} } }); + expect(CommonMessage.error).toHaveBeenCalledWith(expect.anything()); + }); + + it('should apply decimal formatting when specified', () => { + const errorMsg = 'Balance too low'; + txError({ + ...baseParams, + req: { error: { message: errorMsg } }, + }); + + expect(CommonMessage.error).toHaveBeenCalled(); + }); + + it('should enable text copy on click', () => { + const errorText = 'Copyable error'; + txError({ + ...baseParams, + req: { error: { message: errorText } }, + }); + + const messageComponent = (CommonMessage.error as any).mock.calls[0][0]; + messageComponent.props.onClick(); + + expect(writeText).toHaveBeenCalledWith(errorText); + }); + + it('should handle empty transaction ID gracefully', () => { + txError({ + ...baseParams, + req: { TransactionId: '', error: { message: 'Insufficient balance and Current balance is 10' } }, + }); + + expect(CommonMessage.error).not.toHaveBeenCalledWith(expect.stringContaining('View TX')); + }); + + it('should handle number error.message', () => { + txError({ + ...baseParams, + req: { TransactionId: '', error: { message: 400 } }, + }); + + expect(CommonMessage.error).not.toHaveBeenCalledWith(expect.stringContaining('View TX')); + }); + + it('should handle null request object', () => { + txError({ + ...baseParams, + req: null, + }); + + expect(CommonMessage.error).toHaveBeenCalledWith(expect.anything()); + }); + + it('should use default message when error missing', () => { + txError({ + ...baseParams, + req: { error: null }, + }); + + expect(CommonMessage.error).toHaveBeenCalled(); + }); + + it('should use default message when error missing and decimals is undefined', () => { + txError({ + ...baseParams, + req: { error: null }, + decimals: undefined, + }); + + expect(CommonMessage.error).toHaveBeenCalled(); + }); +}); + +describe('txMessage function', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('when request has error (req.error exists)', () => { + it('should call txError with translated message and decimals', () => { + const mockReq = { error: new Error('test') }; + + txMessage({ + req: mockReq, + chainId: mockChainId, + decimals: 18, + }); + + // Verify CommonMessage.error to be called + expect(CommonMessage.error).toHaveBeenCalled(); + }); + + it('should use default errorMessage when not provided', () => { + const mockReq = { error: new Error('test') }; + + txMessage({ + req: mockReq, + chainId: mockChainId, + }); + + // Verify CommonMessage.error to be called + expect(CommonMessage.error).toHaveBeenCalled(); + }); + }); + + describe('when request succeeds (no req.error)', () => { + it('should call copyTxMessage when copy=true', () => { + const mockReq = { txHash: '0x123' }; + + txMessage({ + req: mockReq, + chainId: mockChainId, + copy: true, + }); + + // Verify CommonMessage.error to be called + expect(antMessage.open).toHaveBeenCalled(); + }); + + it('should call txSuccess when copy=false', () => { + const mockReq = { txHash: '0x123' }; + + txMessage({ + req: mockReq, + chainId: mockChainId, + copy: false, + }); + + // Verify CommonMessage.error to be called + expect(antMessage.success).toHaveBeenCalled(); + }); + + it('should use default successMessage when not provided', () => { + const mockReq = { txHash: '0x123' }; + + txMessage({ + req: mockReq, + chainId: mockChainId, + }); + + // Verify CommonMessage.error to be called + expect(antMessage.success).toHaveBeenCalled(); + }); + + it('should default to txSuccess when copy=undefined', () => { + const mockReq = { txHash: '0x123' }; + + txMessage({ + req: mockReq, + chainId: mockChainId, + }); + + // Verify CommonMessage.error to be called + expect(antMessage.success).toHaveBeenCalled(); + }); + }); + + it('should handle undefined decimals parameter', () => { + const mockReq = { error: new Error('test') }; + + txMessage({ + req: mockReq, + chainId: mockChainId, + }); + + // Verify CommonMessage.error to be called + expect(CommonMessage.error).toHaveBeenCalled(); + }); +}); + +describe('copyTxMessage', () => { + const mockChainId = 1 as ChainId; + const mockTransactionId = '0x1234567890abcdef'; + const mockMessage = 'Transaction Successful'; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should render message with all elements (with custom message)', () => { + // Call function with all parameters + copyTxMessage({ + req: { TransactionId: mockTransactionId }, + chainId: mockChainId, + message: mockMessage, + }); + + // Verify antd message.open called with correct content + expect(antMessage.open).toHaveBeenCalled(); + const messageContent = (antMessage.open as Mock).mock.calls[0][0].content; + + // Render the content to inspect structure + render(messageContent); + + // Verify title with custom message + expect(screen.getByText(mockMessage)).toBeInTheDocument(); + + // Verify transaction link + const txLink = screen.getByTestId('tx-link'); + expect(txLink.textContent).toBe(`${mockTransactionId}-${mockChainId}-6`); + + // Verify copy icon + const copyIcon = screen.getByTestId('icon-copy'); + expect(copyIcon).toBeInTheDocument(); + }); + + it('should render message without custom message', () => { + // Call function without message + copyTxMessage({ + req: { TransactionId: mockTransactionId }, + chainId: mockChainId, + }); + + const messageContent = (antMessage.open as Mock).mock.calls[0][0].content; + render(messageContent); + + // Should not show message title + expect(screen.queryByTestId('tx-title-row')).not.toBeInTheDocument(); + }); + + it('should handle empty transaction ID', () => { + // Call function with empty TransactionId + copyTxMessage({ + req: { TransactionId: '' }, + chainId: mockChainId, + message: mockMessage, + }); + + const messageContent = (antMessage.open as Mock).mock.calls[0][0].content; + render(messageContent); + + // Should show empty transaction ID + expect(screen.getByTestId('tx-link').textContent).toBe(`-${mockChainId}-6`); + }); + + it('should handle clipboard copy', async () => { + // Call function with valid parameters + copyTxMessage({ + req: { TransactionId: mockTransactionId }, + chainId: mockChainId, + message: mockMessage, + }); + + const messageContent = (antMessage.open as Mock).mock.calls[0][0].content; + render(messageContent); + + // Simulate copy icon click + const copyIcon = screen.getByTestId('icon-copy'); + copyIcon.click(); + + // Verify clipboard write + expect(writeText).toHaveBeenCalledWith(mockTransactionId); + + // Verify success message + expect(antMessage.success).toHaveBeenCalledWith('Copy Success'); + }); + + it('should handle clipboard copy with req is undefined', async () => { + // Call function with valid parameters + copyTxMessage({ + req: undefined, + chainId: mockChainId, + message: mockMessage, + }); + + const messageContent = (antMessage.open as Mock).mock.calls[0][0].content; + render(messageContent); + + // Simulate copy icon click + const copyIcon = screen.getByTestId('icon-copy'); + copyIcon.click(); + + // Verify clipboard write + expect(writeText).toHaveBeenCalledWith(undefined); + + // Verify success message + expect(antMessage.success).toHaveBeenCalledWith('Copy Success'); + }); + + it('should handle invalid chainId', () => { + // Call function with invalid chainId + copyTxMessage({ + req: { TransactionId: mockTransactionId }, + chainId: 999 as ChainId, + message: mockMessage, + }); + + const messageContent = (antMessage.open as Mock).mock.calls[0][0].content; + render(messageContent); + + // Verify tx link with invalid chainId + expect(screen.getByTestId('tx-link').textContent).toBe(`${mockTransactionId}-999-6`); + }); +}); diff --git a/src/utils/__tests__/network.test.ts b/src/utils/__tests__/network.test.ts new file mode 100644 index 00000000..987a779b --- /dev/null +++ b/src/utils/__tests__/network.test.ts @@ -0,0 +1,542 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + getNetworkInfo, + getSupportedChainIdsFromWalletConnectSession, + isChainAllowed, + switchChain, + switchNetwork, +} from 'utils/network'; +import { ChainId, COINBASE_WALLET_ID, METAMASK_WALLET_ID, WALLET_CONNECT_ID } from 'types'; +import { eventBus } from 'utils/eBridgeEventBus'; +import { isELFChain } from 'utils/aelfUtils'; +import storages from 'constants/storages'; +import { Connector } from 'wagmi'; +import { switchChainByWagmi } from 'utils/wagmi'; + +// Mock the NetworkList in the network module +vi.mock('constants/index', async (importOriginal) => { + const actual: any = await importOriginal(); + + // Mock NetworkList + const NetworkList = [ + { + info: { + chainId: 'AELF' as ChainId, + name: 'MainNet', + url: 'https://mainnet.api.com', + }, + }, + { + info: { + chainId: 'tDVV' as ChainId, + name: 'TestNet', + url: 'https://testnet.api.com', + }, + }, + ]; + + return { + ...actual, + NetworkList, + }; +}); + +vi.mock('constants/chain', async (importOriginal) => { + const actual: any = await importOriginal(); + + return { + ...actual, + ALL_SUPPORTED_CHAIN_IDS: [11155111, 1, 3, 4, 56, 137, 42161], + }; +}); + +vi.mock('utils/aelfUtils', () => ({ + isELFChain: vi.fn(), +})); + +vi.mock('components/CommonMessage', () => ({ + default: { + error: vi.fn(), + }, +})); + +vi.mock('constants/storages', () => { + return { + default: { + storages: { serERCChainId: 'user-erc-chain-id', userELFChainId: 'user-elf-chain-id' }, + }, + }; +}); + +vi.mock('utils/eBridgeEventBus', () => { + return { + eventBus: { + emit: vi.fn(), + }, + }; +}); + +vi.mock('utils/wagmi', () => { + return { + switchChainByWagmi: vi.fn(), + }; +}); + +// Mock global objects +const mockEthereum = { + providerMap: new Map([ + ['MetaMask', { isMetaMask: true, request: vi.fn() }], + ['OtherWallet', {}], + ]), + request: vi.fn(), +}; + +// Mock dependencies +vi.stubGlobal('ethereum', mockEthereum); + +vi.stubGlobal('console', { + log: vi.fn(), + error: vi.fn(), +}); + +const mainChainId = 'AELF' as ChainId; +const dappChainId = 'tDVV' as ChainId; + +const mainnetChainId = 11155111 as ChainId; + +const mockSepoliaInfo = { + chainId: mainnetChainId, + exploreUrl: 'https://sepolia.etherscan.io/', + rpcUrl: 'https://sepolia.drpc.org', + chainName: 'sepolia', + nativeCurrency: { name: 'SETH', decimals: 18, symbol: 'ETH' }, + rpcUrls: ['https://sepolia.drpc.org'], + blockExplorerUrls: ['https://sepolia.etherscan.io/'], + iconUrls: ['https://mock.icon.com'], +}; + +const mockAelfInfo = { + chainId: mainChainId, + exploreUrl: 'https://testnet.aelfscan.io/AELF/', + rpcUrl: 'https://aelf-test-node.aelf.io', + chainName: 'aelf MainChain', + nativeCurrency: { name: 'ELF', decimals: 8, symbol: 'ELF' }, +}; + +describe('switchNetwork', () => { + const baseInfo = { + chainId: 1, + chainName: 'Test Chain', + nativeCurrency: { + name: 'Test', + symbol: 'TEST', + decimals: 18, + }, + rpcUrls: ['https://rpc.test.com'], + blockExplorerUrls: ['https://explorer.test.com'], + iconUrls: ['https://icon.test.com'], + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should handle string chainId (ELF chain)', async () => { + // Test ELF chain scenario + const result = await switchNetwork({ ...baseInfo, chainId: 'AELF' }); + + // Verify event emission + expect(eventBus.emit).toHaveBeenCalled(); + expect(result).toBe(true); + }); + + it('should add new chain when nativeCurrency and chainName exist', async () => { + // Test wallet_addEthereumChain path + const result = await switchNetwork(baseInfo); + + expect(result).toBe(true); + }); + + it('should switch chain when missing nativeCurrency or chainName', async () => { + // Test wallet_switchEthereumChain path + const result = await switchNetwork({ + ...baseInfo, + nativeCurrency: undefined, + chainName: undefined, + }); + + expect(result).toBe(true); + }); + + it('should handle ERC chain with missing provider', async () => { + // Remove ethereum provider + vi.stubGlobal('ethereum', undefined); + + const result = await switchNetwork(baseInfo); + + // Verify error logging + expect(console.error).toHaveBeenCalledWith( + "Can't setup the RPC network on metamask because window.ethereum is undefined", + ); + expect(result).toBe(false); + }); + + it('should handle metamask provider in providerMap', async () => { + // Test providerMap handling + vi.stubGlobal('ethereum', { + providerMap: new Map([['MetaMask', { isMetaMask: true, request: vi.fn() }]]), + }); + + const result = await switchNetwork(baseInfo); + + expect(result).toBe(true); + }); + + it('should handle request errors gracefully', async () => { + // Mock rejected request + vi.stubGlobal('ethereum', { + providerMap: 'invalid' as any, + request: vi.fn().mockRejectedValue(new Error('User rejected')), + }); + + const result = await switchNetwork(baseInfo); + + // Verify error logging + expect(console.error).toHaveBeenCalledWith('switchNetwork', Error('User rejected')); + expect(result).toBe(false); + }); + + it('should handle invalid chainId conversions', async () => { + vi.stubGlobal('ethereum', mockEthereum); + + // Test non-numeric chainId + const result = await switchNetwork({ ...baseInfo, chainId: 'invalid' }); + + expect(result).toBe(true); + }); + + it('should handle providerMap iteration errors', async () => { + // Break providerMap iteration + vi.stubGlobal('ethereum', { + providerMap: { + AELF: { url: 'http://aelf.com' }, + ERC: { url: 'http://erc.com' }, + }, + request: vi.fn(), + }); + + const result = await switchNetwork(baseInfo); + + expect(result).toBe(true); + }); +}); + +describe('isChainAllowed', () => { + const supportedChainId = 1; // Example supported chain ID + const unsupportedChainId = 999; // Example unsupported chain ID + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return true for supported chainId with metamask connector', () => { + const result = isChainAllowed({ id: METAMASK_WALLET_ID } as unknown as Connector, supportedChainId); + + expect(result).toBe(true); + }); + + it('should return false for unsupported chainId with metamask connector', () => { + const result = isChainAllowed({ id: METAMASK_WALLET_ID } as unknown as Connector, unsupportedChainId); + + expect(result).toBe(false); + }); + + it('should return true for supported chainId with coinbaseWallet connector', () => { + const result = isChainAllowed({ id: COINBASE_WALLET_ID } as unknown as Connector, supportedChainId); + + expect(result).toBe(true); + }); + + it('should return false for unsupported chainId with coinbaseWallet connector', () => { + const result = isChainAllowed({ id: COINBASE_WALLET_ID } as unknown as Connector, unsupportedChainId); + + expect(result).toBe(false); + }); + + it('should return true for supported chainId with walletConnect connector', () => { + const result = isChainAllowed({ id: WALLET_CONNECT_ID } as unknown as Connector, supportedChainId); + + expect(result).toBe(true); + }); + + it('should return false for unsupported chainId with walletConnect connector', () => { + const result = isChainAllowed({ id: WALLET_CONNECT_ID } as unknown as Connector, unsupportedChainId); + + expect(result).toBe(false); + }); + + it('should return false for any chainId with an unknown connector', () => { + const result = isChainAllowed({ id: 'unknownConnectorId' } as unknown as Connector, supportedChainId); + + expect(result).toBe(false); + }); +}); + +describe('getSupportedChainIdsFromWalletConnectSession', () => { + it('should return empty array for empty session', () => { + // Test undefined session + expect(getSupportedChainIdsFromWalletConnectSession()).toEqual([]); + + // Test empty object + expect(getSupportedChainIdsFromWalletConnectSession({})).toEqual([]); + + // Test null input + expect(getSupportedChainIdsFromWalletConnectSession(null)).toEqual([]); + }); + + it('should extract unique chainIds from complex session structure', () => { + // Mock complex session data + const mockSession = { + namespaces: { + 'eip155:1': { + chains: ['eip155:1', 1, 'invalid'], + accounts: ['eip155:1:0x123', 'eip155:3'], + }, + 'eip155:3': { + chains: [3, 'eip155:3'], + accounts: ['eip155:3:0x456'], + }, + cosmos: { + chains: ['cosmos:cosmoshub-4'], + accounts: ['cosmos:cosmoshub-4:cosmos1...'], + }, + }, + }; + + const result = getSupportedChainIdsFromWalletConnectSession(mockSession); + expect(result).toEqual([1, 3]); + }); + + it('should handle mixed data types and invalid values', () => { + // Test session with mixed valid/invalid values + const mockSession = { + namespaces: { + 'eip155:invalid': { + // Should be ignored + chains: [NaN, {}], + accounts: [null], + }, + 'eip155:5': { + chains: ['eip155:5', '5', 5], + accounts: [5, 'eip155:5:0x789'], + }, + 6: { + // Number key + chains: ['6'], + accounts: ['eip155:6'], + }, + }, + }; + + const result = getSupportedChainIdsFromWalletConnectSession(mockSession); + expect(result).toEqual([5, 6, 0]); + }); + + it('should deduplicate chainIds', () => { + // Test duplicate values in different locations + const mockSession = { + namespaces: { + 'eip155:1': { + chains: ['eip155:1'], + accounts: ['eip155:1:0x123'], + }, + 'eip155:2': { + chains: [2], + accounts: ['eip155:1'], // Duplicate with namespace key + }, + }, + }; + + const result = getSupportedChainIdsFromWalletConnectSession(mockSession); + expect(result).toEqual([1, 2]); + }); + + it('should handle numeric chainIds', () => { + // Test numeric values in chains array + const mockSession = { + namespaces: { + 'eip155:1': { + chains: [1, 100], + accounts: [42], + }, + }, + }; + + const result = getSupportedChainIdsFromWalletConnectSession(mockSession); + expect(result).toEqual([1, 100, 42]); + }); + + it('should filter out null values', () => { + // Test session with invalid entries + const mockSession = { + namespaces: { + 'eip155:invalid': { + chains: ['invalid'], + accounts: [{}], + }, + 'eip155:10': { + chains: [10], + accounts: ['eip155:10:0xabc'], + }, + }, + }; + + const result = getSupportedChainIdsFromWalletConnectSession(mockSession); + expect(result).toEqual([10]); + }); +}); + +describe('switchChain', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + // Test 1: Handle string chainId case + it('should handle string chainId and emit event', async () => { + const result = await switchChain(mockAelfInfo); + + expect(eventBus.emit).toHaveBeenCalledWith(storages.userELFChainId, mainChainId); + expect(result).toBe(true); + }); + + // Test 2: Handle non-ELF chain with matching web3ChainId + it('should return undefined for non-ELF chain with matching web3ChainId', async () => { + // Mock isELFChain return false + vi.mocked(isELFChain).mockReturnValue(false); + + const result = await switchChain(mockSepoliaInfo, undefined, true, 1); + expect(result).toBeUndefined(); + }); + + // Test 3: Handle missing connector or string connector + it('should return early with invalid connector', async () => { + const result1 = await switchChain(mockSepoliaInfo); + const result2 = await switchChain(mockSepoliaInfo, 'metamask'); + + expect(result1).toBeUndefined(); + expect(result2).toBeUndefined(); + }); + + // Test 4: Handle WalletConnect chain validation failure + it('should show error for unsupported WalletConnect chain', async () => { + const mockConnector = { + provider: { session: { namespaces: {} } }, + activate: vi.fn(), + }; + + await expect(switchChain(mockSepoliaInfo, mockConnector as unknown as Connector, true)).rejects.toThrow( + 'Chain 11155111 not supported for connector (object)', + ); + }); + + // Test 5: Executed switchNetwork + it('should executed switchNetwork method', async () => { + const mockConnector = { + provider: { session: { namespaces: {} } }, + activate: vi.fn(), + }; + + const result = await switchChain(mockSepoliaInfo, mockConnector as unknown as Connector, false); + + expect(result).toBeUndefined(); + }); + + // Test 6: Handle WalletConnect activation + it('should activate wallet connector', async () => { + await switchChain( + { ...mockSepoliaInfo, chainId: 3 as any }, + { id: METAMASK_WALLET_ID } as unknown as Connector, + true, + ); + + expect(switchChainByWagmi).toHaveBeenCalledWith({ chainId: 3 }); + }); + + // Test 7: Handle network connection activation + it('should activate network connector', async () => { + await switchChain(mockSepoliaInfo, { id: COINBASE_WALLET_ID } as unknown as Connector, true); + + expect(switchChainByWagmi).toHaveBeenCalledWith({ chainId: 11155111 }); + }); + + // Test 8: The network connection activate method is not executed + it('should not execute activate method', async () => { + // Mock isELFChain return false + vi.mocked(isELFChain).mockReturnValue(false); + + const result = await switchChain( + mockSepoliaInfo, + { id: COINBASE_WALLET_ID } as unknown as Connector, + true, + 11155111, + ); + + expect(result).toBeUndefined(); + expect(switchChainByWagmi).not.toHaveBeenCalled(); + }); + + // Test 9: Handle inactive web3 case + it('should switch network when web3 is inactive', async () => { + const result = await switchChain(mockSepoliaInfo, undefined, false); + + expect(result).toBeUndefined(); + }); + + // Test 10: Boundary test - minimum valid chainId + it('should handle minimum valid chainId (1)', async () => { + await switchChain({ ...mockSepoliaInfo, chainId: 1 }); + + expect(eventBus.emit).toHaveBeenCalledWith(storages.userERCChainId, 1); + }); + + // Test 11: Boundary test - maximum safe integer chainId + it('should handle maximum safe chainId (2^53-1)', async () => { + const maxSafeChainId = Number.MAX_SAFE_INTEGER; + await switchChain({ ...mockSepoliaInfo, chainId: maxSafeChainId }); + + expect(eventBus.emit).toHaveBeenCalledWith(storages.userERCChainId, maxSafeChainId); + }); +}); + +describe('getNetworkInfo', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return the correct network info for MainNet', () => { + const result = getNetworkInfo(mainChainId); + expect(result).toEqual({ + info: { + chainId: mainChainId, + name: 'MainNet', + url: 'https://mainnet.api.com', + }, + }); + }); + + it('should return the correct network info for TestNet', () => { + const result = getNetworkInfo(dappChainId); + expect(result).toEqual({ + info: { + chainId: dappChainId, + name: 'TestNet', + url: 'https://testnet.api.com', + }, + }); + }); + + it('should return undefined for an unknown chainId', () => { + const result = getNetworkInfo('UnknownChainId' as any); + expect(result).toBeUndefined(); + }); +}); diff --git a/src/utils/__tests__/pools.test.ts b/src/utils/__tests__/pools.test.ts new file mode 100644 index 00000000..6963c0c9 --- /dev/null +++ b/src/utils/__tests__/pools.test.ts @@ -0,0 +1,423 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { addLiquidity, removeLiquidity, getTotalLiquidity, getMyLiquidity } from '../pools'; +import { ChainId, TokenInfo } from 'types'; +import { ContractBasic } from '../contract'; +import { checkApprove } from 'contracts'; +import { REQ_CODE } from 'constants/misc'; +import { getBalanceByWagmi, readContractByWagmi } from '../wagmi'; +import { POOLS_ABI } from 'constants/abis'; +import { isELFChain } from '../aelfUtils'; +import { getTokenInfoByWhitelist } from '../whitelist'; + +// Mock external dependencies +vi.mock('../whitelist', () => ({ + getTokenInfoByWhitelist: vi.fn(), +})); + +vi.mock('../calculate', () => ({ + timesDecimals: vi.fn((amount: string, decimals?: number) => ({ + toFixed: () => (decimals ? amount.padEnd(decimals, '0') : amount), + })), +})); + +vi.mock('../aelfUtils', () => ({ + isELFChain: vi.fn(), +})); + +vi.mock('contracts', () => ({ + checkApprove: vi.fn(), +})); + +vi.mock('../contract'); + +vi.mock('../wagmi', () => ({ + getBalanceByWagmi: vi.fn(), + readContractByWagmi: vi.fn(), +})); + +const mockTokenInfo: TokenInfo = { + symbol: 'ELF', + decimals: 8, + isNativeToken: false, + address: '0xToken', +}; + +describe('Liquidity Functions', () => { + const mockAccount = '0xAccount'; + const mockAmount = '100'; + const mockChainId = 1 as ChainId; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + + // Default mock implementations + vi.mocked(checkApprove).mockResolvedValue(REQ_CODE.Success); + }); + + describe('addLiquidity', () => { + it('should handle aelf chain native token flow', async () => { + // Setup aelf chain environment + vi.mocked(isELFChain).mockReturnValue(true); + vi.mocked(getTokenInfoByWhitelist).mockReturnValue({ + ...mockTokenInfo, + isNativeToken: true, + }); + + const mockCallSendMethod = vi.fn().mockResolvedValue({ status: 'success', transactionHash: '0xTxHash' }); + + // Execute function + const result = await addLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + }); + + // Verify native token handling + expect(result).toHaveProperty('status', 'success'); + expect(result).toHaveProperty('transactionHash', '0xTxHash'); + expect(mockCallSendMethod).toHaveBeenCalledWith('addLiquidity', mockAccount, { + tokenSymbol: 'ELF', + amount: '10000000', + }); + expect(checkApprove).not.toHaveBeenCalled(); + }); + + it('should handle aelf chain non-native token flow', async () => { + // Setup aelf chain environment + vi.mocked(isELFChain).mockReturnValue(true); + vi.mocked(getTokenInfoByWhitelist).mockReturnValue({ + ...mockTokenInfo, + symbol: 'USDT', + }); + + const mockCallSendMethod = vi.fn().mockResolvedValue({ status: 'success', transactionHash: '0xTxHash' }); + + // Execute function + const result = await addLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + }); + + // Verify native token handling + expect(result).toHaveProperty('status', 'success'); + expect(result).toHaveProperty('transactionHash', '0xTxHash'); + expect(mockCallSendMethod).toHaveBeenCalledWith('addLiquidity', mockAccount, { + tokenSymbol: 'USDT', + amount: '10000000', + }); + }); + + it('should handle EVM chain ERC20 token flow', async () => { + // Setup EVM environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(getTokenInfoByWhitelist).mockReturnValue(mockTokenInfo); + + const mockCallSendMethod = vi.fn().mockResolvedValue({ status: 'success', transactionHash: '0xTxHash' }); + + // Execute function + const result = await addLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + }); + + expect(result.status).toBe('success'); + expect(result.transactionHash).toBe('0xTxHash'); + // Verify ERC20 approval check + expect(checkApprove).toHaveBeenCalledWith(1, '0xToken', mockAccount, '', '10000000', undefined, undefined); + }); + + it('should handle EVM chain ERC20 native token flow', async () => { + // Setup EVM environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(getTokenInfoByWhitelist).mockReturnValue({ ...mockTokenInfo, symbol: 'ETH', isNativeToken: true }); + + const mockCallSendMethod = vi.fn().mockResolvedValue({ status: 'success', transactionHash: '0xTxHash' }); + + // Execute function + const result = await addLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + }); + + expect(result.status).toBe('success'); + expect(result.transactionHash).toBe('0xTxHash'); + }); + + it('should return correctly if input empty tokenInfo', async () => { + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(getTokenInfoByWhitelist).mockReturnValue(mockTokenInfo); + + const mockCallSendMethod = vi.fn().mockResolvedValue({ status: 'success', transactionHash: '0xTxHash' }); + + // Execute function + const result = await addLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + tokenInfo: {} as any, + }); + + expect(result.status).toBe('success'); + expect(result.transactionHash).toBe('0xTxHash'); + // Verify ERC20 approval check + expect(checkApprove).toHaveBeenCalledWith(1, '0xToken', mockAccount, '', '10000000', undefined, undefined); + expect(checkApprove).toHaveBeenCalled(); + }); + + it('should throw error when approval fails', async () => { + // Setup failed approval + vi.mocked(checkApprove).mockResolvedValue(REQ_CODE.Fail); + vi.mocked(isELFChain).mockReturnValue(false); + + const mockCallSendMethod = vi.fn().mockResolvedValue({ status: 'error', transactionHash: '' }); + + // Verify error throwing + await expect( + addLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + }), + ).rejects.toThrow('Failed to add liquidity'); + }); + + it('should handle missing token info', async () => { + // Test missing token info scenario + vi.mocked(getTokenInfoByWhitelist).mockReturnValue(undefined); + + const mockCallSendMethod = vi.fn().mockRejectedValue({ status: 'error', transactionHash: '' }); + + await expect( + addLiquidity({ + symbol: 'UNKNOWN', + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + }), + ).rejects.toThrow(); + }); + }); + + describe('removeLiquidity', () => { + const mockCallSendMethod = vi.fn().mockResolvedValue({ status: 'success', transactionHash: '0xTxHash' }); + + it('should handle aelf chain removal', async () => { + // Setup aelf chain environment + vi.mocked(isELFChain).mockReturnValue(true); + vi.mocked(getTokenInfoByWhitelist).mockReturnValue({ + ...mockTokenInfo, + isNativeToken: true, + }); + + // Execute function + const result = await removeLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + }); + + expect(result).toHaveProperty('status', 'success'); + expect(result).toHaveProperty('transactionHash', '0xTxHash'); + + // Verify ELF-specific call + expect(mockCallSendMethod).toHaveBeenCalledWith('removeLiquidity', mockAccount, { + tokenSymbol: 'ELF', + amount: '10000000', + }); + }); + + it('should handle EVM chain removal', async () => { + // Setup EVM environment + vi.mocked(isELFChain).mockReturnValue(false); + vi.mocked(getTokenInfoByWhitelist).mockReturnValue({ + ...mockTokenInfo, + isNativeToken: false, + }); + + // Execute function + const result = await removeLiquidity({ + account: mockAccount, + amount: mockAmount, + chainId: mockChainId, + poolContract: { callSendMethod: mockCallSendMethod } as unknown as ContractBasic, + tokenInfo: mockTokenInfo, + }); + + expect(result).toHaveProperty('status', 'success'); + expect(result).toHaveProperty('transactionHash', '0xTxHash'); + + // Verify EVM-specific call + expect(mockCallSendMethod).toHaveBeenCalledWith('removeLiquidity', mockAccount, ['0xToken', '10000000']); + }); + }); + + describe('getTotalLiquidity', () => { + it('should handle aelf chain total liquidity', async () => { + // Setup aelf chain environment + const elfContract = { + contractType: 'ELF', + callViewMethod: vi.fn().mockResolvedValue({ totalSupply: '1000' }), + } as unknown as ContractBasic; + + // Execute function + const result = await getTotalLiquidity({ + poolContract: elfContract, + symbol: 'ELF', + }); + + // Verify view method call + expect(elfContract.callViewMethod).toHaveBeenCalledWith('GetTokenPoolInfo', { tokenSymbol: 'ELF' }); + expect(result).toBeUndefined(); + }); + + it('should handle EVM chain total liquidity', async () => { + // Setup EVM environment + const evmContract = { + contractType: 'EVM', + callViewMethod: vi.fn().mockResolvedValue({ totalSupply: '1000' }), + } as unknown as ContractBasic; + + // Mock balance response + vi.mocked(getBalanceByWagmi).mockResolvedValue({ + value: BigInt(9007199254740991), + decimals: 6, + formatted: '', + symbol: 'ETH', + }); + + // Execute function + const result = await getTotalLiquidity({ + poolContract: evmContract, + tokenContract: { address: '0xToken', chainId: 1 }, + }); + + // Verify wagmi call + expect(getBalanceByWagmi).toHaveBeenCalled(); + expect(result).toBe('9007199254740991'); + }); + + it('should catch error if callViewMethod rejected', async () => { + // Setup aelf chain environment + const elfContract = { + contractType: 'ELF', + callViewMethod: vi.fn().mockRejectedValue('Failed to get token pool info'), + } as unknown as ContractBasic; + + // Execute function + await expect( + getTotalLiquidity({ + poolContract: elfContract, + symbol: 'ELF', + }), + ).rejects.toThrow('Failed to get token pool info'); + + // Verify view method call + expect(elfContract.callViewMethod).toHaveBeenCalledWith('GetTokenPoolInfo', { tokenSymbol: 'ELF' }); + }); + + it('should catch error if callViewMethod return error', async () => { + // Setup aelf chain environment + const elfContract = { + contractType: 'ELF', + callViewMethod: vi.fn().mockResolvedValue({ error: 'Failed to get token pool info' }), + } as unknown as ContractBasic; + + // Execute function + await expect( + getTotalLiquidity({ + poolContract: elfContract, + symbol: 'ELF', + }), + ).rejects.toThrow('Failed to get token pool info'); + + // Verify view method call + expect(elfContract.callViewMethod).toHaveBeenCalledWith('GetTokenPoolInfo', { tokenSymbol: 'ELF' }); + }); + }); + + describe('getMyLiquidity', () => { + it('should handle aelf chain user liquidity', async () => { + // Setup aelf chain environment + const elfContract = { + contractType: 'ELF', + callViewMethod: vi.fn().mockResolvedValue({ value: { Liquidity: '1000' } }), + } as unknown as ContractBasic; + + // Execute function + const result = await getMyLiquidity({ + poolContract: elfContract, + account: mockAccount, + tokenInfo: mockTokenInfo, + }); + + // Verify view method call + expect(elfContract.callViewMethod).toHaveBeenCalledWith('GetLiquidity', { + tokenSymbol: 'ELF', + provider: mockAccount, + }); + expect(result).toHaveProperty('Liquidity', '1000'); + }); + + it('should throw error if callViewMethod return errro', async () => { + // Setup aelf chain environment + const elfContract = { + contractType: 'ELF', + callViewMethod: vi.fn().mockResolvedValue({ error: 'Failed to get liquidity' }), + } as unknown as ContractBasic; + + // Execute function + await expect( + getMyLiquidity({ + poolContract: elfContract, + account: mockAccount, + tokenInfo: mockTokenInfo, + }), + ).rejects.toThrow('Failed to get liquidity'); + + // Verify view method call + expect(elfContract.callViewMethod).toHaveBeenCalledWith('GetLiquidity', { + tokenSymbol: 'ELF', + provider: mockAccount, + }); + }); + + it('should handle EVM chain user liquidity', async () => { + // Setup EVM environment + const evmContract = { + contractType: 'EVM', + } as unknown as ContractBasic; + + // Mock contract response + vi.mocked(readContractByWagmi).mockResolvedValue(200); + + // Execute function + const result = await getMyLiquidity({ + poolContract: evmContract, + account: mockAccount, + tokenInfo: mockTokenInfo, + }); + + // Verify contract call + expect(readContractByWagmi).toHaveBeenCalledWith({ + abi: POOLS_ABI, + functionName: 'getUserLiquidity', + address: undefined, + chainId: undefined, + args: [mockAccount, '0xToken'], + }); + expect(result).toBe('200'); + }); + }); +}); diff --git a/src/utils/__tests__/portkey.test.ts b/src/utils/__tests__/portkey.test.ts new file mode 100644 index 00000000..7f68d065 --- /dev/null +++ b/src/utils/__tests__/portkey.test.ts @@ -0,0 +1,116 @@ +import { afterEach, describe, it, expect, vi } from 'vitest'; +import { compareVersions, isPortkey, isPortkeyConnectEagerly, isPortkeyConnector, isSelectPortkey } from '../portkey'; + +// Mocking `window` object for testing +declare const window: Window & typeof globalThis; + +describe('compareVersions', () => { + it('should return -1 when v1 is less than v2', () => { + expect(compareVersions('1.2.3', '1.2.4')).toBe(-1); + expect(compareVersions('1.2.3', '1.3.0')).toBe(-1); + expect(compareVersions('1.2.3', '2.0.0')).toBe(-1); + }); + + it('should return 1 when v1 is greater than v2', () => { + expect(compareVersions('1.2.4', '1.2.3')).toBe(1); + expect(compareVersions('1.3.0', '1.2.3')).toBe(1); + expect(compareVersions('2.0.0', '1.2.3')).toBe(1); + }); + + it('should return 0 when v1 is equal to v2', () => { + expect(compareVersions('1.2.3', '1.2.3')).toBe(0); + expect(compareVersions('1.0.0', '1.0.0')).toBe(0); + }); + + it('should handle versions with different lengths', () => { + expect(compareVersions('1.2', '1.2.0')).toBe(0); + expect(compareVersions('1.2.0', '1.2')).toBe(0); + expect(compareVersions('1.2.3', '1.2')).toBe(1); + expect(compareVersions('1.2', '1.2.3')).toBe(-1); + }); +}); + +describe('isPortkey', () => { + afterEach(() => { + // Reset the mocked `window` object after each test + vi.resetModules(); + }); + + it('should return true if `window.navigator.userAgent` contains "Portkey"', () => { + // Mock a user agent containing "Portkey" + Object.defineProperty(window, 'navigator', { + value: { userAgent: 'SomeBrowser Portkey OtherInfo' }, + writable: true, + }); + + expect(isPortkey()).toBe(true); + }); + + it('should return false if `window.navigator.userAgent` does not contain "Portkey"', () => { + // Mock a user agent not containing "Portkey" + Object.defineProperty(window, 'navigator', { + value: { userAgent: 'SomeBrowser OtherInfo' }, + writable: true, + }); + + expect(isPortkey()).toBe(false); + }); + + it('should return false if `window` is not an object', () => { + // Simulate a non-browser environment by completely removing `window` + const originalWindow = global.window; + delete (global as any).window; + + expect(isPortkey()).toBeUndefined(); + + // Restore original `window` after the test + global.window = originalWindow; + }); +}); + +describe('isPortkeyConnectEagerly', () => { + it('should return false if not Portkey', () => { + vi.spyOn(window.navigator, 'userAgent', 'get').mockReturnValue('Chrome'); + + expect(isPortkeyConnectEagerly()).toBe(false); + }); + + it('should return false if version is less than 1.4.0', () => { + vi.spyOn(window.navigator, 'userAgent', 'get').mockReturnValue('PortkeyV1.3.0'); + + expect(isPortkeyConnectEagerly()).toBe(false); + }); + + it('should return true if version is greater than or equal to 1.4.0', () => { + vi.spyOn(window.navigator, 'userAgent', 'get').mockReturnValue('PortkeyV1.4.0'); + + expect(isPortkeyConnectEagerly()).toBe(true); + + vi.spyOn(window.navigator, 'userAgent', 'get').mockReturnValue('Portkey2.0.0'); + + expect(isPortkeyConnectEagerly()).toBe(false); + }); +}); + +describe('isPortkeyConnector', () => { + it('should return true if connector is PORTKEY', () => { + expect(isPortkeyConnector('PORTKEY')).toBe(true); + }); + + it('should return false if connector is not PORTKEY', () => { + expect(isPortkeyConnector('METAMASK')).toBe(false); + expect(isPortkeyConnector(undefined)).toBe(false); + }); +}); + +describe('isSelectPortkey', () => { + it('should return true if type includes PORTKEY', () => { + expect(isSelectPortkey('PORTKEY_CONNECTOR')).toBe(true); + expect(isSelectPortkey('PORTKEY_WALLET')).toBe(true); + }); + + it('should return false if type does not include PORTKEY', () => { + expect(isSelectPortkey('METAMASK_CONNECTOR')).toBe(false); + expect(isSelectPortkey(undefined)).toBeUndefined(); + }); +}); diff --git a/src/utils/__tests__/provider.test.ts b/src/utils/__tests__/provider.test.ts new file mode 100644 index 00000000..53d9c0b8 --- /dev/null +++ b/src/utils/__tests__/provider.test.ts @@ -0,0 +1,156 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { getProvider, isUserDenied, getDefaultProvider, getDefaultProviderByChainId } from '../provider'; +import Web3 from 'web3'; +import { getBridgeChainInfo } from '../chain'; +import { ChainId } from 'types'; + +// Mock external dependencies +vi.mock('web3', () => { + return { + default: { + providers: { + HttpProvider: vi.fn().mockImplementation((url: string) => ({ + url, + options: { keepAlive: true, withCredentials: false, timeout: 20000 }, + })), + }, + }, + }; +}); + +vi.mock('constants/ChainConstants', () => ({ + ERCChainConstants: { + constants: { + CHAIN_INFO: { + rpcUrl: 'https://default.rpc.url', + }, + }, + }, +})); + +vi.mock('constants/index', () => { + // Mock SupportedERCChain data + const mockSupportedERCChain = { + 1: { CHAIN_INFO: { rpcUrl: 'https://chain1.rpc.url' } }, + 2: { CHAIN_INFO: { rpcUrl: 'https://chain2.rpc.url' } }, + }; + + // Mock SupportedERCChain implementation + return { + SupportedERCChain: mockSupportedERCChain, + }; +}); + +vi.mock('../chain', () => ({ + getBridgeChainInfo: vi.fn(), +})); + +describe('Web3 Provider Utilities', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + describe('getProvider', () => { + it('should return correct provider for existing chainId (number)', () => { + const provider = getProvider(1); + + // Verify provider construction with correct URL + expect(provider).toHaveProperty('url', 'https://chain1.rpc.url'); + }); + + it('should return correct provider for existing chainId (string)', () => { + const provider = getProvider(2); + + // Verify provider construction with correct URL + expect(provider).toHaveProperty('url', 'https://chain2.rpc.url'); + }); + + it('should return undefined for non-existing chainId', () => { + const provider = getProvider(999); + expect(provider).toBeUndefined(); + }); + + it('should handle invalid chainId types', () => { + // Test with boolean type + const provider = getProvider(true as unknown as number); + expect(provider).toBeUndefined(); + }); + }); + + describe('isUserDenied', () => { + it('should return true for messages containing "User denied"', () => { + expect(isUserDenied('User denied transaction')).toBe(true); + expect(isUserDenied('Error: User denied message')).toBe(true); + }); + + it('should return false for messages without "User denied"', () => { + expect(isUserDenied('Transaction failed')).toBe(false); + expect(isUserDenied('')).toBe(false); + }); + + it('should handle non-string inputs gracefully', () => { + // Test with number input + expect(isUserDenied(123 as unknown as string)).toBe(false); + // Test with object input + expect(isUserDenied({ message: 'User denied' } as unknown as string)).toBe(false); + }); + }); + + describe('getDefaultProvider', () => { + it('should create provider with default ERC chain URL', () => { + const provider = getDefaultProvider(); + expect(Web3.providers.HttpProvider).toHaveBeenCalledWith( + 'https://default.rpc.url', + expect.objectContaining({ + keepAlive: true, + withCredentials: false, + timeout: 20000, + }), + ); + + // Verify provider construction with correct URL + expect(provider).toHaveProperty('url', 'https://default.rpc.url'); + }); + }); + + describe('getDefaultProviderByChainId', () => { + it('should create provider with chain-specific URL', () => { + // Mock bridge chain info response + const rpcUrl = 'https://custom.rpc.url'; + vi.mocked(getBridgeChainInfo).mockReturnValue({ + CHAIN_INFO: { rpcUrl: rpcUrl }, + }); + + const provider = getDefaultProviderByChainId(5 as ChainId); + + expect(getBridgeChainInfo).toHaveBeenCalledWith(5); + expect(Web3.providers.HttpProvider).toHaveBeenCalledWith(rpcUrl, expect.any(Object)); + + // Verify provider construction with correct URL + expect(provider).toHaveProperty('url', rpcUrl); + }); + + it('should handle missing chain info gracefully', () => { + vi.mocked(getBridgeChainInfo).mockReturnValue(undefined); + const provider = getDefaultProviderByChainId(999 as ChainId); + + // Verify provider construction with correct URL + expect(provider).toHaveProperty('url', undefined); + }); + + it('should handle invalid chainId types', () => { + // Test with string chainId + const rpcUrl = 'https://string.chain.url'; + vi.mocked(getBridgeChainInfo).mockReturnValue({ + CHAIN_INFO: { rpcUrl: rpcUrl }, + }); + const provider = getDefaultProviderByChainId('invalid' as unknown as ChainId); + + expect(getBridgeChainInfo).toHaveBeenCalledWith('invalid'); + + // Verify provider construction with correct URL + expect(provider).toHaveProperty('url', rpcUrl); + }); + }); +}); diff --git a/src/utils/__tests__/reg.test.ts b/src/utils/__tests__/reg.test.ts new file mode 100644 index 00000000..cda86ea6 --- /dev/null +++ b/src/utils/__tests__/reg.test.ts @@ -0,0 +1,120 @@ +import { describe, it, expect } from 'vitest'; +import { isSymbol, isUrl, isValidNumber, isValidPositiveNumber } from '../reg'; + +describe('Validation Utilities', () => { + /** + * Test `isUrl` + */ + describe('isUrl', () => { + it('should return false for non-string inputs', () => { + expect(isUrl(123 as any)).toBe(false); // number + expect(isUrl(null as any)).toBe(false); // null + expect(isUrl(undefined as any)).toBe(false); // undefined + expect(isUrl({} as any)).toBe(false); // object + expect(isUrl([] as any)).toBe(false); // array + expect(isUrl(true as any)).toBe(false); // boolean + }); + + it('should return false for invalid URLs', () => { + expect(isUrl('')).toBe(false); // empty string + expect(isUrl('not a url')).toBe(false); // string + expect(isUrl('http://')).toBe(false); // only protocol + expect(isUrl('://example.com')).toBe(false); // not protocol + }); + + it('should return true for valid localhost URLs', () => { + expect(isUrl('http://localhost')).toBe(true); + expect(isUrl('https://localhost:8080')).toBe(true); + expect(isUrl('http://localhost/path')).toBe(true); + expect(isUrl('http://localhost:3000/path')).toBe(true); + }); + + it('should return true for valid non-localhost URLs', () => { + expect(isUrl('http://example.com')).toBe(true); + expect(isUrl('https://example.com')).toBe(true); + expect(isUrl('http://sub.example.com')).toBe(true); + expect(isUrl('https://example.com/path')).toBe(true); + expect(isUrl('http://example.com:8080')).toBe(true); + expect(isUrl('https://example.com/path?query=param')).toBe(true); + expect(isUrl('http://example.com/path#fragment')).toBe(true); + }); + + it('should return false for invalid domain formats', () => { + expect(isUrl('http://.com')).toBe(false); // Invalid domain name + expect(isUrl('http://example.')).toBe(false); // Invalid domain name + expect(isUrl('http://example.c')).toBe(false); // The domain name is too short + }); + + it('should handle edge cases', () => { + expect(isUrl('http://localhost:')).toBe(true); // The port number is empty + expect(isUrl('http://localhost:3000')).toBe(true); // With port number + expect(isUrl('http://localhost/path/to/resource')).toBe(true); // With Path + expect(isUrl('http://localhost?query=param')).toBe(true); // With query parameters + expect(isUrl('http://localhost#fragment')).toBe(true); // With fragment + }); + }); + + /** + * Test `isSymbol` + */ + describe('isSymbol', () => { + it('should return true for valid symbols', () => { + expect(isSymbol('BTC')).toBe(true); + expect(isSymbol('123')).toBe(true); + expect(isSymbol('TestSymbol')).toBe(true); + }); + + it('should return false for invalid symbols', () => { + expect(isSymbol(undefined)).toBe(false); + expect(isSymbol('')).toBe(false); + expect(isSymbol('BTC/USD')).toBe(false); // Contains special characters + expect(isSymbol('BTC@')).toBe(false); + expect(isSymbol('#SYMBOL')).toBe(false); + }); + }); + + /** + * Test `isValidNumber` + */ + describe('isValidNumber', () => { + it('should return true for valid numbers', () => { + expect(isValidNumber('123')).toBe(true); + expect(isValidNumber('123.45')).toBe(true); + expect(isValidNumber('-123')).toBe(true); + expect(isValidNumber('-123.45')).toBe(true); + expect(isValidNumber('-')).toBe(true); // Special case + }); + + it('should return false for invalid numbers', () => { + expect(isValidNumber('')).toBe(false); + expect(isValidNumber('.')).toBe(false); + expect(isValidNumber('abc')).toBe(false); + expect(isValidNumber('12abc34')).toBe(false); + expect(isValidNumber('.45')).toBe(false); + expect(isValidNumber('12.34.56')).toBe(false); + expect(isValidNumber('--123')).toBe(false); + expect(isValidNumber(undefined)).toBe(false); + }); + }); + + /** + * Test `isValidPositiveNumber` + */ + describe('isValidPositiveNumber', () => { + it('should return true for valid positive numbers', () => { + expect(isValidPositiveNumber('123')).toBe(true); + expect(isValidPositiveNumber('0')).toBe(true); + expect(isValidPositiveNumber('123.45')).toBe(true); + }); + + it('should return false for invalid or negative numbers', () => { + expect(isValidPositiveNumber('')).toBe(false); + expect(isValidPositiveNumber('abc')).toBe(false); + expect(isValidPositiveNumber('.45')).toBe(false); + expect(isValidPositiveNumber('-123')).toBe(false); + expect(isValidPositiveNumber('-123.45')).toBe(false); + expect(isValidPositiveNumber('--123')).toBe(false); + expect(isValidPositiveNumber(undefined)).toBe(false); + }); + }); +}); diff --git a/src/utils/__tests__/telegram/telegram.test.ts b/src/utils/__tests__/telegram/telegram.test.ts new file mode 100644 index 00000000..b1ebc62b --- /dev/null +++ b/src/utils/__tests__/telegram/telegram.test.ts @@ -0,0 +1,216 @@ +import { afterEach, describe, it, expect } from 'vitest'; +import { TelegramPlatform } from '../../telegram/telegram'; + +// Mocking the browser `window` object +declare const window: Window & typeof globalThis & { Telegram?: any }; + +describe('TelegramPlatform', () => { + // Clean up `window.Telegram` after each test + afterEach(() => { + delete window.Telegram; + }); + + /** + * Test `getTelegram` method + */ + describe('getTelegram', () => { + it('should return `undefined` if `window.Telegram` is not defined', () => { + const result = TelegramPlatform.getTelegram(); + + expect(result).toBeUndefined(); + }); + + it('should return `window.Telegram` if it is defined', () => { + const mockTelegram = { WebApp: { platform: 'web' } }; + window.Telegram = mockTelegram; + + const result = TelegramPlatform.getTelegram(); + + expect(result).toBe(mockTelegram); + }); + }); + + /** + * Test `getWebApp` method + */ + describe('getWebApp', () => { + it('should return `undefined` if `window.Telegram` is not defined', () => { + const result = TelegramPlatform.getWebApp(); + + expect(result).toBeUndefined(); + }); + + it('should return the `Telegram.WebApp` object if `window.Telegram` is defined', () => { + const mockWebApp = { platform: 'web' }; + const mockTelegram = { WebApp: mockWebApp }; + window.Telegram = mockTelegram; + + const result = TelegramPlatform.getWebApp(); + + expect(result).toBe(mockWebApp); + }); + + it('should return `undefined` if `Telegram.WebApp` is not defined', () => { + window.Telegram = {}; + + const result = TelegramPlatform.getWebApp(); + + expect(result).toBeUndefined(); + }); + }); + + /** + * Test `isTelegramPlatform` method + */ + describe('isTelegramPlatform', () => { + it('should return `false` if `window.Telegram` is not defined', () => { + const result = TelegramPlatform.isTelegramPlatform(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp` is not defined', () => { + window.Telegram = {}; + + const result = TelegramPlatform.isTelegramPlatform(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is "unknown"', () => { + window.Telegram = { WebApp: { platform: 'unknown' } }; + + const result = TelegramPlatform.isTelegramPlatform(); + + expect(result).toBe(false); + }); + + it('should return `true` if `Telegram.WebApp.platform` is not "unknown"', () => { + window.Telegram = { WebApp: { platform: 'web' } }; + + const result = TelegramPlatform.isTelegramPlatform(); + + expect(result).toBe(true); + }); + }); + + describe('isTelegramPlatformWeb', () => { + it('should return `false` if `window.Telegram` is not defined', () => { + const result = TelegramPlatform.isTelegramPlatformWeb(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp` is not defined', () => { + window.Telegram = {}; + + const result = TelegramPlatform.isTelegramPlatformWeb(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is "unknown"', () => { + window.Telegram = { WebApp: { platform: 'unknown' } }; + + const result = TelegramPlatform.isTelegramPlatformWeb(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is not "android"', () => { + window.Telegram = { WebApp: { platform: 'android' } }; + + const result = TelegramPlatform.isTelegramPlatformWeb(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is "web"', () => { + window.Telegram = { WebApp: { platform: 'web' } }; + + const result = TelegramPlatform.isTelegramPlatformWeb(); + + expect(result).toBe(true); + }); + }); + + describe('isTelegramPlatformAndNotWeb', () => { + it('should return `false` if `window.Telegram` is not defined', () => { + const result = TelegramPlatform.isTelegramPlatformAndNotWeb(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp` is not defined', () => { + window.Telegram = {}; + + const result = TelegramPlatform.isTelegramPlatformAndNotWeb(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is "unknown"', () => { + window.Telegram = { WebApp: { platform: 'unknown' } }; + + const result = TelegramPlatform.isTelegramPlatformAndNotWeb(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is "web"', () => { + window.Telegram = { WebApp: { platform: 'web' } }; + + const result = TelegramPlatform.isTelegramPlatformAndNotWeb(); + + expect(result).toBe(false); + }); + + it('should return `true` if `Telegram.WebApp.platform` is "android"', () => { + window.Telegram = { WebApp: { platform: 'android' } }; + + const result = TelegramPlatform.isTelegramPlatformAndNotWeb(); + + expect(result).toBe(true); + }); + }); + + describe('isTelegramPlatformDesktop', () => { + it('should return `false` if `window.Telegram` is not defined', () => { + const result = TelegramPlatform.isTelegramPlatformDesktop(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp` is not defined', () => { + window.Telegram = {}; + + const result = TelegramPlatform.isTelegramPlatformDesktop(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is "unknown"', () => { + window.Telegram = { WebApp: { platform: 'unknown' } }; + + const result = TelegramPlatform.isTelegramPlatformDesktop(); + + expect(result).toBe(false); + }); + + it('should return `false` if `Telegram.WebApp.platform` is "web"', () => { + window.Telegram = { WebApp: { platform: 'web' } }; + + const result = TelegramPlatform.isTelegramPlatformDesktop(); + + expect(result).toBe(false); + }); + + it('should return `true` if `Telegram.WebApp.platform` is "desktop"', () => { + window.Telegram = { WebApp: { platform: 'desktop' } }; + + const result = TelegramPlatform.isTelegramPlatformDesktop(); + + expect(result).toBe(true); + }); + }); +}); diff --git a/src/utils/__tests__/themes.test.ts b/src/utils/__tests__/themes.test.ts new file mode 100644 index 00000000..341c0834 --- /dev/null +++ b/src/utils/__tests__/themes.test.ts @@ -0,0 +1,101 @@ +import { getThemes, setThemes } from 'utils/themes'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// Attach the mock to the global document object +vi.mock('react-dom'); + +describe('getThemes', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should return the dark theme from HTML attribute', () => { + // Mock document object + document.getElementsByTagName = vi.fn().mockImplementation(() => { + return [ + { + setAttribute: vi.fn(), + getAttribute: vi.fn().mockReturnValue('dark'), + }, + ]; + }); + + const html = document.getElementsByTagName('html')[0]; + html.setAttribute('data-theme', 'dark'); + + // Execution Method + const result = getThemes(); + + // Assertions + expect(result).toBe('dark'); + }); + + it('should return the light theme from HTML attribute', () => { + // Mock document object + document.getElementsByTagName = vi.fn().mockImplementation(() => { + return [ + { + setAttribute: vi.fn(), + getAttribute: vi.fn().mockReturnValue('light'), + }, + ]; + }); + + const html = document.getElementsByTagName('html')[0]; + html.setAttribute('data-theme', 'light'); + + // Execution Method + const result = getThemes(); + + // Assertions + expect(result).toBe('light'); + }); +}); + +describe('setThemes', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should set the theme on HTML attribute if different from current theme', () => { + // Mock document object + document.getElementsByTagName = vi.fn().mockImplementation(() => { + return [ + { + setAttribute: vi.fn(), + getAttribute: vi.fn().mockReturnValue('light'), + }, + ]; + }); + + // Execution Method + setThemes('dark'); + + // Assertions + const html = document.getElementsByTagName('html')[0]; + expect(html.getAttribute('data-theme')).toBe('light'); + }); + + it('should not change the theme if it matches the current theme', () => { + // Mock document object + document.getElementsByTagName = vi.fn().mockImplementation(() => { + return [ + { + setAttribute: vi.fn(), + getAttribute: vi.fn().mockReturnValue('dark'), + }, + ]; + }); + + const html = document.getElementsByTagName('html')[0]; + html.setAttribute('data-theme', 'dark'); + + // Execution Method + setThemes('dark'); + + // Assertions + expect(html.getAttribute('data-theme')).toBe('dark'); + }); +}); diff --git a/src/utils/__tests__/time.test.ts b/src/utils/__tests__/time.test.ts new file mode 100644 index 00000000..cc17d38f --- /dev/null +++ b/src/utils/__tests__/time.test.ts @@ -0,0 +1,118 @@ +import { formatTime, getCrossChainTime, getFullYear, getMillisecond } from 'utils/time'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { ChainId } from 'types'; +import { isIncludesChainId } from 'utils'; + +// Mock Moment.js +vi.mock('moment', () => ({ + default: vi.fn((time: string | number | Date) => ({ + format: (format: string) => `Formatted: ${time} as ${format}`, + })), +})); + +vi.mock('utils', () => ({ + isIncludesChainId: vi.fn(), +})); + +const mockMainChainId = 'AELF' as ChainId; +const mockEthId = 'ETH' as ChainId; +const mockBaseId = 'BASE' as ChainId; + +describe('getMillisecond', () => { + it('should return milliseconds when the time is a number less than or equal to 10 digits', () => { + const result = getMillisecond(1234567890); // 10 digits + expect(result).toBe(1234567890000); + }); + + it('should return milliseconds when time is a string less than or equal to 10 digits', () => { + const result = getMillisecond('1234567890'); // 10 digits + expect(result).toBe(1234567890000); + }); + + it('should return milliseconds when the time is a string with more than 10 digits', () => { + const result = getMillisecond('1234567890123456'); // More than 10 digits + expect(result).toBe(1234567890123456); + }); + + it('should return the input number when it is greater than 10 digits', () => { + const result = getMillisecond(1234567890123456); // More than 10 digits + expect(result).toBe(1234567890123456); + }); + + it('should return NaN if the time is not a valid number or string', () => { + const result = getMillisecond('invalid'); + expect(result).toBeNaN(); + }); + + it('should return NaN if the time is undefined', () => { + const result = getMillisecond(undefined); + expect(result).toBeNaN(); + }); +}); + +describe('formatTime', () => { + it('should format time correctly for timestamp strings', () => { + const result = formatTime('1633072800'); + + expect(result).toBe('Formatted: 1633072800000 as YYYY MMM DD, HH:mm:ss'); + }); + + it('should format time correctly for Date objects', () => { + const date = new Date(); + + const result = formatTime(date); + + expect(result).toBe(`Formatted: ${date} as YYYY MMM DD, HH:mm:ss`); + }); + + it('should format time correctly for number timestamps', () => { + const result = formatTime(1633072800000); + + expect(result).toBe('Formatted: 1633072800000 as YYYY MMM DD, HH:mm:ss'); + }); + + it('should return null if time is invalid (undefined)', () => { + const result = formatTime(undefined as any); + expect(result).toBe('Formatted: undefined as YYYY MMM DD, HH:mm:ss'); + }); + + it('should handle edge case with string containing "T" or "-" correctly', () => { + const result = formatTime('2025-01-01T12:00:00'); + expect(result).toBe('Formatted: 2025-01-01T12:00:00 as YYYY MMM DD, HH:mm:ss'); + }); +}); + +describe('getCrossChainTime', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should return cross-chain time if matching chain IDs are found', () => { + // Mock isIncludesChainId return true + vi.mocked(isIncludesChainId).mockReturnValue(true); + + const result = getCrossChainTime(mockMainChainId, mockEthId); + + expect(result).toBe('8'); + }); + + it('should return default time if no matching chain IDs are found', () => { + // Mock isIncludesChainId return false + vi.mocked(isIncludesChainId).mockReturnValue(false); + + const result = getCrossChainTime(mockMainChainId, mockBaseId); + + expect(result).toBe('10'); + }); +}); + +describe('getFullYear', () => { + it('should return the current year', () => { + const currentYear = new Date().getFullYear(); + + const result = getFullYear(); + + expect(result).toBe(currentYear); + }); +}); diff --git a/src/utils/__tests__/token.test.ts b/src/utils/__tests__/token.test.ts new file mode 100644 index 00000000..bf43676f --- /dev/null +++ b/src/utils/__tests__/token.test.ts @@ -0,0 +1,33 @@ +import { NATIVE_TOKEN_LIST } from 'constants/index'; + +import { formatSymbol, formatSymbolAndNativeToken } from 'utils/token'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; + +describe('formatSymbol', () => { + it('should format symbol using SYMBOL_FORMAT_MAP', () => { + const result = formatSymbol('SGR-1'); + expect(result).toBe('SGR'); + }); + + it('should return the original symbol if not in SYMBOL_FORMAT_MAP', () => { + const result = formatSymbol('ETH'); + expect(result).toBe('ETH'); + }); +}); + +describe('formatSymbolAndNativeToken', () => { + it('should format symbol using SYMBOL_FORMAT_MAP', () => { + const result = formatSymbolAndNativeToken('SGR-1'); + expect(result).toBe('SGR'); + }); + + it('should remove "W" prefix for native tokens', () => { + const result = formatSymbolAndNativeToken('WETH'); + expect(result).toBe('ETH'); + }); + + it('should return the original symbol if not in SYMBOL_FORMAT_MAP or NATIVE_TOKEN_LIST', () => { + const result = formatSymbolAndNativeToken('BTC'); + expect(result).toBe('BTC'); + }); +}); diff --git a/src/utils/__tests__/tonContractCall.test.ts b/src/utils/__tests__/tonContractCall.test.ts new file mode 100644 index 00000000..105bb5f9 --- /dev/null +++ b/src/utils/__tests__/tonContractCall.test.ts @@ -0,0 +1,184 @@ +import { describe, it, expect, vi, beforeEach, Mock } from 'vitest'; +import { CallTonContract, TonContractCallData } from 'utils/tonContractCall'; +import { ZERO } from 'constants/misc'; +import { getTONJettonMinter, packCreateReceiptBody, TON_WEB } from '../ton'; + +// Mock external dependencies +vi.mock('tonweb', async () => { + const tonMock: any = vi.fn().mockImplementation(() => ({ + utils: { + Address: vi.fn(), + }, + })); + + tonMock.utils = { + Address: vi.fn(), + }; + + return { + default: tonMock, + }; +}); + +vi.mock('@ton/core', () => ({ + Address: { + parse: vi.fn(), + isFriendly: vi.fn(), + isAddress: vi.fn(), + }, + beginCell: vi.fn(() => ({ + storeUint: vi.fn().mockReturnThis(), + storeCoins: vi.fn().mockReturnThis(), + storeBuffer: vi.fn().mockReturnThis(), + storeAddress: vi.fn().mockReturnThis(), + storeMaybeRef: vi.fn().mockReturnThis(), + endCell: vi.fn(() => ({ + toBoc: vi.fn(() => Buffer.from('mock_boc')), + hash: vi.fn(() => Buffer.from('mock_hash')), + })), + })), + toNano: vi.fn((amount: string) => Number(amount) * 1e9), +})); + +vi.mock('../ton', () => { + return { + getTONJettonMinter: vi.fn(), + packCreateReceiptBody: vi.fn(), + TON_WEB: { + provider: { + call: vi.fn(), + }, + }, + }; +}); + +vi.mock('../chain', () => { + return { + base58ToChainId: vi.fn(), + }; +}); + +vi.mock('aelf-sdk', () => { + return { + default: { + utils: { + base58: { + decode: vi.fn(), + }, + }, + }, + }; +}); + +const mockAccount = 'EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'; +const mockContractAddress = 'EQAhE0sJbtgHJqENqgHSpkPPNmR_9lYMTw_1QJkCCtS-H6e5'; + +describe('TonContractCallData', () => { + describe('createReceipt', () => { + beforeEach(() => { + // Mock dependencies + (getTONJettonMinter as Mock).mockReturnValue({ + getJettonWalletAddress: vi.fn().mockReturnValue({ + isUserFriendly: true, + isBounceable: true, + isUrlSafe: true, + toString: vi.fn(), + }), + }); + (packCreateReceiptBody as Mock).mockReturnValue({ + toBoc: vi.fn(() => Buffer.from('mock_boc')), + hash: vi.fn(() => Buffer.from('mock_hash')), + }); + }); + + it('should create receipt transaction with valid parameters', async () => { + const transaction = await TonContractCallData.createReceipt(mockContractAddress, mockAccount, [ + 'token_address', + '100', + 'AELF', + 'target_address', + ]); + + // Verify transaction structure + expect(transaction.messages[0].address).toBeUndefined(); + expect(transaction.messages[0].amount).toBe((0.3 * 1e9).toString()); + expect(transaction.validUntil).toBeDefined(); + }); + + it('should handle zero amount in transaction', async () => { + const transaction = await TonContractCallData.createReceipt(mockContractAddress, mockAccount, [ + 'token_address', + '0', + 'chainId_AELF', + 'target_address', + ]); + + // Verify transaction structure + expect(transaction.messages[0].payload).toBeDefined(); + }); + }); +}); + +describe('CallTonContract', () => { + describe('getReceiptDailyLimit', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should get receipt daily limit correctly', async () => { + // Mock provider call response + (TON_WEB.provider.call as any).mockResolvedValue({ + stack: [ + [null, '100'], + [null, '1633072800'], + [null, '500'], + ], + }); + + const result = await CallTonContract.getReceiptDailyLimit(mockContractAddress, ['', 'AELF']); + + expect(result.tokenAmount).toEqual(ZERO.plus(100)); + expect(result.dailyLimit).toEqual(ZERO.plus(500)); + }); + + it('should handle empty response from provider', async () => { + (TON_WEB.provider.call as any).mockResolvedValue({ stack: [] }); + + await expect(CallTonContract.getReceiptDailyLimit(mockContractAddress, ['', ''])).rejects.toThrow(); + }); + }); + + describe('getCurrentReceiptTokenBucketState', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should get current receipt token bucket state correctly', async () => { + (TON_WEB.provider.call as any).mockResolvedValue({ + stack: [ + [null, '100'], + [null, '1633072800'], + [null, '500'], + [null, '600'], + [null, '700'], + ], + }); + + const result = await CallTonContract.getCurrentReceiptTokenBucketState(mockContractAddress, ['', 'AELF']); + + expect(result.currentTokenAmount).toEqual(ZERO.plus(100)); + expect(result.refreshTime).toEqual(ZERO.plus(1633072800)); + expect(result.tokenCapacity).toEqual(ZERO.plus(500)); + expect(result.isEnable).toEqual(false); + expect(result.rate).toEqual(ZERO.plus(700)); + }); + + it('should handle empty response from provider', async () => { + (TON_WEB.provider.call as any).mockResolvedValue({ stack: [] }); + + await expect(CallTonContract.getReceiptDailyLimit(mockContractAddress, ['', ''])).rejects.toThrow(); + }); + }); +}); diff --git a/src/utils/__tests__/ton_mainnet.test.ts b/src/utils/__tests__/ton_mainnet.test.ts new file mode 100644 index 00000000..db890dbe --- /dev/null +++ b/src/utils/__tests__/ton_mainnet.test.ts @@ -0,0 +1,105 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import TonWeb from 'tonweb'; +import { getTONJettonMinter } from 'utils/ton'; + +// Mock external dependencies +vi.mock('tonweb', async () => { + const tonMock: any = vi.fn().mockImplementation(() => ({ + HttpProvider: vi.fn(), + provider: { + call: vi.fn(), + }, + token: { + jetton: { + JettonMinter: vi.fn(), + JettonWallet: vi.fn(), + }, + }, + boc: { + Cell: { + oneFromBoc: vi.fn().mockReturnValue({ + hash: vi.fn(), + }), + }, + }, + utils: { + base64ToBytes: vi.fn(), + bytesToBase64: vi.fn((bytes: number[]) => Buffer.from(bytes).toString('base64')), + Address: vi.fn(), + }, + })); + + tonMock.HttpProvider = vi.fn(); + + tonMock.provider = { + call: vi.fn(), + }; + + tonMock.token = { + jetton: { + JettonMinter: vi.fn(), + JettonWallet: vi.fn(), + }, + }; + + tonMock.boc = { + Cell: { + oneFromBoc: vi.fn().mockReturnValue({ + hash: vi.fn(), + }), + }, + }; + + tonMock.utils = { + base64ToBytes: vi.fn(), + bytesToBase64: vi.fn((bytes: number[]) => Buffer.from(bytes).toString('base64')), + Address: vi.fn(), + }; + + return { + default: tonMock, + }; +}); + +vi.mock('constants/index', async (importOriginal) => { + const originalModule: any = await importOriginal(); + + return { + ...originalModule, + IS_MAINNET: true, + }; +}); + +describe('getTONJettonMinter', () => { + const mockTokenContractAddress = 'EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA'; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should return token balance correctly with mainnet', async () => { + // Mock TonWeb.token.jetton.JettonMinter return value + const mockJettonMinter = { + getJettonWalletAddress: vi.fn().mockReturnValue({ + isUserFriendly: true, + isBounceable: true, + isUrlSafe: true, + toString: vi.fn(), + }), + }; + (TonWeb.token.jetton.JettonMinter as any).mockImplementation(() => mockJettonMinter); + + const result = getTONJettonMinter(mockTokenContractAddress); + + expect(result).toBeDefined(); + }); + + it('should handle wallet data fetch error with mainnet', async () => { + // Mock TonWeb.token.jetton.JettonMinter throw error + const error = 'Failed to get JettonMinter'; + (TonWeb.token.jetton.JettonMinter as any).mockRejectedValue(error); + + await expect(getTONJettonMinter(mockTokenContractAddress)).rejects.toThrow(error); + }); +}); diff --git a/src/utils/__tests__/ton_testnet.test.ts b/src/utils/__tests__/ton_testnet.test.ts new file mode 100644 index 00000000..9df4807c --- /dev/null +++ b/src/utils/__tests__/ton_testnet.test.ts @@ -0,0 +1,264 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import TonWeb from 'tonweb'; +import { Address } from '@ton/core'; +import { SendTransactionResponse } from '@tonconnect/ui-react'; +import { + getTonChainBalance, + packCreateReceiptBody, + getTransactionResponseHash, + isTonAddress, + getTONJettonMinter, +} from 'utils/ton'; + +// Mock external dependencies +vi.mock('tonweb', async () => { + const tonMock: any = vi.fn().mockImplementation(() => ({ + HttpProvider: vi.fn(), + provider: { + call: vi.fn(), + }, + token: { + jetton: { + JettonMinter: vi.fn(), + JettonWallet: vi.fn(), + }, + }, + boc: { + Cell: { + oneFromBoc: vi.fn().mockReturnValue({ + hash: vi.fn(), + }), + }, + }, + utils: { + base64ToBytes: vi.fn(), + bytesToBase64: vi.fn((bytes: number[]) => Buffer.from(bytes).toString('base64')), + Address: vi.fn(), + }, + })); + + tonMock.HttpProvider = vi.fn(); + + tonMock.provider = { + call: vi.fn(), + }; + + tonMock.token = { + jetton: { + JettonMinter: vi.fn(), + JettonWallet: vi.fn(), + }, + }; + + tonMock.boc = { + Cell: { + oneFromBoc: vi.fn().mockReturnValue({ + hash: vi.fn(), + }), + }, + }; + + tonMock.utils = { + base64ToBytes: vi.fn(), + bytesToBase64: vi.fn((bytes: number[]) => Buffer.from(bytes).toString('base64')), + Address: vi.fn(), + }; + + return { + default: tonMock, + }; +}); + +vi.mock('@ton/core', () => ({ + Address: { + parse: vi.fn(), + isFriendly: vi.fn(), + isAddress: vi.fn(), + }, + beginCell: vi.fn(() => ({ + storeUint: vi.fn().mockReturnThis(), + storeBuffer: vi.fn().mockReturnThis(), + storeAddress: vi.fn().mockReturnThis(), + endCell: vi.fn(() => ({ + toBoc: vi.fn(() => Buffer.from('mock_boc')), + hash: vi.fn(() => Buffer.from('mock_hash')), + })), + })), +})); + +vi.mock('../chain', () => ({ + base58ToChainId: vi.fn((chainId: string) => chainId), +})); + +vi.mock('constants/index', async (importOriginal) => { + const originalModule: any = await importOriginal(); + + return { + ...originalModule, + IS_MAINNET: false, + }; +}); + +const mockAccount = 'EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'; +const mockContractAddress = 'EQAhE0sJbtgHJqENqgHSpkPPNmR_9lYMTw_1QJkCCtS-H6e5'; +const mockTokenContractAddress = 'EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA'; + +describe('getTONJettonMinter', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should return token balance correctly with testnet', async () => { + // Mock TonWeb.token.jetton.JettonMinter return value + const mockJettonMinter = { + getJettonWalletAddress: vi.fn().mockReturnValue({ + isUserFriendly: true, + isBounceable: true, + isUrlSafe: true, + toString: vi.fn(), + }), + }; + (TonWeb.token.jetton.JettonMinter as any).mockImplementation(() => mockJettonMinter); + + const result = getTONJettonMinter(mockTokenContractAddress); + + expect(result).toBeDefined(); + }); + + it('should handle wallet data fetch error with testnet', async () => { + // Mock TonWeb.token.jetton.JettonMinter throw error + const error = 'Failed to get JettonMinter'; + (TonWeb.token.jetton.JettonMinter as any).mockRejectedValue(error); + + await expect(getTONJettonMinter(mockTokenContractAddress)).rejects.toThrow(error); + }); +}); + +describe('getTonChainBalance', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + + // Mock TonWeb.token.jetton.JettonMinter return value + const mockJettonMinter = { + getJettonWalletAddress: vi.fn().mockReturnValue({ + isUserFriendly: true, + isBounceable: true, + isUrlSafe: true, + toString: vi.fn(), + }), + }; + (TonWeb.token.jetton.JettonMinter as any).mockImplementation(() => mockJettonMinter); + }); + + it('should return token balance correctly', async () => { + // Mock TonWeb.token.jetton.JettonWallet return balance + const mockJettonWallet = { + getData: vi.fn().mockReturnValue({ + balance: 1500, + }), + }; + (TonWeb.token.jetton.JettonWallet as any).mockImplementation(() => mockJettonWallet); + + const balance = await getTonChainBalance(mockContractAddress, mockAccount); + expect(balance).toBe('1500'); + }); + + it('should handle wallet data fetch error', async () => { + // Mock TonWeb.token.jetton.JettonWallet throw error + const error = 'Failed to get balance'; + const mockJettonWallet = { + getData: vi.fn().mockRejectedValue(error), + }; + (TonWeb.token.jetton.JettonWallet as any).mockImplementation(() => mockJettonWallet); + + await expect(getTonChainBalance(mockContractAddress, mockAccount)).rejects.toThrow(error); + }); +}); + +describe('packCreateReceiptBody', () => { + it('should pack receipt body correctly', () => { + const payload = packCreateReceiptBody( + 1, + Buffer.from('address'), + Address.parse('EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'), + ); + expect(payload).toBeDefined(); + }); +}); + +describe('getTransactionResponseHash', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should generate valid transaction hash', async () => { + const mockBoc = 'mock_boc'; + + const uint8Array = new Uint8Array([0, 1, 2, 3, 15, 16, 255]); + const expectedHex = '000102030f10ff'; + + (TonWeb.boc.Cell.oneFromBoc as any).mockImplementation(() => { + return { + hash: () => { + return uint8Array; + }, + }; + }); + + (TonWeb.utils.bytesToBase64 as any).mockResolvedValue(expectedHex); + + const hash = await getTransactionResponseHash({ boc: mockBoc } as SendTransactionResponse); + + expect(hash).toBe(expectedHex); + }); + + it('should catch the error', async () => { + const mockBoc = 'mock_boc'; + const error = 'Failed to get hash'; + (TonWeb.boc.Cell.oneFromBoc as any).mockImplementation(() => { + return { + hash: vi.fn().mockRejectedValue(error), + }; + }); + + await expect(getTransactionResponseHash({ boc: mockBoc } as SendTransactionResponse)).rejects.toThrow(error); + }); +}); + +describe('Address Validation', () => { + it('should validate TON friendly addresses', () => { + // Mock Address.isFriendly return true + (Address.isFriendly as any).mockReturnValue(true); + + const result = isTonAddress(mockAccount); + + expect(result).toBe(true); + }); + + it('should return TON address', () => { + // Mock Address.isFriendly return true + (Address.isFriendly as any).mockReturnValue(true); + + // Mock Address.isAddress return true + (Address.isAddress as any).mockReturnValue(true); + + const result = isTonAddress(mockAccount); + + expect(result).toBe(true); + }); + + it('should reject invalid addresses', () => { + // Mock Address.isFriendly return false + (Address.isFriendly as any).mockReturnValue(false); + + // Mock Address.isAddress return false + (Address.isAddress as any).mockReturnValue(false); + + const result = isTonAddress('invalid_address'); + + expect(result).toBe(false); + }); +}); diff --git a/src/utils/__tests__/transfers.test.ts b/src/utils/__tests__/transfers.test.ts new file mode 100644 index 00000000..efb49ea7 --- /dev/null +++ b/src/utils/__tests__/transfers.test.ts @@ -0,0 +1,30 @@ +import { parseCrossChainTransfers } from 'utils/transfers'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +vi.mock('utils/chain', () => ({ + getChainIdByAPI: vi.fn((chainId: string) => chainId), +})); + +describe('parseCrossChainTransfers', () => { + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + it('should parse cross-chain transfers correctly', () => { + const items = [ + { toChainId: 'AELF', fromChainId: 'ETH', txId: 'tx1' }, + { toChainId: 'BSC', fromChainId: 'AELF', txId: 'tx2' }, + ]; + const result = parseCrossChainTransfers({ items }); + expect(result).toEqual([ + { toChainId: 'AELF', fromChainId: 'ETH', txId: 'tx1' }, + { toChainId: 'BSC', fromChainId: 'AELF', txId: 'tx2' }, + ]); + }); + + it('should return undefined if items are undefined', () => { + const result = parseCrossChainTransfers({ items: undefined }); + expect(result).toBeUndefined(); + }); +}); diff --git a/src/utils/__tests__/wagmi.test.ts b/src/utils/__tests__/wagmi.test.ts new file mode 100644 index 00000000..f7aadd74 --- /dev/null +++ b/src/utils/__tests__/wagmi.test.ts @@ -0,0 +1,283 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { + getTransactionReceiptAutoRetry, + getBalanceByWagmi, + readContractByWagmi, + writeContractByWagmi, + getGasPriceByWagmi, + waitForTransactionReceiptByWagmi, + switchChainByWagmi, +} from 'utils/wagmi'; +import { + getTransactionReceipt, + getBalance, + readContract, + writeContract, + getAccount, + getGasPrice, + waitForTransactionReceipt, + switchChain, +} from '@wagmi/core'; +import { EVMProviderConfig } from 'constants/evm'; +import { METAMASK_WALLET_ID } from 'types'; + +// Mock external dependencies +vi.mock('constants/evm', () => ({ + EVMProviderConfig: {}, +})); + +vi.mock('@wagmi/core', () => ({ + getTransactionReceipt: vi.fn(), + getBalance: vi.fn(), + readContract: vi.fn(), + writeContract: vi.fn(), + getGasPrice: vi.fn(), + waitForTransactionReceipt: vi.fn(), + getAccount: vi.fn(), + switchChain: vi.fn(), +})); + +vi.mock('utils', () => ({ + sleep: vi.fn(), +})); + +vi.mock('utils/error', () => ({ + handleErrorMessage: vi.fn((error: Error) => error.message), +})); + +type TEVMStatus = 'success' | 'reverted'; + +describe('EVM Utils', () => { + const sepoliaChainId = 11155111; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + }); + + describe('getTransactionReceiptAutoRetry', () => { + const mockParams = { + hash: '0x2c37286396fe853d91cb7ebb21a8e4fb0e10ccc057cc153e7fda88ebba7a5651' as `0x${string}`, + chainId: sepoliaChainId, + }; + const mockReceipt = { + status: 'success' as TEVMStatus, + blockNumber: 7517556n, + blockHash: '0xd8c6d51e04cfa34f2a5cf41b8a38925af273d33c97298c29545a8d46a8262bd3' as `0x${string}`, + contractAddress: null, + cumulativeGasUsed: 8720366n, + effectiveGasPrice: 3945136086n, + from: '0x08915f275100dfec26f63624eeacdd41e4040cc0' as `0x${string}`, + gasUsed: 1317262n, + logs: [], + logsBloom: + '0x00400000000000000000000008000000000000000000002000800000000000000000000002000004200000000000044000000000000000000000000000200000000080000000000000000008000000000001000000000000000000000000200000000000020020020000000000000808000002000200000040000418000200400400000000000000000000000000000000000000000000000000400000000008020004000001800020000000000000000000000000000000000000000000000000000002000000001000000000000000000000000000000000000000000020000010080400000000000000040000000000c00200000000000008000108000000' as `0x${string}`, + to: '0xbec2bbcb0fa594544c79bbc31d6fd58f55b6dc30' as `0x${string}`, + transactionHash: '0x2c37286396fe853d91cb7ebb21a8e4fb0e10ccc057cc153e7fda88ebba7a5651' as `0x${string}`, + transactionIndex: 63, + type: 'eip1559', + chainId: sepoliaChainId, + }; + + const PENDING_ERROR_MESSAGE = new Error('Transaction pending'); + + it('should return receipt immediately when status is success', async () => { + // Mock successful receipt on first attempt + vi.mocked(getTransactionReceipt).mockResolvedValueOnce(mockReceipt); + + const result = await getTransactionReceiptAutoRetry(mockParams); + expect(result).toEqual(mockReceipt); + expect(getTransactionReceipt).toHaveBeenCalledTimes(1); + }); + + it('should throw error immediately when status is reverted', async () => { + // Mock reverted receipt + vi.mocked(getTransactionReceipt).mockResolvedValueOnce({ ...mockReceipt, status: 'reverted' }); + + await expect(getTransactionReceiptAutoRetry(mockParams)).rejects.toThrow('Transaction is reverted'); + expect(getTransactionReceipt).toHaveBeenCalledTimes(1); + }); + + it('should retry when getTransactionReceipt is pending and then is success', async () => { + // Mock sequence: pending error -> success + vi.mocked(getTransactionReceipt) + .mockResolvedValueOnce({ ...mockReceipt, status: 'pending' as any }) + .mockResolvedValueOnce(mockReceipt); + + const result = await getTransactionReceiptAutoRetry(mockParams); + expect(result).toEqual(mockReceipt); + expect(getTransactionReceipt).toHaveBeenCalledTimes(2); + }); + + it('should retry when getTransactionReceipt is error and then is pending', async () => { + // Mock sequence: pending error -> success + const error = 'The Transaction may not be processed on a block yet.'; + vi.mocked(getTransactionReceipt).mockRejectedValueOnce(new Error(error)).mockResolvedValueOnce(mockReceipt); + + const result = await getTransactionReceiptAutoRetry(mockParams); + expect(result).toEqual(mockReceipt); + expect(getTransactionReceipt).toHaveBeenCalledTimes(2); + }); + + it('should catch eror when getTransactionReceipt is throw error', async () => { + // Mock sequence: pending error -> success + const error = 'The Transaction may not be processed on a block yet.'; + vi.mocked(getTransactionReceipt).mockRejectedValue(new Error(error)); + + await expect(getTransactionReceiptAutoRetry(mockParams, 10, 300)).rejects.toThrow(error); + expect(getTransactionReceipt).toHaveBeenCalledTimes(1); + }); + + it('should stop retrying after 200 attempts for pending transactions', async () => { + // Mock permanent pending error + vi.mocked(getTransactionReceipt).mockRejectedValue(PENDING_ERROR_MESSAGE); + + await expect(getTransactionReceiptAutoRetry(mockParams)).rejects.toThrow(PENDING_ERROR_MESSAGE); + expect(getTransactionReceipt).toHaveBeenCalled(); + }); + + it('should propagate non-pending errors immediately', async () => { + // Mock non-pending error + const criticalError = new Error('Critical error'); + vi.mocked(getTransactionReceipt).mockRejectedValue(criticalError); + + await expect(getTransactionReceiptAutoRetry(mockParams)).rejects.toThrow('Critical error'); + expect(getTransactionReceipt).toHaveBeenCalledTimes(1); + }); + }); + + describe('getBalanceByWagmi', () => { + it('should call wagmi getBalance with correct parameters', async () => { + const mockBalance = { value: 100n, decimals: 18, formatted: '', symbol: 'ETH' }; + vi.mocked(getBalance).mockResolvedValue(mockBalance); + + const params = { address: '0x123' as `0x${string}`, chainId: sepoliaChainId }; + const result = await getBalanceByWagmi(params); + + expect(result).toEqual(mockBalance); + expect(getBalance).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + }); + + describe('readContractByWagmi', () => { + it('should call wagmi readContract with formatted parameters', async () => { + const mockResult = { data: 'result' }; + vi.mocked(readContract).mockResolvedValue(mockResult); + + const params = { + address: '0x123', + abi: ['mockABI'], + functionName: 'balanceOf', + args: ['0x456'], + chainId: sepoliaChainId, + }; + const result = await readContractByWagmi(params); + + expect(result).toEqual(mockResult); + expect(readContract).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + + it('should handle missing optional parameters', async () => { + const mockResult = { data: 'empty' }; + vi.mocked(readContract).mockResolvedValue(mockResult); + + const params = { + address: '0x123', + functionName: 'totalSupply', + }; + const result = await readContractByWagmi(params as any); + + expect(result).toEqual(mockResult); + expect(readContract).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + }); + + describe('writeContractByWagmi', () => { + it('should call wagmi writeContract with formatted parameters', async () => { + const connector = { id: METAMASK_WALLET_ID } as any; + vi.mocked(getAccount).mockResolvedValue(connector); + const mockResult = '0x123' as `0x${string}`; + vi.mocked(writeContract).mockResolvedValue(mockResult); + + const params = { + address: '0x123', + account: '0x123' as `0x${string}`, + abi: ['mockABI'], + functionName: 'balanceOf', + args: ['0x456'], + chainId: sepoliaChainId, + }; + const result = await writeContractByWagmi(params); + + expect(result).toEqual(mockResult); + expect(getAccount).toHaveBeenCalledWith(EVMProviderConfig); + expect(writeContract).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + + it('should handle missing optional parameters', async () => { + const connector = { id: METAMASK_WALLET_ID } as any; + vi.mocked(getAccount).mockResolvedValue(connector); + const mockResult = '' as any; + vi.mocked(writeContract).mockResolvedValue(mockResult); + + const params = { + address: '0x123', + functionName: 'totalSupply', + }; + const result = await writeContractByWagmi(params as any); + + expect(result).toEqual(mockResult); + expect(writeContract).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + }); + + describe('getGasPriceByWagmi', () => { + it('should call wagmi getGasPrice with formatted parameters', async () => { + const mockResult = BigInt(10000); + vi.mocked(getGasPrice).mockResolvedValue(mockResult); + + const params = { + chainId: sepoliaChainId, + }; + const result = await getGasPriceByWagmi(params); + + expect(result).toEqual(mockResult); + expect(getGasPrice).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + }); + + describe('waitForTransactionReceiptByWagmi', () => { + it('should call wagmi waitForTransactionReceipt with formatted parameters', async () => { + const mockResult = { + status: 'success' as TEVMStatus, + transactionHash: '0x2c37286' as `0x${string}`, + type: 'eip1559', + chainId: sepoliaChainId, + } as any; + + vi.mocked(waitForTransactionReceipt).mockResolvedValue(mockResult); + + const params = { + hash: '0x2c37286' as `0x${string}`, + chainId: sepoliaChainId, + }; + + const result = await waitForTransactionReceiptByWagmi(params); + + expect(result).toEqual(mockResult); + expect(waitForTransactionReceipt).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + }); + + describe('switchChainByWagmi', () => { + it('should call wagmi switchChain with formatted parameters', async () => { + const params = { + chainId: sepoliaChainId, + }; + + await switchChainByWagmi(params); + + expect(switchChain).toHaveBeenCalledWith(EVMProviderConfig, params); + }); + }); +}); diff --git a/src/utils/__tests__/wallet.test.ts b/src/utils/__tests__/wallet.test.ts new file mode 100644 index 00000000..49fa237b --- /dev/null +++ b/src/utils/__tests__/wallet.test.ts @@ -0,0 +1,52 @@ +import { CHAIN_ID_MAP, SupportedChainId, SupportedTONChainId } from 'constants/chain'; +import { SupportedELFChainList } from 'constants/index'; +import { getPortkeySDKAccount, isEVMChain, isTONChain } from 'utils/wallet'; +import { describe, it, expect } from 'vitest'; + +describe('getPortkeySDKAccount', () => { + it('should generate ELF addresses for all supported chains', () => { + const accounts = getPortkeySDKAccount({ main: '0x123' }); + SupportedELFChainList.forEach((chain) => { + const chainId = chain.CHAIN_INFO.chainId; + expect(accounts[chainId][0]).toMatch(/ELF_0x123_/); + }); + }); + + it('should generate incorrect ELF addresses if input is empty', () => { + const accounts = getPortkeySDKAccount({}); + expect(accounts); + + SupportedELFChainList.forEach((chain) => { + const chainId = chain.CHAIN_INFO.chainId; + expect(accounts[chainId][0]).toBe(`ELF__${chainId}`); + }); + }); + + it('should throw error for invalid input', () => { + expect(() => getPortkeySDKAccount()).toThrow('accounts invalid'); + }); +}); + +describe('isEVMChain', () => { + it('should return true if input is EVM chains', () => { + const result = isEVMChain(CHAIN_ID_MAP[SupportedChainId.MAINNET]); + expect(result).toBe(true); + }); + + it('should return false if input is not EVM chains', () => { + const result = isEVMChain('AELF'); + expect(result).toBe(false); + }); +}); + +describe('isTONChain', () => { + it('should return true if input is TON chains', () => { + const result = isTONChain(CHAIN_ID_MAP[SupportedTONChainId.TESTNET]); + expect(result).toBe(true); + }); + + it('should return true if input is not TON chains', () => { + const result = isTONChain('AELF'); + expect(result).toBe(false); + }); +}); diff --git a/src/utils/__tests__/whitelist.test.ts b/src/utils/__tests__/whitelist.test.ts new file mode 100644 index 00000000..919d80a5 --- /dev/null +++ b/src/utils/__tests__/whitelist.test.ts @@ -0,0 +1,70 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { getTokenInfoByWhitelist } from 'utils/whitelist'; +import storages from 'constants/storages'; +import { ChainId } from 'types'; + +// Mock localStorage +const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: vi.fn((key: string) => store[key] || null), + setItem: vi.fn((key: string, value: string) => { + store[key] = value.toString(); + }), + removeItem: vi.fn((key: string) => { + delete store[key]; + }), + clear: vi.fn(() => { + store = {}; + }), + }; +})(); + +Object.defineProperty(window, 'localStorage', { value: localStorageMock }); + +describe('getTokenInfoByWhitelist', () => { + const mockTokenInfo = { symbol: 'ELF', address: '0x123' }; + const aelfMainChainId = 'AELF' as ChainId; + + beforeEach(() => { + const value = JSON.stringify({ + defaultWhitelistMap: { + ELF: { [aelfMainChainId]: mockTokenInfo }, + }, + }); + localStorageMock.setItem(storages.useWhitelist, value); + }); + + it('should retrieve token info from localStorage', () => { + const result = getTokenInfoByWhitelist(aelfMainChainId, 'ELF'); + expect(result).toEqual(mockTokenInfo); + }); + + it('should return undefined for missing data', () => { + localStorageMock.clear(); + const result = getTokenInfoByWhitelist(aelfMainChainId, 'ELF'); + expect(result).toBeUndefined(); + }); + + it('should handle invalid JSON in localStorage', () => { + localStorageMock.setItem(storages.useWhitelist, 'invalid_json'); + const result = getTokenInfoByWhitelist(aelfMainChainId, 'ELF'); + expect(result).toBeUndefined(); + }); + + it('should return undefined for invalid chainId', () => { + const result = getTokenInfoByWhitelist(undefined, 'ELF'); + expect(result).toBeUndefined(); + }); + + it('should return undefined for invalid symbol', () => { + const result = getTokenInfoByWhitelist(aelfMainChainId, undefined); + expect(result).toBeUndefined(); + }); + + it('should return undefined for invalid arguments', () => { + const result = getTokenInfoByWhitelist(); + expect(result).toBeUndefined(); + }); +}); diff --git a/src/utils/aelfAuthToken.ts b/src/utils/aelfAuthToken.ts new file mode 100644 index 00000000..1a2d34cd --- /dev/null +++ b/src/utils/aelfAuthToken.ts @@ -0,0 +1,87 @@ +import { LocalStorageKey } from 'constants/storages'; + +export enum AuthTokenSource { + Portkey = 'portkey', + NightElf = 'nightElf', + EVM = 'EVM', +} + +export type QueryAuthApiExtraRequest = { + pubkey: string; + signature: string; + plain_text: string; + source: AuthTokenSource; + managerAddress: string; + ca_hash?: string; // for Portkey + chain_id?: string; // for Portkey + recaptchaToken?: string; // for NightElf +}; + +export type JWTData = { + access_token: string; + expires_in: number; + token_type: string; +}; +const Day = 1 * 24 * 60 * 60 * 1000; +export type LocalJWTData = { + expiresTime?: number; +} & JWTData; +export const getLocalJWT = (key: string) => { + try { + const localData = localStorage.getItem(LocalStorageKey.ACCESS_TOKEN); + if (!localData) return; + const data = JSON.parse(localData) as { [key: string]: LocalJWTData }; + const cData = data[key]; + if (!cData || !cData?.expiresTime) return; + if (Date.now() + 0.5 * Day > cData?.expiresTime) return; + return cData; + } catch (error) { + return; + } +}; + +export const resetLocalJWT = () => { + return localStorage.removeItem(LocalStorageKey.ACCESS_TOKEN); +}; + +export const removeOneLocalJWT = (key: string) => { + const localData = localStorage.getItem(LocalStorageKey.ACCESS_TOKEN); + if (!localData) return; + const data = JSON.parse(localData) as { [key: string]: LocalJWTData }; + delete data[key]; + + localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify(data)); +}; + +export const setLocalJWT = (key: string, data: LocalJWTData) => { + const localData: LocalJWTData = { + ...data, + expiresTime: Date.now() + (data.expires_in - 10) * 1000, + }; + const _oldLocalData = localStorage.getItem(LocalStorageKey.ACCESS_TOKEN); + if (!_oldLocalData) { + return localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify({ [key]: localData })); + } + + const _localDataParse = JSON.parse(_oldLocalData) as { [key: string]: LocalJWTData }; + + _localDataParse[key] = localData; + + return localStorage.setItem(LocalStorageKey.ACCESS_TOKEN, JSON.stringify(_localDataParse)); +}; + +export function getAuthPlainText() { + const plainText = `Welcome to eBridge! + +Click to sign in and accept the eBridge Terms of Service (https://ebridge-exchange.gitbook.io/docs/more-information/terms-of-service) and Privacy Policy (https://ebridge-exchange.gitbook.io/docs/more-information/privacy-policy). + +This request will not trigger a blockchain transaction or cost any gas fees. + +Nonce: +${Date.now()}`; + + return { + plainTextOrigin: plainText, + plainTextHex: Buffer.from(plainText).toString('hex').replace('0x', ''), + }; +} diff --git a/src/utils/aelfUtils.tsx b/src/utils/aelfUtils.tsx new file mode 100644 index 00000000..82946eda --- /dev/null +++ b/src/utils/aelfUtils.tsx @@ -0,0 +1,373 @@ +import { message } from 'antd'; +import { getExploreLink, isELFAddress, shortenString, sleep } from 'utils'; +import BigNumber from 'bignumber.js'; +import { ContractBasic } from './contract'; +import AElf from 'aelf-sdk'; +import { AElfNodes } from 'constants/aelf'; +import { baseRequest } from 'api'; +import descriptor from '@aelfqueen/protobufjs/ext/descriptor'; +import { timesDecimals } from './calculate'; +import { isSymbol } from './reg'; +import { SupportedELFChainId } from 'constants/chain'; +import storages from 'constants/storages'; +import { isMobileDevices } from './isMobile'; +import { AelfInstancesKey, ChainId } from 'types'; +import type { AElfWallet } from '@aelf-react/types'; +import CommonMessage from 'components/CommonMessage'; +const Wallet = AElf.wallet; + +let wallet: AElfWallet; +const httpProviders: any = {}; + +export function getNodeByChainId(chainId: ChainId) { + return AElfNodes[chainId as AelfInstancesKey]; +} + +export function getAElf(chainId: ChainId) { + const rpc = getNodeByChainId(chainId)?.rpcUrl; + if (!httpProviders[rpc]) httpProviders[rpc] = new AElf(new AElf.providers.HttpProvider(rpc)); + return httpProviders[rpc]; +} + +export function getWallet() { + if (!wallet) wallet = Wallet.createNewWallet(); + return wallet; +} + +export const approveELF = async ( + address: string, + tokenContract: ContractBasic, + symbol = 'ELF', + amount: BigNumber | number | string, + chainId: ChainId, +) => { + const approveResult = await tokenContract.callSendMethod('Approve', '', [address, symbol, amount.toString()]); + if (approveResult.error) { + CommonMessage.error( + approveResult.error.message || approveResult?.errorMessage?.message || approveResult.errorMessage, + ); + return false; + } + const { TransactionId } = approveResult.result || approveResult; + await MessageTxToExplore(chainId, TransactionId); + return true; +}; + +export function getBlockHeight(chainId: ChainId) { + return getAElf(chainId).chain.getBlockHeight(); +} +export function getSerializedDataFromLog(log: any) { + return AElf.pbUtils.getSerializedDataFromLog(log); +} +export async function getTxResult( + chainId: ChainId, + TransactionId: string, + reGetCount = 0, + notExistedReGetCount = 0, +): Promise { + const txFun = getAElf(chainId).chain.getTxResult; + const txResult = await txFun(TransactionId); + // console.log(txResult, reGetCount, '====txResult'); + + if (txResult.error && txResult.errorMessage) { + throw Error(txResult.errorMessage.message || txResult.errorMessage.Message); + } + const result = txResult?.result || txResult; + + if (!result) { + throw Error('Can not get transaction result.'); + } + const lowerCaseStatus = result.Status.toLowerCase(); + + if (lowerCaseStatus === 'notexisted') { + if (notExistedReGetCount > 5) return result; + await sleep(1000); + notExistedReGetCount++; + reGetCount++; + return getTxResult(chainId, TransactionId, reGetCount, notExistedReGetCount); + } + + if (lowerCaseStatus === 'pending' || lowerCaseStatus === 'pending_validation') { + if (reGetCount > 20) return result; + await sleep(1000); + reGetCount++; + return getTxResult(chainId, TransactionId, reGetCount, notExistedReGetCount); + } + + if (lowerCaseStatus === 'mined') { + return result; + } + + throw Error(result.Error || `Transaction: ${result.Status}`); +} +export function messageHTML(txId: string, type: 'success' | 'error' | 'warning' = 'success', moreMessage = '') { + const aProps = isMobileDevices() ? {} : { target: '_blank', rel: 'noreferrer' }; + const explorerHref = getExploreLink(txId, 'transaction'); + const txIdHTML = ( + + + Transaction Id:   + + {shortenString(txId || '', 8)} + + +
+ {moreMessage && {moreMessage.replace('AElf.Sdk.CSharp.AssertionException:', '')}} +
+ ); + message[type](txIdHTML, 10); +} + +export async function MessageTxToExplore( + chainId: ChainId, + txId: string, + type: 'success' | 'error' | 'warning' = 'success', +) { + try { + const { TransactionId: validTxId } = await getTxResult(chainId, txId); + messageHTML(validTxId, type); + } catch (e: any) { + if (e.TransactionId) { + messageHTML(txId, 'error', e.Error || 'Transaction error.'); + } else { + messageHTML(txId, 'error', e.message || 'Transaction error.'); + } + } +} +export const checkElfAllowanceAndApprove = async ( + tokenContract: ContractBasic, + symbol: string, + address: string, + approveTargetAddress: string, + amount: string | number, + chainId: ChainId, +): Promise< + | boolean + | { + error: Error; + } +> => { + const [allowance, tokenInfo] = await Promise.all([ + tokenContract.callViewMethod('GetAllowance', [symbol, address, approveTargetAddress]), + tokenContract.callViewMethod('GetTokenInfo', [symbol]), + ]); + if (allowance?.error) { + CommonMessage.error(allowance.error.message || allowance.errorMessage?.message || allowance.errorMessage); + return false; + } + const bigA = timesDecimals(amount, tokenInfo?.decimals ?? 8); + const allowanceBN = new BigNumber(allowance?.allowance); + if (allowanceBN.lt(bigA)) return approveELF(approveTargetAddress, tokenContract, symbol, bigA, chainId); + return true; +}; + +export async function initContracts(contracts: { [name: string]: string }, aelfInstance: any, account?: string) { + const contractList = Object.entries(contracts); + try { + const list = await Promise.all( + contractList.map(async ([, address]) => { + try { + const contract = await aelfInstance.chain.contractAt(address, account ? { address: account } : getWallet()); + return contract; + } catch (error) { + console.debug(error, aelfInstance, '=====contractAt'); + return undefined; + } + }), + ); + const obj: any = {}; + contractList.forEach(([, value], index) => { + obj[value] = list[index]; + }); + + return obj; + } catch (error) { + console.log(error, 'initContracts'); + } +} +function setContractsFileDescriptorBase64(key: string, contracts: any) { + localStorage.setItem(key, JSON.stringify(contracts)); +} +function fileDescriptorSetFormatter(result: any) { + const buffer = Buffer.from(result, 'base64'); + return descriptor.FileDescriptorSet.decode(buffer as any); +} + +export function deleteLocalContractFileDescriptor() { + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if ( + key && + key.includes(storages.contractsFileDescriptorBase64.slice(0, 32)) && + !key.includes(storages.contractsFileDescriptorBase64) + ) { + localStorage.removeItem(key); + } + } +} +export async function getContractFileDescriptorSet(chainId: ChainId, address: string): Promise { + deleteLocalContractFileDescriptor(); + const key = storages.contractsFileDescriptorBase64 + chainId; + let base64s: any = localStorage.getItem(key); + const node = getNodeByChainId(chainId); + base64s = JSON.parse(base64s); + if (base64s && base64s[address]) { + try { + return fileDescriptorSetFormatter(base64s[address]); + } catch (error) { + delete base64s[address]; + setContractsFileDescriptorBase64(key, base64s); + return getContractFileDescriptorSet(chainId, address); + } + } else { + try { + if (!base64s) base64s = {}; + const base64 = await baseRequest({ + url: `${node.rpcUrl}/api/blockChain/contractFileDescriptorSet`, + params: { address }, + method: 'GET', + }); + const fds = fileDescriptorSetFormatter(base64); + base64s[address] = base64; + setContractsFileDescriptorBase64(key, base64s); + return fds; + } catch (error) { + console.debug(error, '======getContractFileDescriptorSet'); + } + } +} + +export const getServicesFromFileDescriptors = (descriptors: any) => { + const root = AElf.pbjs.Root.fromDescriptor(descriptors, 'proto3').resolveAll(); + return descriptors.file + .filter((f: any) => f.service.length > 0) + .map((f: any) => { + const sn = f.service[0].name; + const fullName = f.package ? `${f.package}.${sn}` : sn; + return root.lookupService(fullName); + }); +}; +const isWrappedBytes = (resolvedType: any, name: string) => { + if (!resolvedType?.name || resolvedType.name !== name) { + return false; + } + if (!resolvedType.fieldsArray || resolvedType.fieldsArray.length !== 1) { + return false; + } + return resolvedType.fieldsArray[0].type === 'bytes'; +}; +const isAddress = (resolvedType: any) => isWrappedBytes(resolvedType, 'Address'); + +const isHash = (resolvedType: any) => isWrappedBytes(resolvedType, 'Hash'); +export function transformArrayToMap(inputType: any, origin: any[]) { + if (!origin) return ''; + if (!Array.isArray(origin)) return origin; + if (origin.length === 0) return ''; + if (isAddress(inputType) || isHash(inputType)) return origin[0]; + + const { fieldsArray } = inputType || {}; + const fieldsLength = (fieldsArray || []).length; + + if (fieldsLength === 0) return origin; + + if (fieldsLength === 1) { + const i = fieldsArray[0]; + return { [i.name]: origin[0] }; + } + let result = {}; + Array.isArray(fieldsArray) && + Array.isArray(origin) && + fieldsArray.forEach((i, k) => { + result = { + ...result, + [i.name]: origin[k], + }; + }); + return result; +} + +export async function getContractMethods(chainId: ChainId, address: string) { + const fds = await getContractFileDescriptorSet(chainId, address); + const services = getServicesFromFileDescriptors(fds); + const obj: any = {}; + Object.keys(services).forEach((key) => { + const service = services[key]; + Object.keys(service.methods).forEach((key) => { + const method = service.methods[key].resolve(); + obj[method.name] = method.resolvedRequestType; + }); + }); + return obj; +} + +export const isElfChainSymbol = (symbol?: string | null) => { + if (symbol && symbol.length >= 2 && symbol.length <= 10 && isSymbol(symbol)) return symbol; + return false; +}; + +export const isELFChain = (chainId?: ChainId) => { + return !!(typeof chainId === 'string' && SupportedELFChainId[chainId as SupportedELFChainId]); +}; +export const getRawTx = ({ + blockHeight, + blockHash, + packedInput, + methodName, + contractAddress, + account, +}: { + account: string; + methodName: string; + contractAddress: string; + blockHeight: number; + blockHash: string; + packedInput: any; +}) => { + const rawTx = AElf.pbUtils.getTransaction(account, contractAddress, methodName, packedInput); + rawTx.refBlockNumber = blockHeight; + rawTx.refBlockPrefix = Buffer.from(blockHash, 'hex').slice(0, 4); + return rawTx; +}; + +export const encodedTransfer = ({ params, inputType }: { params: any; inputType: any }) => { + let input = AElf.utils.transform.transformMapToArray(inputType, params); + + input = AElf.utils.transform.transform(inputType, input, AElf.utils.transform.INPUT_TRANSFORMERS); + + const message = inputType.fromObject(input); + return inputType.encode(message).finish(); +}; + +export const encodeTransaction = (tx: any) => { + return AElf.pbUtils.encodeTransaction(tx); +}; + +export const uint8ArrayToHex = (tx: any) => { + return AElf.utils.uint8ArrayToHex(tx); +}; + +export const getELFAddress = (address?: string) => { + if (!address) return; + const list = address.split('_'); + if (list.length === 3 && isELFAddress(list[1])) return list[1]; +}; + +export const recoverPubKey = (msg: any, signature: string) => { + const signatureObj = { + r: signature.slice(0, 64), + s: signature.slice(64, 128), + recoveryParam: Number(signature.slice(128, 130)), + }; + + const hexMsg = AElf.utils.sha256(msg); + const publicKey = AElf.wallet.ellipticEc + .recoverPubKey(Buffer.from(hexMsg, 'hex'), signatureObj, signatureObj.recoveryParam) + .encode('hex', false); + return publicKey; +}; + +export const pubKeyToAddress = (pubKey: string) => { + const onceSHAResult = Buffer.from(AElf.utils.sha256(Buffer.from(pubKey, 'hex')), 'hex'); + const hash = AElf.utils.sha256(onceSHAResult).slice(0, 64); + return AElf.utils.encodeAddressRep(hash); +}; diff --git a/src/utils/api/application.ts b/src/utils/api/application.ts new file mode 100644 index 00000000..b6ede85c --- /dev/null +++ b/src/utils/api/application.ts @@ -0,0 +1,102 @@ +import { request } from 'api'; +import { + TAddApplicationChainRequest, + TAddApplicationChainResult, + TGetApplicationChainStatusListRequest, + TGetApplicationChainStatusListResult, + TGetApplicationDetailRequest, + TGetApplicationDetailResult, + TGetApplicationIssueResult, + TGetApplicationTokenInfoRequest, + TGetApplicationTokenInfoResult, + TGetApplicationTokenListRequest, + TGetApplicationTokenListResult, + TCommitTokenInfoRequest, + TCommitTokenInfoResult, + TGetMyApplicationListRequest, + TGetMyApplicationListResult, + TPrepareBindIssueRequest, + TPrepareBindIssueResult, + TGetApplicationIssueRequest, + TGetTokenConfigRequest, + TGetTokenConfigResult, + TGetApplicationTokenDetailRequest, + TGetApplicationTokenDetailResult, +} from 'types/api'; + +export const getApplicationTokenList = async ( + params: TGetApplicationTokenListRequest, +): Promise => { + const res = await request.application.getTokenList({ params }); + return res.data; +}; + +export const getApplicationTokenDetail = async ( + params: TGetApplicationTokenDetailRequest, +): Promise => { + const res = await request.application.getTokenDetail({ params }); + return res.data; +}; + +export const commitTokenInfo = async (params: TCommitTokenInfoRequest): Promise => { + const res = await request.application.commitTokenInfo({ + data: params, + }); + return res.data; +}; + +export const getApplicationTokenInfo = async ( + params: TGetApplicationTokenInfoRequest, +): Promise => { + const res = await request.application.getTokenInfo({ + params, + }); + return res.data; +}; + +export const getApplicationChainStatusList = async ( + params: TGetApplicationChainStatusListRequest, +): Promise => { + const res = await request.application.getChainStatus({ + params, + }); + return res.data; +}; + +export const addApplicationChain = async (params: TAddApplicationChainRequest): Promise => { + const res = await request.application.addChain({ + data: params, + }); + return res.data; +}; + +export const prepareBindIssue = async (params: TPrepareBindIssueRequest): Promise => { + const res = await request.application.prepareBindIssue({ + data: params, + }); + return res.data; +}; + +export const getApplicationIssue = async (params: TGetApplicationIssueRequest): Promise => { + const res = await request.application.getIssue({ data: params }); + return res.data; +}; + +export const getMyApplicationList = async ( + params: TGetMyApplicationListRequest, +): Promise => { + const res = await request.application.getMyApplicationList({ params }); + return res.data; +}; + +export const getApplicationDetail = async ( + params: TGetApplicationDetailRequest, +): Promise => { + const res = await request.application.getApplicationDetail({ params }); + return res.data; +}; + +export const getApplicationTokenConfig = async (params: TGetTokenConfigRequest): Promise => { + const res = await request.application.getTokenConfig({ params }); + return res.data; +}; diff --git a/src/utils/api/auth.ts b/src/utils/api/auth.ts new file mode 100644 index 00000000..4c6da6cf --- /dev/null +++ b/src/utils/api/auth.ts @@ -0,0 +1,40 @@ +import { service } from 'api/utils'; +import axios from 'axios'; +import { BASE_AUTH_URL } from 'constants/index'; +import { stringify } from 'query-string'; +import { PortkeyVersion } from 'types/wallet'; +import eBridgeEventBus from 'utils/eBridgeEventBus'; +import { JWTData, QueryAuthApiExtraRequest, setLocalJWT } from 'utils/aelfAuthToken'; + +type QueryAuthApiBaseConfig = { + grant_type: string; + scope: string; + client_id: string; + version: PortkeyVersion; +}; + +const queryAuthApiBaseConfig: QueryAuthApiBaseConfig = { + grant_type: 'signature', + scope: 'CrossChainServer', + client_id: 'CrossChainServer_App', + version: PortkeyVersion.v2, +}; + +export const queryAuthApi = async (config: QueryAuthApiExtraRequest): Promise => { + const data = { ...queryAuthApiBaseConfig, ...config }; + const res = await axios.post(`${BASE_AUTH_URL}/connect/token`, stringify(data), { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + }); + const token_type = res.data.token_type; + const access_token = res.data.access_token; + + service.defaults.headers.common['Authorization'] = `${token_type} ${access_token}`; + eBridgeEventBus.AelfAuthTokenSuccess.emit(); + + if (localStorage) { + const key = (config?.ca_hash || config.source) + config.managerAddress; + setLocalJWT(key, res.data); + } + + return `${token_type} ${access_token}`; +}; diff --git a/src/utils/api/common.ts b/src/utils/api/common.ts new file mode 100644 index 00000000..ab162b22 --- /dev/null +++ b/src/utils/api/common.ts @@ -0,0 +1,14 @@ +import { request } from 'api'; +import { TGetTokenPriceRequest, TGetTokenPriceResult, TGetTokenWhiteListResult } from 'types/api'; + +export const getTokenPrice = async (params: TGetTokenPriceRequest): Promise => { + const res = await request.common.getTokenPrice({ params }); + + return res.data; +}; + +export const getTokenWhiteList = async (): Promise => { + const res = await request.common.getTokenWhiteList(); + + return res.data; +}; diff --git a/src/utils/calculate.ts b/src/utils/calculate.ts new file mode 100644 index 00000000..789d5277 --- /dev/null +++ b/src/utils/calculate.ts @@ -0,0 +1,30 @@ +import BigNumber from 'bignumber.js'; +import { ZERO } from 'constants/misc'; +import { isEffectiveNumber } from 'utils'; +export function timesDecimals(a?: BigNumber.Value, decimals: string | number = 18) { + if (!a) return ZERO; + const bigA = ZERO.plus(a); + if (bigA.isNaN()) return ZERO; + if (typeof decimals === 'string' && decimals.length > 10) return bigA.times(decimals); + return bigA.times(`1e${decimals}`); +} + +export function divDecimals(a?: BigNumber.Value, decimals: string | number = 18) { + if (!a) return ZERO; + const bigA = ZERO.plus(a); + if (bigA.isNaN()) return ZERO; + if (typeof decimals === 'string' && decimals.length > 10) return bigA.div(decimals); + return bigA.div(`1e${decimals}`); +} + +export function divDecimalsStr(a?: BigNumber.Value, decimals: string | number = 8, defaultVal = '--') { + const n = divDecimals(a, decimals); + return isEffectiveNumber(n) ? n.toFormat() : defaultVal; +} + +export function bigNumberToWeb3Input(input: BigNumber): string { + return BigNumber.isBigNumber(input) ? input.toFixed(0) : new BigNumber(input).toFixed(0); +} +export function valueToPercentage(input?: BigNumber.Value) { + return BigNumber.isBigNumber(input) ? input.times(100) : timesDecimals(input, 2); +} diff --git a/src/utils/chain.ts b/src/utils/chain.ts new file mode 100644 index 00000000..a4fbdf7f --- /dev/null +++ b/src/utils/chain.ts @@ -0,0 +1,102 @@ +import { CHAIN_SHORT_NAME, SupportedELFChainId } from 'constants/chain'; +import { CHAIN_ID_MAP } from 'constants/chain'; +import { ChainId, ChainType } from 'types'; +import { enumToMap, isTonChain, shortenAddress } from 'utils'; +import { isELFChain } from './aelfUtils'; +import AElf from 'aelf-sdk'; +import { CHAIN_ICON, CHAIN_NAME, SupportedELFChain, SupportedERCChain, SupportedTONChain } from 'constants/index'; +const { chainIdConvertor } = AElf.utils; +export function getIconByChainId(chainId: ChainId) { + return CHAIN_ICON[chainId]; +} +export function getNameByChainId(chainId?: ChainId) { + if (!chainId) return 'unknown chain'; + return CHAIN_NAME[chainId]; +} +export function getShortNameByChainId(chainId?: ChainId) { + if (!chainId) return 'unknown chain'; + const name = (CHAIN_SHORT_NAME as any)[chainId]; + if (name) return name; + return chainId as string; +} + +export function getNativeTokenByChainId(chainId?: ChainId) { + if (!chainId) return 'unknown chain'; + const name = (CHAIN_SHORT_NAME as any)[chainId]; + if (name) return name; + return chainId as string; +} + +const chainMap = enumToMap(CHAIN_ID_MAP); +export function getChainIdByAPI(chainId: string) { + const elfChainId = chainMap[chainId]; + if (elfChainId) return isNaN(elfChainId) ? elfChainId : (Number(elfChainId) as ChainId); + return chainId as ChainId; +} +export function getIconByAPIChainId(apiChainId: string) { + const chainId = getChainIdByAPI(apiChainId); + return getIconByChainId(chainId); +} +export function getChainIdToMap(chainId?: ChainId) { + const id = (CHAIN_ID_MAP as any)[chainId as any]; + if (id) return id; + return chainId; +} +export function base58ToChainId(chainId: ChainId) { + return chainIdConvertor.base58ToChainId(chainId); +} +export function chainIdToBase58(chainId: number) { + return chainIdConvertor.chainIdToBase58(chainId); +} +const head = 'ELF_'; + +export function formatRestoreAddress(chainId: ChainId, address?: string) { + if (!address) return ''; + const tail = `_${chainId}`; + return address.replace(new RegExp(head, 'g'), '').replace(new RegExp(tail, 'g'), ''); +} + +export function formatAddress(chainId?: ChainId, address?: string) { + if (!address || !chainId) return ''; + address = formatRestoreAddress(chainId, address); + const tail = `_${chainId}`; + return head + address + tail; +} + +export function shortenAddressByAPI(address?: string, chainId?: ChainId, chars?: number) { + if (!address || !chainId) { + return ''; + } + const isELF = isELFChain(chainId); + const tmpAddress = isELF ? formatAddress(chainId, address) : address; + return shortenAddress(tmpAddress, chars, isELF ? 58 : undefined); +} + +export const formatNetworkName = (item: string) => { + switch (item) { + case SupportedELFChainId.AELF: + return CHAIN_NAME[SupportedELFChainId.AELF]; + case SupportedELFChainId.tDVV: + return CHAIN_NAME[SupportedELFChainId.tDVV]; + case SupportedELFChainId.tDVW: + return CHAIN_NAME[SupportedELFChainId.tDVW]; + default: + return item; + } +}; + +export const getBridgeChainInfo = (chainId?: ChainId) => { + if (isELFChain(chainId)) return (SupportedELFChain as any)[chainId as any]; + if (isTonChain(chainId)) return (SupportedTONChain as any)[chainId as any]; + return (SupportedERCChain as any)[chainId as any]; +}; + +export const getChainName = (chanId: ChainId) => { + return CHAIN_NAME[chanId]; +}; + +export const getChainType = (chanId: ChainId): ChainType => { + if (isELFChain(chanId)) return 'ELF'; + if (isTonChain(chanId)) return 'TON'; + return 'ERC'; +}; diff --git a/src/utils/checkAElfBridge.ts b/src/utils/checkAElfBridge.ts new file mode 100644 index 00000000..f44ec66f --- /dev/null +++ b/src/utils/checkAElfBridge.ts @@ -0,0 +1,23 @@ +import { AElfDappBridge } from '@aelf-react/types'; +let endpoint = ''; + +// options & connect is aelf-bridge only +export function isAElfBridge(aelfBridge: AElfDappBridge) { + return !!(aelfBridge.options && aelfBridge.connect); +} +export function isCurrentAElfBridge(aelfBridge: AElfDappBridge) { + return endpoint === aelfBridge.options?.endpoint; +} + +export async function reConnectAElfBridge(aelfBridge: AElfDappBridge) { + const isConnected = await aelfBridge.connect?.(); + if (!isConnected) throw Error('Reconnect Failed'); + endpoint = aelfBridge.options?.endpoint || ''; +} + +// aelf-bridge only supports one node and needs to check whether it is connected +export async function checkAElfBridge(aelfBridge: AElfDappBridge) { + if (isAElfBridge(aelfBridge) && !isCurrentAElfBridge(aelfBridge)) { + await reConnectAElfBridge(aelfBridge); + } +} diff --git a/src/utils/contract.ts b/src/utils/contract.ts new file mode 100644 index 00000000..47427a43 --- /dev/null +++ b/src/utils/contract.ts @@ -0,0 +1,678 @@ +/* eslint-disable react/no-is-mounted */ +import { ELFChainConstants, ERCChainConstants } from 'constants/ChainConstants'; +import { getContractMethods, transformArrayToMap, getTxResult, isELFChain, encodedTransfer } from './aelfUtils'; +import { ChainId, ChainType } from 'types'; +import { isTonChain, sleep } from 'utils'; +import { AElfDappBridge } from '@aelf-react/types'; +import { checkAElfBridge } from './checkAElfBridge'; +import { IAElfChain, IChain, IPortkeyProvider } from '@portkey/provider-types'; +import { IContract } from '@portkey/types'; +import { WebLoginWalletInfo } from 'types/wallet'; +import { TonConnectUI } from '@tonconnect/ui-react'; +import { getTransactionResponseHash } from './ton'; +import { CallTonContract, TonContractCallData } from './tonContractCall'; +import { + readContractByWagmi, + TReadContractByWagmiParams, + TWriteContractByWagmiParams, + waitForTransactionReceiptByWagmi, + writeContractByWagmi, +} from './wagmi'; +import detectProvider from '@portkey/detect-provider'; + +export interface AbiType { + internalType?: string; + name?: string; + type?: string; + components?: AbiType[]; +} +type SendOptions = { + from?: string; + gasPrice?: string; + gas?: number; + value?: number | string; + nonce?: number; + onMethod: 'transactionHash' | 'receipt' | 'confirmation'; +}; +export interface AbiItem { + constant?: boolean; + inputs?: AbiType[]; + name?: string; + outputs?: AbiType[]; + payable?: boolean; + stateMutability?: string; + type?: string; +} + +export interface ContractProps { + contractABI?: AbiItem[]; + contractAddress: string; + chainId?: ChainId; + aelfContract?: any; + aelfInstance?: AElfDappBridge; + viewContract?: any; + portkeyChain?: IAElfChain; + tonConnectUI?: TonConnectUI; + fairyVaultChain?: IAElfChain; +} + +interface ErrorMsg { + error: { + name?: string; + code: number; + message: string; + }; +} + +type CallViewMethod = ( + functionName: string, + paramsOption?: any, + callOptions?: { + defaultBlock: number | string; + options?: any; + callback?: any; + }, +) => Promise; + +type CallSendMethod = ( + functionName: string, + account: string, + paramsOption?: any, + sendOptions?: SendOptions, +) => Promise | Promise; + +export type ContractBasicErrorMsg = ErrorMsg; +export class ContractBasic { + public address?: string; + public callContract: + | WB3ContractBasic + | AElfContractBasic + | PortkeyContractBasic + | TONContractBasic + | FairyVaultContractBasic; + public contractType: ChainType; + public chainId?: ChainId; + // public isPortkey?: boolean; + constructor(options: ContractProps) { + this.address = options.contractAddress; + this.chainId = options.chainId; + const isELF = isELFChain(options.chainId); + const isTON = isTonChain(options.chainId); + this.callContract = options.fairyVaultChain + ? new FairyVaultContractBasic(options) + : options.portkeyChain + ? new PortkeyContractBasic(options) + : isELF + ? new AElfContractBasic(options) + : isTON + ? new TONContractBasic(options) + : new WB3ContractBasic(options); + this.contractType = isELF ? 'ELF' : isTON ? 'TON' : 'ERC'; + } + + public checkContractParams() { + return ( + this.callContract instanceof AElfContractBasic || + this.callContract instanceof PortkeyContractBasic || + this.callContract instanceof FairyVaultContractBasic + ); + } + + public callViewMethod: CallViewMethod = async ( + functionName, + paramsOption, + callOptions = { defaultBlock: 'latest' }, + ) => { + if (this.checkContractParams()) return this.callContract.callViewMethod(functionName, paramsOption); + + return this.callContract.callViewMethod(functionName, paramsOption, callOptions); + }; + + public callSendMethod: CallSendMethod = async (functionName, account, paramsOption, sendOptions) => { + console.log(functionName, paramsOption, '++++paramsOption'); + if (this.checkContractParams()) return this.callContract.callSendMethod(functionName, paramsOption, sendOptions); + return this.callContract.callSendMethod(functionName, account, paramsOption, sendOptions); + }; + public callSendPromiseMethod: CallSendMethod = async (functionName, account, paramsOption, sendOptions) => { + if (this.checkContractParams()) return this.callContract.callSendPromiseMethod(functionName, paramsOption); + + return this.callContract.callSendPromiseMethod(functionName, account, paramsOption, sendOptions); + }; + public encodedTx: CallViewMethod = async (functionName, paramsOption) => { + if (this.callContract instanceof AElfContractBasic) return this.callContract.encodedTx(functionName, paramsOption); + }; +} + +export class WB3ContractBasic { + public address?: string; + public chainId?: number; + contractABI: AbiItem[] | undefined; + constructor(options: ContractProps) { + const { contractABI, contractAddress, chainId } = options; + this.contractABI = contractABI; + this.chainId = chainId as number; + this.address = contractAddress; + } + + public callViewMethod: CallViewMethod = async ( + functionName, + paramsOption, + callOptions = { defaultBlock: 'latest' }, + ) => { + try { + const { defaultBlock } = callOptions; + const _chainId = this.chainId || ERCChainConstants.chainId; + + const params = { + abi: this.contractABI, + functionName, + address: this.address as string, + chainId: _chainId, + args: paramsOption, + } as TReadContractByWagmiParams; + + if (defaultBlock === 'latest') delete params.blockNumber; + + console.log(params, '===== WB3ContractBasic callViewMethod readContractByWagmi params'); + const res = await readContractByWagmi(params); + return res; + } catch (e) { + console.log(e, '===== WB3ContractBasic callViewMethod error'); + + return { error: e }; + } + }; + + public callSendMethod: CallSendMethod = async (functionName, account, paramsOption, sendOptions) => { + try { + const { onMethod = 'receipt', gas, gasPrice, value, nonce } = sendOptions || {}; + const _chainId = this.chainId || ERCChainConstants.chainId; + + // optimize gas fees + const gasPriceFromApi = gasPrice; + // BSC does not need to actively calculate gas fee + // if (_chainId !== SupportedChainId.BSC_MAINNET && _chainId !== SupportedChainId.BSC_TESTNET) { + // try { + // const _gasPrice = (await getGasPriceByWagmi({ chainId: _chainId as number })) || '10000000000'; + // gasPriceFromApi = ZERO.plus(String(_gasPrice)).times(1.15).toFixed(0); + // } catch (error) { + // console.log(error); + // } + // } + + const params = { + abi: this.contractABI, + functionName, + address: this.address as string, + chainId: _chainId, + args: paramsOption, + account, + } as TWriteContractByWagmiParams; + + if (gasPriceFromApi) { + params.gasPrice = BigInt(gasPriceFromApi); + } + if (gas) { + params.gas = BigInt(gas); + } + if (value) { + params.value = BigInt(String(value)); // parseEther('0.01') + } + if (nonce) { + params.nonce = nonce; + } + + console.log('params', '===== WB3ContractBasic callSendMethod writeContractByWagmi params'); + const result = await writeContractByWagmi(params); + + if (onMethod === 'receipt') { + const receiptResult = await waitForTransactionReceiptByWagmi({ hash: result, chainId: _chainId as number }); + if (receiptResult.status === 'reverted') { + throw { message: 'Transaction is reverted', ...receiptResult }; + } + + return { ...receiptResult, TransactionId: receiptResult.transactionHash }; + } + + return { TransactionId: result }; + } catch (error) { + console.log(error, '===== WB3ContractBasic callSendMethod error'); + return { error }; + } + }; + + public callSendPromiseMethod: CallSendMethod = async (functionName, account, paramsOption, sendOptions) => { + try { + const _chainId = this.chainId || ERCChainConstants.chainId; + const params = { + abi: this.contractABI, + functionName, + address: this.address as string, + chainId: _chainId, + args: paramsOption, + account, + } as TWriteContractByWagmiParams; + + const { gasPrice, gas, value, nonce } = sendOptions || {}; + if (gasPrice) { + params.gasPrice = BigInt(gasPrice); // parseGwei(gasPrice); + } + if (gas) { + params.gas = BigInt(gas); + } + if (value) { + params.value = BigInt(String(value)); + } + if (nonce) { + params.nonce = nonce; + } + + return await writeContractByWagmi(params); + } catch (e) { + return { error: e }; + } + }; +} + +type AElfCallViewMethod = (functionName: string, paramsOption?: any) => Promise; + +type AElfCallSendMethod = ( + functionName: string, + paramsOption?: any, + sendOptions?: SendOptions, +) => Promise | Promise; + +export class AElfContractBasic { + public aelfContract: any; + public viewContract?: any; + public address: string; + public methods?: any; + public chainId: ChainId; + public aelfInstance?: AElfDappBridge; + constructor(options: ContractProps) { + const { aelfContract, contractAddress, chainId, aelfInstance, viewContract } = options; + this.address = contractAddress; + this.aelfContract = aelfContract; + this.chainId = chainId as ChainId; + this.aelfInstance = aelfInstance; + this.viewContract = viewContract; + this.getFileDescriptorsSet(this.address); + } + getFileDescriptorsSet = async (address: string) => { + try { + this.methods = await getContractMethods(this.chainId, address); + } catch (error) { + throw new Error(JSON.stringify(error) + 'address:' + address + 'Contract:' + 'getContractMethods'); + } + }; + checkMethods = async () => { + if (!this.methods) await this.getFileDescriptorsSet(this.address); + }; + checkConnected = async () => { + if (!this.aelfInstance) throw Error('aelfInstance is undefined'); + await checkAElfBridge(this.aelfInstance); + }; + public callViewMethod: AElfCallViewMethod = async (functionName, paramsOption) => { + const contract = this.viewContract || this.aelfContract; + if (!contract) return { error: { code: 401, message: 'Contract init error1' } }; + try { + await this.checkMethods(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + + const req = await contract[functionNameUpper].call(transformArrayToMap(inputType, paramsOption)); + if (!req?.error && (req?.result || req?.result === null)) return req.result; + return req; + } catch (e) { + return { error: e }; + } + }; + + public callSendMethod: AElfCallSendMethod = async (functionName, paramsOption, sendOptions) => { + if (!this.aelfContract) return { error: { code: 401, message: 'Contract init error2' } }; + try { + const { onMethod = 'receipt' } = sendOptions || {}; + await this.checkMethods(); + await this.checkConnected(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + const params = transformArrayToMap(inputType, paramsOption); + console.log(params, functionNameUpper, '=======params', paramsOption, inputType); + const req = await this.aelfContract[functionNameUpper](params); + if (req.error) { + return { + error: { + code: req.error.message?.Code || req.error, + message: req.errorMessage?.message || req.error.message?.Message, + }, + }; + } + + const { TransactionId } = req.result || req; + if (onMethod === 'receipt') { + await sleep(1000); + try { + return await getTxResult(this.chainId, TransactionId); + } catch (error: any) { + if (error.message) return { error }; + return { + ...error, + error: { + code: req?.error?.message?.Code || req?.error, + message: error.Error || req?.errorMessage?.message || req?.error?.message?.Message, + }, + }; + } + } + return { TransactionId }; + } catch (error: any) { + if (error.message) return { error }; + return { error: { message: error.Error || error.Status } }; + } + }; + + public encodedTx: AElfCallViewMethod = async (functionName, paramsOption) => { + if (!this.aelfContract) return { error: { code: 401, message: 'Contract init error2' } }; + try { + await this.checkMethods(); + const methodName = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[methodName]; + return encodedTransfer({ params: paramsOption, inputType }); + } catch (e) { + return { error: e }; + } + }; + + public callSendPromiseMethod: AElfCallSendMethod = async (functionName, paramsOption) => { + if (!this.aelfContract) return { error: { code: 401, message: 'Contract init error3' } }; + if (!ELFChainConstants.aelfInstances?.AELF.appName && !ELFChainConstants.aelfInstances?.AELF.options) + return { error: { code: 402, message: 'connect aelf' } }; + try { + await this.checkMethods(); + return this.aelfContract[functionName](transformArrayToMap(this.methods[functionName], paramsOption)); + } catch (e) { + return { error: e }; + } + }; +} + +export class PortkeyContractBasic { + public contract?: IContract; + public chainId: ChainId; + public portkeyChain?: IAElfChain; + public methods?: any; + public address: string; + constructor(options: ContractProps) { + const { contractAddress, chainId } = options; + this.portkeyChain = options.portkeyChain; + this.contract = this.portkeyChain?.getContract(options.contractAddress); + this.address = contractAddress; + this.chainId = chainId as ChainId; + this.getFileDescriptorsSet(this.address); + } + + getFileDescriptorsSet = async (address: string) => { + try { + this.methods = await getContractMethods(this.chainId, address); + } catch (error) { + throw new Error(JSON.stringify(error) + 'address:' + address + 'Contract:' + 'getContractMethods'); + } + }; + checkMethods = async () => { + if (!this.methods) await this.getFileDescriptorsSet(this.address); + }; + public callViewMethod: AElfCallViewMethod = async (functionName, paramsOption) => { + const contract = this.contract; + if (!contract) return { error: { code: 401, message: 'Contract init error1' } }; + try { + await this.checkMethods(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + + const req = await contract.callViewMethod(functionNameUpper, transformArrayToMap(inputType, paramsOption)); + console.log(req, transformArrayToMap(inputType, paramsOption), functionNameUpper, '=======callViewMethod'); + + if (req.error) return req; + + return req?.data || req; + } catch (e) { + return { error: e }; + } + }; + + public callSendMethod: AElfCallSendMethod = async (functionName, paramsOption, sendOptions) => { + const contract = this.contract; + if (!contract) return { error: { code: 401, message: 'Contract init error2' } }; + const { onMethod = 'receipt', ...options } = sendOptions || {}; + try { + await this.checkMethods(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + console.log(transformArrayToMap(inputType, paramsOption), paramsOption, functionNameUpper, '=callSendMethod'); + const req = await contract.callSendMethod(functionNameUpper, '', transformArrayToMap(inputType, paramsOption), { + onMethod, + ...options, + }); + if (req.error) return req; + return req?.data || req; + } catch (error: any) { + if (error.message) return { error }; + return { error: { message: error.Error || error.Status } }; + } + }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public callSendPromiseMethod: CallSendMethod = async (_functionName, _account, _paramsOption, _sendOptions) => { + throw new Error('Method not implemented.'); + }; +} + +export class PortkeySDKContractBasic { + public chainId: ChainId; + public contractType: string; + public methods?: any; + public address: string; + public sdkContract?: IContract; + public viewContract: any; + public portkeyWallet?: WebLoginWalletInfo; + public caContract?: IContract; + public contract?: IContract; + public provider?: IPortkeyProvider | null; + public chain?: IChain; + constructor( + options: { + sdkContract?: IContract; + viewContract: any; + portkeyWallet?: WebLoginWalletInfo; + caContract?: IContract; + } & ContractProps, + ) { + const { sdkContract, contractAddress, chainId, viewContract, portkeyWallet, caContract } = options; + // this.portkeyChain = options.portkeyChain; + // this.contract = this.portkeyChain?.getContract(options.contractAddress); + this.address = contractAddress; + this.sdkContract = sdkContract; + this.chainId = chainId as ChainId; + this.viewContract = viewContract; + this.contractType = 'ELF'; + this.portkeyWallet = portkeyWallet; + this.caContract = caContract; + } + + getFileDescriptorsSet = async (address: string) => { + try { + this.methods = await getContractMethods(this.chainId, address); + } catch (error) { + throw new Error(JSON.stringify(error) + 'address:' + address + 'Contract:' + 'getContractMethods'); + } + }; + checkMethods = async () => { + if (!this.methods) await this.getFileDescriptorsSet(this.address); + }; + public callViewMethod: AElfCallViewMethod = async (functionName, paramsOption) => { + const contract = this.viewContract; + if (!contract) return { error: { code: 401, message: 'Contract init error1' } }; + try { + await this.checkMethods(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + + const req = await contract[functionNameUpper].call(transformArrayToMap(inputType, paramsOption)); + if (!req?.error && (req?.result || req?.result === null)) return req.result; + return req; + } catch (e) { + return { error: e }; + } + }; + + public getProvider = async () => { + if (!this.provider) this.provider = await detectProvider({ providerName: 'PortkeyWebWallet' }); + return this.provider; + }; + + public getChain = async (chainId: ChainId) => { + const provider = await this.getProvider(); + if (!this.chain) this.chain = await provider?.getChain(chainId as any); + return this.chain; + }; + public getContract = async () => { + const chain = await this.getChain(this.chainId); + if (!this.contract) this.contract = chain?.getContract(this.address); + return this.contract; + }; + + public callSendMethod: CallSendMethod = async (functionName, _account, paramsOption, sendOptions) => { + const { onMethod = 'receipt', ...options } = sendOptions || {}; + + try { + const contract = await this.getContract(); + if (!contract) return { error: { code: 401, message: 'Contract init error2' } }; + await this.checkMethods(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + + const req = await contract.callSendMethod(functionNameUpper, '', transformArrayToMap(inputType, paramsOption), { + onMethod, + ...options, + }); + console.log(req, '======PortkeySDKContractBasic-callSendMethod-req'); + if (req.error) return req; + return req?.data || req; + } catch (error: any) { + console.log(error, '======PortkeySDKContractBasic-callSendMethod-error'); + + if (error.message) return { error }; + return { error: { message: error.Error || error.Status } }; + } + }; +} + +export class TONContractBasic { + public tonConnectUI?: TonConnectUI; + public address?: string; + public chainId?: number; + + constructor(options: ContractProps) { + this.tonConnectUI = options.tonConnectUI; + const { contractAddress, chainId } = options; + this.chainId = chainId as number; + this.address = contractAddress; + } + // eslint-disable-next-line @typescript-eslint/no-empty-function + public callViewMethod: AElfCallViewMethod = async (functionName, paramsOption) => { + try { + return (CallTonContract as any)[functionName](this.address, paramsOption); + } catch (error: any) { + if (error.message) return { error }; + return { error: { message: error.Error || error.Status } }; + } + }; + + // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars + public callSendMethod: CallSendMethod = async (functionName, _account, paramsOption, _sendOptions) => { + const account = this.tonConnectUI?.account; + if (!this.address || !account || !this.tonConnectUI) + return { error: { code: 401, message: 'Contract init error' } }; + try { + const callData = await (TonContractCallData as any)[functionName](this.address, account.address, paramsOption); + const result = await this.tonConnectUI?.sendTransaction(callData); + const hashBase64 = await getTransactionResponseHash(result); + return { TransactionId: hashBase64 }; + } catch (error: any) { + if (error.message) return { error }; + return { error: { message: error.Error || error.Status } }; + } + }; + // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars + public callSendPromiseMethod: CallSendMethod = async (_functionName, account, paramsOption, sendOptions) => {}; +} + +export class FairyVaultContractBasic { + public contract?: IContract; + public chainId: ChainId; + public fairyVaultChain?: IAElfChain; + public methods?: any; + public address: string; + constructor(options: ContractProps) { + const { contractAddress, chainId } = options; + this.fairyVaultChain = options.fairyVaultChain; + this.contract = this.fairyVaultChain?.getContract(options.contractAddress); + this.address = contractAddress; + this.chainId = chainId as ChainId; + this.getFileDescriptorsSet(this.address); + } + + getFileDescriptorsSet = async (address: string) => { + try { + this.methods = await getContractMethods(this.chainId, address); + } catch (error) { + throw new Error(JSON.stringify(error) + 'address:' + address + 'Contract:' + 'getContractMethods'); + } + }; + checkMethods = async () => { + if (!this.methods) await this.getFileDescriptorsSet(this.address); + }; + public callViewMethod: AElfCallViewMethod = async (functionName, paramsOption) => { + const contract = this.contract; + if (!contract) return { error: { code: 401, message: 'Contract init error1' } }; + try { + await this.checkMethods(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + + const req = await contract.callViewMethod(functionNameUpper, transformArrayToMap(inputType, paramsOption)); + console.log(req, transformArrayToMap(inputType, paramsOption), functionNameUpper, '=======callViewMethod'); + + if (req.error) return req; + + return req?.data || req; + } catch (e) { + return { error: e }; + } + }; + + public callSendMethod: AElfCallSendMethod = async (functionName, paramsOption, sendOptions) => { + const contract = this.contract; + if (!contract) return { error: { code: 401, message: 'Contract init error2' } }; + const { onMethod = 'receipt', ...options } = sendOptions || {}; + try { + await this.checkMethods(); + const functionNameUpper = functionName.replace(functionName[0], functionName[0].toLocaleUpperCase()); + const inputType = this.methods[functionNameUpper]; + + console.log(transformArrayToMap(inputType, paramsOption), paramsOption, functionNameUpper, '=callSendMethod'); + const req = await contract.callSendMethod(functionNameUpper, '', transformArrayToMap(inputType, paramsOption), { + onMethod, + ...options, + }); + if (req.error) return req; + return req?.data || req; + } catch (error: any) { + if (error.message) return { error }; + return { error: { message: error.Error || error.Status } }; + } + }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public callSendPromiseMethod: CallSendMethod = async (_functionName, _account, _paramsOption, _sendOptions) => { + throw new Error('Method not implemented.'); + }; +} diff --git a/src/utils/converter.ts b/src/utils/converter.ts new file mode 100644 index 00000000..d65b4eb1 --- /dev/null +++ b/src/utils/converter.ts @@ -0,0 +1,178 @@ +import BigNumber from 'bignumber.js'; + +const options = { + zh: { + list: [ + { value: 1e12, symbol: '萬億' }, + { value: 1e8, symbol: '億' }, + { value: 1e4, symbol: '萬' }, + ], + lastValue: 1e4, + }, + en: { + list: [ + { value: 1e12, symbol: 'T' }, + { value: 1e9, symbol: 'B' }, + { value: 1e6, symbol: 'M' }, + // { value: 1e3, symbol: 'K' }, + ], + lastValue: 1e3, + }, +}; +export const fixedDecimal = ({ + num, + decimals = 4, + minDecimals = 8, +}: { + num?: number | BigNumber | string; + decimals?: number; + minDecimals?: number; +}) => { + const bigCount = BigNumber.isBigNumber(num) ? num : new BigNumber(num || ''); + if (bigCount.isNaN()) return '0'; + const dpCount = bigCount.dp(decimals, BigNumber.ROUND_DOWN); + if (dpCount.gt(0)) return dpCount.toFixed(); + return bigCount.dp(minDecimals, BigNumber.ROUND_DOWN).toFixed(); +}; + +export const fixedDecimalToFormat = ({ + num, + decimals = 4, + minDecimals = 8, +}: { + num?: number | BigNumber | string; + decimals?: number; + minDecimals?: number; +}) => { + const bigCount = BigNumber.isBigNumber(num) ? num : new BigNumber(num || ''); + if (bigCount.isNaN()) return '0'; + const dpCount = bigCount.dp(decimals, BigNumber.ROUND_DOWN); + if (dpCount.gt(0)) return dpCount.toFormat(); + return bigCount.dp(minDecimals, BigNumber.ROUND_DOWN).toFormat(); +}; + +type Num = number | BigNumber | string; +export const unitConverter = ( + ags: + | { + num?: Num; + decimals?: number; + defaultVal?: string; + minDecimals?: number; + skipConverterIndex?: number; + } + | Num + | undefined, +) => { + let obj: any = {}; + if (!BigNumber.isBigNumber(ags) && typeof ags === 'object') { + obj = ags; + } else { + obj.num = ags; + } + const { num, decimals = 4, defaultVal = '0', minDecimals = 8, skipConverterIndex = 0 } = obj; + const bigNum = BigNumber.isBigNumber(num) ? num : new BigNumber(num || ''); + if (bigNum.isNaN() || bigNum.eq(0)) return defaultVal; + const abs = bigNum.abs(); + const { list, lastValue } = options.en; + if (abs.gt(lastValue)) { + for (let i = 0; i < list.length - skipConverterIndex; i++) { + const { value, symbol } = list[i]; + if (abs.gt(value)) + return ( + fixedDecimal({ + num: bigNum.div(value), + decimals, + minDecimals, + }) + symbol + ); + } + } + return fixedDecimal({ + num: bigNum, + decimals, + minDecimals, + }); +}; +export const unitConverterToFormat = ( + ags: + | { + num?: Num; + decimals?: number; + defaultVal?: string; + minDecimals?: number; + } + | Num + | undefined, +) => { + let obj: any = {}; + if (!BigNumber.isBigNumber(ags) && typeof ags === 'object') { + obj = ags; + } else { + obj.num = ags; + } + const { num, decimals = 4, defaultVal = '0', minDecimals = 8 } = obj; + const bigNum = BigNumber.isBigNumber(num) ? num : new BigNumber(num || ''); + if (bigNum.isNaN() || bigNum.eq(0)) return defaultVal; + const abs = bigNum.abs(); + const { list, lastValue } = options.en; + if (abs.gt(lastValue)) { + for (let i = 0; i < list.length; i++) { + const { value, symbol } = list[i]; + if (abs.gt(value)) + return ( + fixedDecimalToFormat({ + num: bigNum.div(value), + decimals, + minDecimals, + }) + symbol + ); + } + } + return fixedDecimalToFormat({ + num: bigNum, + decimals, + minDecimals, + }); +}; + +export const getShareOfPool = (num?: BigNumber.Value, total?: BigNumber.Value) => { + const bigNum = BigNumber.isBigNumber(num) ? num : new BigNumber(num || ''); + if (!total) return '0.00'; + const newtotal = bigNum.plus(total); + return percentConverter(bigNum.div(newtotal)); +}; + +export const percentConverter = (num: BigNumber.Value) => { + let bigNum = BigNumber.isBigNumber(num) ? num : new BigNumber(num || ''); + bigNum = bigNum.times(100); + + if (bigNum.isNaN() || bigNum.lt(0) || bigNum.toFixed() === 'Infinity') return '0.00'; + + return bigNum.gt(100) ? '100.00' : bigNum.toFixed(2); +}; + +export const showUSDConverter = ( + ags: + | { + num?: Num; + decimals?: number; + defaultVal?: string; + minDecimals?: number; + skipConverterIndex?: number; + } + | Num + | undefined, +) => { + let obj: any = {}; + if (!BigNumber.isBigNumber(ags) && typeof ags === 'object') { + obj = ags; + } else { + obj.num = ags; + } + const { num, defaultVal } = obj; + const bigNum = BigNumber.isBigNumber(num) ? num : new BigNumber(num || ''); + + if (bigNum.isNaN() || bigNum.lte(0)) return defaultVal || '--'; + return `$${unitConverter(obj)}`; +}; diff --git a/src/utils/crossChain.ts b/src/utils/crossChain.ts new file mode 100644 index 00000000..1bae5743 --- /dev/null +++ b/src/utils/crossChain.ts @@ -0,0 +1,456 @@ +import { SupportedELFChainId } from 'constants/chain'; +import { ChainId, TokenInfo } from 'types'; +import { CrossChainItem } from 'types/api'; +import { encodeTransaction, getAElf, uint8ArrayToHex } from './aelfUtils'; +import { base58ToChainId, getChainIdToMap } from './chain'; +import type { ContractBasic } from './contract'; +import AElf from 'aelf-sdk'; +import { AElfTransaction, TransactionResult } from '@aelf-react/types'; +import { checkApprove } from 'contracts'; +import { CrossFeeToken, REQ_CODE, ZERO } from 'constants/misc'; +import { getTokenInfoByWhitelist } from './whitelist'; +import { timesDecimals } from './calculate'; +import { formatAddress, isIncludesChainId, isTonChain } from 'utils'; +import { FormatTokenList } from 'constants/index'; +import { LimitDataProps } from 'page-components/Bridge/useLimitAmountModal/constants'; +import BigNumber from 'bignumber.js'; +import CommonMessage from 'components/CommonMessage'; +import { handleErrorMessage } from './error'; +export async function CrossChainTransfer({ + contract, + account, + to, + token, + toChainId, + amount, +}: { + contract: ContractBasic; + account: string; + to: string; + token: TokenInfo; + toChainId: ChainId; + amount: string; +}) { + console.log('CrossChainTransfer', '===CrossChainTransfer'); + + return contract.callSendMethod( + 'CrossChainTransfer', + account, + [to, token.symbol, amount, ' ', base58ToChainId(toChainId), token.issueChainId], + { onMethod: 'receipt' }, + ); +} + +async function getMerklePath(sendInstance: any, transferTransactionId?: string | null) { + const merklePathByTxId = await sendInstance.chain.getMerklePathByTxId(transferTransactionId); + const merklePath = { + merklePathNodes: [...merklePathByTxId.MerklePathNodes], + }; + merklePath.merklePathNodes = merklePath.merklePathNodes.map((item) => ({ + hash: { + value: Buffer.from(item.Hash, 'hex').toString('base64'), + }, + isLeftChildNode: item.IsLeftChildNode, + })); + return { + merklePath, + }; +} +async function getBoundParentChainHeightAndMerklePathByHeight( + sendCrossChainContract: ContractBasic, + crossTransferTxBlockHeight?: number | null, +) { + const req = await sendCrossChainContract.callViewMethod('GetBoundParentChainHeightAndMerklePathByHeight', [ + crossTransferTxBlockHeight, + ]); + const { merklePathFromParentChain, boundParentChainHeight } = req; + + merklePathFromParentChain.merklePathNodes = merklePathFromParentChain.merklePathNodes.map((item: any) => ({ + hash: { + value: Buffer.from(item.hash, 'hex').toString('base64'), + }, + isLeftChildNode: item.isLeftChildNode, + })); + return { + merklePathFromParentChain, + boundParentChainHeight, + }; +} + +async function signTransaction({ + Transaction, + sendTokenContract, +}: { + Transaction: AElfTransaction; + sendTokenContract: ContractBasic; +}): Promise { + const { Params, From, To, MethodName, RefBlockNumber, RefBlockPrefix, Signature } = Transaction; + const encoded = await sendTokenContract.encodedTx(MethodName, JSON.parse(Params)); + const raw = AElf.pbUtils.getTransaction(From, To, MethodName, encoded); + raw.refBlockNumber = RefBlockNumber; + raw.refBlockPrefix = Uint8Array.from(Buffer.from(RefBlockPrefix, 'base64')); + // raw.signature = await getSignatureByBridge( + // ELFChainConstants.aelfInstances?.AELF, + // '2BC7WWMNBp4LjmJ48VAfDocEU2Rjg5yhELxT2HewfYxPPrdxA9', + // Buffer.from(encodeTransaction(raw)).toString('hex'), + // ); + + raw.signature = Buffer.from(Signature, 'base64').toString('hex'); + + let tx = encodeTransaction(raw); + if (tx instanceof Buffer) { + tx = tx.toString('hex'); + } else { + tx = uint8ArrayToHex(tx); + } + return tx; +} + +export async function CrossChainReceive({ + sendChainID, + receiveItem, + sendCrossChainContract, + sendTokenContract, + receiveTokenContract, +}: { + sendChainID: ChainId; + receiveItem: CrossChainItem; + sendCrossChainContract: ContractBasic; + sendTokenContract: ContractBasic; + receiveTokenContract: ContractBasic; +}) { + const { transferTransactionId, fromChainId } = receiveItem; + const sendInstance = getAElf(sendChainID); + const crossTransferTxInfo: TransactionResult = await sendInstance.chain.getTxResult(transferTransactionId); + const { BlockNumber, Transaction } = crossTransferTxInfo; + + const { merklePath } = await getMerklePath(sendInstance, transferTransactionId); + let parentChainHeight = BlockNumber; + if (sendChainID !== SupportedELFChainId.AELF) { + const { merklePathFromParentChain, boundParentChainHeight } = await getBoundParentChainHeightAndMerklePathByHeight( + sendCrossChainContract, + BlockNumber, + ); + parentChainHeight = boundParentChainHeight; + merklePath.merklePathNodes = [...merklePath.merklePathNodes, ...merklePathFromParentChain.merklePathNodes]; + } + const tx = await signTransaction({ + Transaction, + sendTokenContract, + }); + + return receiveTokenContract.callSendMethod('CrossChainReceiveToken', '', { + fromChainId: base58ToChainId(fromChainId as any), + parentChainHeight, + transferTransactionBytes: Buffer.from(tx, 'hex').toString('base64'), + merklePath, + }); +} +export function ValidateTokenInfoExists({ + contract, + tokenInfo, + account, +}: { + contract: ContractBasic; + tokenInfo: TokenInfo; + account: string; +}) { + return contract.callSendMethod('ValidateTokenInfoExists', account, tokenInfo); +} + +export async function CrossChainCreateToken({ + sendChainID, + transactionId, + sendCrossChainContract, + sendTokenContract, + receiveTokenContract, +}: { + sendChainID: ChainId; + transactionId: string; + sendCrossChainContract: ContractBasic; + sendTokenContract: ContractBasic; + receiveTokenContract: ContractBasic; +}) { + const sendInstance = getAElf(sendChainID); + const txInfo: TransactionResult = await sendInstance.chain.getTxResult(transactionId); + const { BlockNumber, Transaction } = txInfo; + + const { merklePath } = await getMerklePath(sendInstance, transactionId); + let parentChainHeight = BlockNumber; + + if (sendChainID !== SupportedELFChainId.AELF) { + const { merklePathFromParentChain, boundParentChainHeight } = await getBoundParentChainHeightAndMerklePathByHeight( + sendCrossChainContract, + BlockNumber, + ); + parentChainHeight = boundParentChainHeight; + merklePath.merklePathNodes = [...merklePath.merklePathNodes, ...merklePathFromParentChain.merklePathNodes]; + } + const tx = await signTransaction({ + Transaction, + sendTokenContract, + }); + + return receiveTokenContract.callSendMethod('CrossChainCreateToken', '', { + fromChainId: base58ToChainId(sendChainID as any), + parentChainHeight, + transactionBytes: Buffer.from(tx, 'hex').toString('base64'), + merklePath, + }); +} + +export async function CreateReceipt({ + fromToken, + account, + bridgeContract, + amount, + fromChainId, + toChainId, + to, + tokenContract, + crossFee, +}: { + bridgeContract: ContractBasic; + fromToken: string; + account: string; + amount: string; + fromChainId: ChainId; + toChainId: ChainId; + to: string; + tokenContract: ContractBasic; + crossFee?: string; +}) { + let toAddress = to; + if (bridgeContract.contractType !== 'ELF') toAddress = formatAddress(to); + if (bridgeContract.contractType === 'ERC') + toAddress = '0x' + Buffer.from(AElf.utils.base58.decode(toAddress)).toString('hex'); + const fromTonChain = bridgeContract.contractType === 'TON'; + const fromELFChain = bridgeContract.contractType === 'ELF'; + if (fromELFChain && fromToken !== CrossFeeToken) { + const req = await checkApprove( + fromChainId, + CrossFeeToken, + account, + bridgeContract.address || '', + timesDecimals(crossFee, 8).toFixed(0), + undefined, + fromELFChain ? tokenContract : undefined, + ); + if (req !== REQ_CODE.Success) throw req; + } + if (!fromTonChain) { + let checkAmount = amount; + if (fromToken === CrossFeeToken) { + if (crossFee) { + // fee ELF decimals 8 + crossFee = timesDecimals(crossFee, 8).toFixed(0); + } + checkAmount = ZERO.plus(amount) + .plus(crossFee || 0) + .toFixed(0); + } + const req = await checkApprove( + fromChainId, + fromToken, + account, + bridgeContract.address || '', + checkAmount, + undefined, + fromELFChain ? tokenContract : undefined, + ); + if (req !== REQ_CODE.Success) throw req; + } + console.log( + fromELFChain, + toChainId, + [fromToken, account, toAddress, amount, getChainIdToMap(toChainId), isTonChain(toChainId) ? 1 : 0], + '======toChainId', + ); + if (fromELFChain) { + return bridgeContract.callSendMethod('createReceipt', account, [ + fromToken, + account, + toAddress, + amount, + getChainIdToMap(toChainId), + isTonChain(toChainId) ? 1 : 0, + ]); + } + return bridgeContract.callSendMethod( + 'createReceipt', + account, + [fromToken, amount, getChainIdToMap(toChainId), toAddress], + { + onMethod: 'transactionHash', + }, + ); +} + +export async function LockToken({ + account, + bridgeContract, + amount, + toChainId, + to, +}: { + bridgeContract: ContractBasic; + account: string; + amount: string; + toChainId: ChainId; + to: string; +}) { + if (to) to = '0x' + Buffer.from(AElf.utils.base58.decode(formatAddress(to))).toString('hex'); + return bridgeContract.callSendMethod('createNativeTokenReceipt', account, [getChainIdToMap(toChainId), to], { + onMethod: 'transactionHash', + value: amount, + }); +} + +export async function SwapToken({ + bridgeOutContract, + toAccount, + receiveItem, +}: { + receiveItem: CrossChainItem; + bridgeOutContract: ContractBasic; + toAccount: string; +}) { + const { transferAmount, receiptId, toAddress, transferToken, toChainId, fromChainId } = receiveItem || {}; + if (!(toChainId && transferToken?.symbol)) return; + let toSymbol = transferToken.symbol; + const item = FormatTokenList.find( + (i) => + i.fromSymbol === transferToken.symbol && + isIncludesChainId(i.fromChainId, fromChainId) && + isIncludesChainId(i.toChainId, toChainId), + ); + if (item) toSymbol = item.toSymbol; + + const { address } = getTokenInfoByWhitelist(toChainId, toSymbol) || {}; + const originAmount = timesDecimals(transferAmount, transferToken.decimals).toFixed(); + const chainId = getChainIdToMap(fromChainId); + + let swapId; + if (bridgeOutContract.contractType === 'ELF') { + swapId = await bridgeOutContract?.callViewMethod('GetSwapIdByToken', [ + chainId, + getTokenInfoByWhitelist(toChainId, toSymbol)?.symbol, + ]); + } else { + swapId = await bridgeOutContract?.callViewMethod('getSwapId', [address, chainId]); + } + + if (swapId.error) return swapId; + + return bridgeOutContract?.callSendMethod('swapToken', toAccount, [swapId, receiptId, originAmount, toAddress]); +} + +export async function getSwapId({ + bridgeOutContract, + toChainId, + fromChainId, + symbol, +}: { + bridgeOutContract?: ContractBasic; + toChainId?: ChainId; + fromChainId?: ChainId; + symbol?: string; +}) { + if (!bridgeOutContract || !toChainId || !fromChainId || !symbol) { + return; + } + + let swapId; + const { address } = getTokenInfoByWhitelist(toChainId, symbol) || {}; + const chainId = getChainIdToMap(fromChainId); + + if (bridgeOutContract?.contractType === 'ELF') { + swapId = await bridgeOutContract?.callViewMethod('GetSwapIdByToken', [chainId, symbol]); + } else { + swapId = await bridgeOutContract?.callViewMethod('getSwapId', [address, chainId]); + } + + return swapId; +} + +export async function getReceiptLimit({ + limitContract, + toChainId, + fromChainId, + fromSymbol, +}: { + limitContract?: ContractBasic; + toChainId?: ChainId; + fromChainId?: ChainId; + fromSymbol?: string; +}): Promise { + if (!limitContract || !toChainId || !fromChainId || !fromSymbol) { + return; + } + + const { address } = getTokenInfoByWhitelist(fromChainId, fromSymbol) || {}; + try { + const [receiptDailyLimit, receiptTokenBucket] = await Promise.all([ + limitContract?.callViewMethod('getReceiptDailyLimit', [address, getChainIdToMap(toChainId)]), + limitContract?.callViewMethod('getCurrentReceiptTokenBucketState', [address, getChainIdToMap(toChainId)]), + ]); + + if (receiptDailyLimit.error || receiptTokenBucket.error) { + throw receiptDailyLimit.error || receiptTokenBucket.error; + } + + const isEnable = receiptTokenBucket.isEnabled; + + return { + remain: new BigNumber(receiptDailyLimit.tokenAmount), + maxCapcity: new BigNumber(receiptTokenBucket.tokenCapacity), + currentCapcity: new BigNumber(receiptTokenBucket.currentTokenAmount), + fillRate: new BigNumber(receiptTokenBucket.rate), + isEnable, + }; + } catch (error: any) { + CommonMessage.error(handleErrorMessage(error)); + console.log('getReceiptLimit error :', error); + } +} + +export async function getSwapLimit({ + limitContract, + fromChainId, + swapId, + toChainId, + toSymbol, +}: { + limitContract?: ContractBasic; + fromChainId?: ChainId; + toChainId?: ChainId; + swapId?: string; + toSymbol?: string; +}): Promise { + if (!limitContract || !toChainId || !toSymbol || !fromChainId || !swapId) { + return; + } + + const { address } = getTokenInfoByWhitelist(toChainId, toSymbol) || {}; + try { + const [swapDailyLimit, swapTokenBucket] = await Promise.all([ + limitContract?.callViewMethod('getSwapDailyLimit', [swapId]), + limitContract?.callViewMethod('getCurrentSwapTokenBucketState', [address, getChainIdToMap(fromChainId)]), + ]); + + if (swapDailyLimit.error || swapTokenBucket.error) { + throw swapDailyLimit.error || swapTokenBucket.error; + } + + return { + remain: new BigNumber(swapDailyLimit.tokenAmount), + maxCapcity: new BigNumber(swapTokenBucket.tokenCapacity), + currentCapcity: new BigNumber(swapTokenBucket.currentTokenAmount), + fillRate: new BigNumber(swapTokenBucket.rate), + isEnable: swapTokenBucket.isEnabled, + }; + } catch (error: any) { + CommonMessage.error(handleErrorMessage(error)); + console.log('getSwapLimit error :', error); + } +} diff --git a/src/utils/eBridgeEventBus.ts b/src/utils/eBridgeEventBus.ts new file mode 100644 index 00000000..1744e379 --- /dev/null +++ b/src/utils/eBridgeEventBus.ts @@ -0,0 +1,52 @@ +import EventEmitter from 'events'; + +export const eventBus = new EventEmitter(); + +const EventList = [ + 'Unauthorized', + 'AelfLoginSuccess', + 'AelfLogoutSuccess', + 'AelfAuthTokenSuccess', + 'GlobalLoading', +] as const; + +// eslint-disable-next-line no-new-func +const eventsServer = new Function(); + +eventsServer.prototype.parseEvent = function (name: string, eventMap: string[]) { + const obj: any = (this[name] = {}); + eventMap.forEach((item) => { + const eventName = item.toLocaleUpperCase(); + obj[item] = { + emit: this.emit.bind(this, eventName), + addListener: this.addListener.bind(this, eventName), + name: eventName, + }; + }); +}; + +eventsServer.prototype.emit = function (eventType: string, ...params: any[]) { + eventBus.emit(eventType, ...params); +}; +eventsServer.prototype.addListener = function (eventType: string, listener: (data: any) => void) { + const cListener = eventBus.addListener(eventType, listener); + return { ...cListener, remove: () => eventBus.removeListener(eventType, listener) }; +}; + +eventsServer.prototype.parseEvent('base', EventList); + +export type TEBridgeEventEmitter = { + remove: () => void; +} & EventEmitter; + +export type TEBridgeEventBusTypes = { + [x in (typeof EventList)[number]]: { + emit: (...params: any[]) => void; + addListener: (listener: (data: any) => void) => TEBridgeEventEmitter; + name: string; + }; +}; + +const eBridgeEventBus = { ...eventsServer.prototype.base }; + +export default eBridgeEventBus as unknown as TEBridgeEventBusTypes; diff --git a/src/utils/eBridgeInstance.ts b/src/utils/eBridgeInstance.ts new file mode 100644 index 00000000..113427a0 --- /dev/null +++ b/src/utils/eBridgeInstance.ts @@ -0,0 +1,19 @@ +export class EBridgeInstance { + public unauthorized: boolean; + public obtainingSignature: boolean; + + constructor() { + this.unauthorized = false; + this.obtainingSignature = false; + } + + setUnauthorized(status: boolean) { + this.unauthorized = status; + } + + setObtainingSignature(status: boolean) { + this.obtainingSignature = status; + } +} + +export const eBridgeInstance = new EBridgeInstance(); diff --git a/src/utils/error.ts b/src/utils/error.ts new file mode 100644 index 00000000..eb143760 --- /dev/null +++ b/src/utils/error.ts @@ -0,0 +1,50 @@ +export const handleError = (error: any) => { + return error?.error || error; +}; + +export function handleContractError(error?: any, req?: any) { + if (typeof error === 'string') return { message: error }; + + // EVM contract error by wagmi + if (error.shortMessage || error.details) { + return { + message: error.shortMessage || error.details, + }; + } + + if (error?.message) return error; + if (error?.Error) { + return { + message: error.Error.Details || error.Error.Message || error.Error, + code: error.Error.Code, + }; + } + return { + code: req?.error?.message?.Code || req?.error, + message: req?.errorMessage?.message || req?.error?.message?.Message, + }; +} + +export const handleErrorMessage = (error: any, errorText?: string) => { + if (error?.status === 500) { + return errorText || 'Failed to fetch data'; + } + error = handleError(error); + error = handleContractError(error); + if (typeof error.message === 'string') errorText = error.message; + return errorText || ''; +}; + +export const handleWebLoginErrorMessage = (error: any, errorText?: string) => { + if (error.nativeError) { + error = error.nativeError; + } + + return handleErrorMessage(error, errorText); +}; + +export const handleListingErrorMessage = (error: any, errorText?: string) => { + const _error = error?.response?.data?.error; + if (_error && typeof _error.message === 'string') errorText = _error.message; + return errorText || ''; +}; diff --git a/src/utils/events.ts b/src/utils/events.ts new file mode 100644 index 00000000..c99b844a --- /dev/null +++ b/src/utils/events.ts @@ -0,0 +1,3 @@ +import eBridgeEventBus from 'utils/eBridgeEventBus'; + +export const emitLoading = (isLoading: boolean) => eBridgeEventBus.GlobalLoading.emit(isLoading); diff --git a/src/utils/format.ts b/src/utils/format.ts new file mode 100644 index 00000000..97c22dfc --- /dev/null +++ b/src/utils/format.ts @@ -0,0 +1,72 @@ +import BigNumber from 'bignumber.js'; +import { divDecimals } from './calculate'; +import { ZERO } from 'constants/misc'; + +export const parseWithCommas = (value?: string | null) => { + return value ? new BigNumber(value.replace(/,/g, '')).toFixed() : ''; +}; + +export const parseWithStringCommas = (value?: string | null) => { + return value ? value.replace(/,/g, '') : ''; +}; + +export const replaceCharacter = (str: string, replaced: string, replacedBy: string) => { + return str?.replace(replaced, replacedBy); +}; + +export const formatSymbolDisplay = (str: string) => { + if (!str) return ''; + + // Prevent malicious tampering of the token display issued by users + if (str?.includes('SGR-1')) return replaceCharacter(str, '-1', ''); + + return str; +}; + +export const formatListWithAnd = (items: string[]): string => { + if (items.length > 1) { + const lastItem = items.pop(); + return `${items.join(', ')} and ${lastItem}`; + } + return items.join(', '); +}; + +export enum AmountSign { + PLUS = '+', + MINUS = '-', + USD = '$', + EMPTY = '', +} + +export interface IFormatWithCommasProps { + amount?: string | number; + decimals?: string | number; + digits?: number; + sign?: AmountSign; +} +export const DEFAULT_AMOUNT = 0; +export const DEFAULT_DECIMAL = 6; +export const DEFAULT_DIGITS = 6; +/** + * formatAmount with prefix and thousand mark, not unit + * @example $11.1 +11.1 -11.1 9,999.9 + */ +export function formatWithCommas({ + amount = DEFAULT_AMOUNT, + decimals, + digits = DEFAULT_DIGITS, + sign = AmountSign.EMPTY, +}: IFormatWithCommasProps): string { + const decimal = decimals || 0; + const splitList = (typeof amount === 'number' ? amount.toString() : amount).split('.'); + + const afterPoint = splitList[1]; + const amountTrans = + `${divDecimals(ZERO.plus(splitList[0]), decimal).decimalPlaces(digits).toFormat()}` + + `${afterPoint ? '.' + afterPoint : ''}`; + + if (sign && amountTrans !== '0') { + return `${sign}${amountTrans}`; + } + return amountTrans; +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 00000000..d709f566 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,204 @@ +import { SupportedChainId, SupportedELFChainId, SupportedTONChainId } from 'constants/chain'; +import { ELFChainConstants, ERCChainConstants } from 'constants/ChainConstants'; +import { AelfInstancesKey, ChainId, TokenInfo } from 'types'; +import { isELFChain } from './aelfUtils'; +import { getAddress } from '@ethersproject/address'; +import AElf from 'aelf-sdk'; +import { BRIDGE_TOKEN_MAP, NATIVE_TOKEN_LIST, SupportedExternalChain } from 'constants/index'; +import { isTonAddress } from './ton'; +import BigNumber from 'bignumber.js'; + +export const sleep = (time: number) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve('sleep'); + }, time); + }); +}; +export function getExploreLink( + data: string, + type: 'transaction' | 'token' | 'address' | 'block', + chainId?: ChainId, +): string { + let prefix; + if (isELFChain(chainId)) { + prefix = ELFChainConstants.constants[chainId as AelfInstancesKey]?.CHAIN_INFO?.exploreUrl; + } else { + prefix = + SupportedExternalChain?.[chainId as AelfInstancesKey]?.CHAIN_INFO?.exploreUrl || + ERCChainConstants.constants.CHAIN_INFO.exploreUrl; + } + const isTON = isTonChain(chainId); + switch (type) { + case 'transaction': { + if (isTON) return `${prefix}transaction/${data}`; + return `${prefix}tx/${data}`; + } + case 'token': { + return `${prefix}token/${data}`; + } + case 'block': { + return `${prefix}block/${data}`; + } + case 'address': + default: { + // TON + if (isTON) return `${prefix}${data}`; + return `${prefix}address/${data}`; + } + } +} +export function shortenAddress(address: string | null, chars = 4, end = 42): string { + const parsed = address; + if (!parsed) throw Error(`Invalid 'address' parameter '${address}'.`); + return `${parsed.substring(0, chars + 2)}...${parsed.substring(end - chars)}`; +} + +export function shortenString(address: string | null, leftChars = 10, rightChars?: number): string { + const parsed = address; + if (!parsed) return ''; + if (!rightChars) { + rightChars = leftChars; + } + return `${parsed.substring(0, leftChars)}...${parsed.substring(parsed.length - rightChars)}`; +} + +export function unityTokenInfo(tokenInfo?: TokenInfo) { + if (!tokenInfo) return; + return { + decimals: tokenInfo.decimals, + symbol: tokenInfo.symbol, + tokenName: tokenInfo.tokenName, + address: tokenInfo.address, + issueChainId: tokenInfo.issueChainId, + issuer: tokenInfo.issuer, + isBurnable: tokenInfo.isBurnable, + totalSupply: tokenInfo.totalSupply, + }; +} +type Network = 'ethereum' | 'binance' | 'kovan' | 'AELF' | 'tDVV' | 'tDVW'; + +function chainIdToNetworkName(chainId?: ChainId): Network { + switch (chainId) { + // case SupportedChainId.MAINNET: + // return 'ethereum'; + // case SupportedChainId.BSC_MAINNET: + // return 'binance'; + case SupportedChainId.KOVAN: + case SupportedELFChainId.AELF: + case SupportedELFChainId.tDVV: + case SupportedELFChainId.tDVW: + case SupportedChainId.SEPOLIA: + case SupportedChainId.BSC_TESTNET: + return 'AELF'; + default: + return 'AELF'; + } +} +// const networksWithNativeUrls: any = [ +// SupportedChainId.KOVAN, +// SupportedChainId.GORELI, +// SupportedELFChainId.AELF, +// SupportedELFChainId.tDVV, +// SupportedELFChainId.tDVW, +// SupportedChainId.BSC_TESTNET, +// SupportedChainId.SEPOLIA, +// ]; + +export const getTokenLogoURL = (address?: string | string, chainId?: ChainId) => { + if (!address) return ''; + const networkName = chainIdToNetworkName(chainId); + // const repositories = 'trustwallet'; + const repositories = 'eBridgeCrosschain'; + // if (networksWithNativeUrls.includes(chainId)) repositories = 'eBridgeCrosschain'; + return `https://raw.githubusercontent.com/${repositories}/assets/master/blockchains/${networkName}/assets/${address}/logo.png`; +}; + +export const enumToMap = (v: object) => { + const newMap: any = {}; + Object.entries(v).forEach(([index, value]) => { + newMap[index] = value; + newMap[value] = index; + }); + return newMap; +}; + +export function isERCAddress(value: string) { + try { + return !!getAddress(value); + } catch { + return false; + } +} +export const isELFAddress = (value: string) => { + if (/[\u4e00-\u9fa5]/.test(value)) return false; + try { + return !!AElf.utils.decodeAddressRep(value); + } catch { + return false; + } +}; + +export function isAddress(value?: string, chainId?: ChainId) { + return isChainAddress(value, chainId); +} + +export function isChainAddress(address?: string, chainId?: ChainId) { + if (!address || !chainId) { + return false; + } + console.log(isTonChain(chainId), '====isTonChain(chainId)'); + + if (isTonChain(chainId)) return isTonAddress(address); + + if (!isELFChain(chainId)) { + return isERCAddress(address); + } + const reg = new RegExp(`${chainId}$`); + if (!reg.test(address)) { + return false; + } + + return isELFAddress(address); +} + +export function formatAddress(value: string) { + const reg = /.*_([a-zA-Z0-9]*)_.*/; + return value.replace(reg, '$1'); +} + +export function formatNativeToken(symbol?: string) { + if (!symbol) return symbol; + if (NATIVE_TOKEN_LIST.includes(symbol)) return symbol.slice(1); + if (BRIDGE_TOKEN_MAP[symbol]) return BRIDGE_TOKEN_MAP[symbol]; + return symbol; +} + +export function isIncludesChainId(list: ChainId[] | ChainId, chainId?: ChainId) { + if (!chainId) return false; + return Array.isArray(list) ? list.includes(chainId) : chainId === list; +} + +export const isTonChain = (chainId?: ChainId) => { + return ( + (typeof chainId === 'number' && chainId === SupportedTONChainId.TESTNET) || chainId === SupportedTONChainId.MAINNET + ); +}; + +export const isBase64 = (str: string) => { + try { + return Buffer.from(str, 'base64').toString('base64') === str; + } catch (error) { + return false; + } +}; + +export const base64ToHexStr = (str?: string) => { + if (!str) return; + return Buffer.from(str, 'base64').toString('hex'); +}; + +export const isEffectiveNumber = (v: any) => { + const val = new BigNumber(v); + return !val.isNaN() && !val.lte(0); +}; diff --git a/src/utils/input.ts b/src/utils/input.ts new file mode 100644 index 00000000..06020004 --- /dev/null +++ b/src/utils/input.ts @@ -0,0 +1,73 @@ +import BigNumber from 'bignumber.js'; +import { sleep } from 'utils'; +import { TelegramPlatform } from 'utils/telegram/telegram'; +import { isAndroid } from 'utils/isMobile'; +import { isELFChain } from 'utils/aelfUtils'; +import { ZERO } from 'constants/misc'; +import { ChainId } from 'types'; + +export function parseInputChange(value: string, min?: BigNumber, maxLength?: number) { + const pivot = new BigNumber(value); + if (pivot.gt(0)) { + if (min && min.gt(pivot)) return min.toFixed(); + const [, dec] = value.split('.'); + return (dec?.length || 0) >= (maxLength || 18) ? pivot.toFixed(maxLength || 18, 1) : value; + } + return value; +} + +export function parseMAXInputChange(value: string, max?: BigNumber, min?: BigNumber, maxLength?: number) { + const pivot = new BigNumber(value); + if (max && max.lt(pivot)) { + return max.toFixed() || ''; + } else if (pivot.gt(0)) { + if (min && min.gt(pivot)) return min.toFixed(); + const [, dec] = value.split('.'); + return (dec?.length || 0) >= (maxLength || 18) ? pivot.toFixed(maxLength || 18, 1) : value; + } + return value; +} + +export function sliceDecimals(value?: string, decimals?: number) { + if (!value) return ''; + const [integer, dec] = value.split('.'); + return integer + (value.includes('.') ? '.' : '') + (dec ? dec.slice(0, decimals) : ''); +} + +export function getMaxAmount({ + chainId, + symbol, + balance, + crossFee, +}: { + chainId?: ChainId; + symbol?: string; + balance?: BigNumber; + crossFee?: string; +}) { + let value = ZERO; + const isELF = isELFChain(chainId); + if (!isELF || symbol !== 'ELF') { + value = ZERO.plus(balance || '0'); + } else { + value = BigNumber.max( + ZERO.plus(balance || '0') + .minus(crossFee || '0') + .minus(0.0041), + 0, + ); + } + return value; +} + +export const handleInputFocus = async (id: string) => { + const _isAndroid = isAndroid(); + if (!TelegramPlatform.isTelegramPlatform() && _isAndroid) { + // The keyboard does not block the input box + await sleep(200); + document.getElementById(id)?.scrollIntoView({ + block: 'center', + behavior: 'smooth', + }); + } +}; diff --git a/src/utils/isMobile.ts b/src/utils/isMobile.ts new file mode 100644 index 00000000..ce8cb3b4 --- /dev/null +++ b/src/utils/isMobile.ts @@ -0,0 +1,192 @@ +const appleIphone = /iPhone/i; +const appleIpod = /iPod/i; +const appleTablet = /iPad/i; +const appleUniversal = /\biOS-universal(?:.+)Mac\b/i; +const androidPhone = /\bAndroid(?:.+)Mobile\b/i; // Match 'Android' AND 'Mobile' +const androidTablet = /Android/i; +const amazonPhone = /(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i; // Match 'Silk' AND 'Mobile' +const amazonTablet = /Silk/i; +const windowsPhone = /Windows Phone/i; +const windowsTablet = /\bWindows(?:.+)ARM\b/i; // Match 'Windows' AND 'ARM' +const otherBlackBerry = /BlackBerry/i; +const otherBlackBerry10 = /BB10/i; +const otherOpera = /Opera Mini/i; +const otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i; +const otherFirefox = /Mobile(?:.+)Firefox\b/i; // Match 'Mobile' AND 'Firefox' + +export type UserAgent = string; +export type Navigator = { + userAgent: string; + platform: string; + maxTouchPoints?: number; +}; + +const isAppleTabletOnIos13 = (navigator?: Navigator): boolean => { + return ( + typeof navigator !== 'undefined' && + navigator.platform === 'MacIntel' && + typeof navigator.maxTouchPoints === 'number' && + navigator.maxTouchPoints > 1 + ); +}; + +function createMatch(userAgent: UserAgent): (regex: RegExp) => boolean { + return (regex: RegExp): boolean => regex.test(userAgent); +} + +export type IsMobileResult = { + apple: { + phone: boolean; + ipod: boolean; + tablet: boolean; + universal: boolean; + device: boolean; + }; + amazon: { + phone: boolean; + tablet: boolean; + device: boolean; + }; + android: { + phone: boolean; + tablet: boolean; + device: boolean; + }; + windows: { + phone: boolean; + tablet: boolean; + device: boolean; + }; + other: { + blackberry: boolean; + blackberry10: boolean; + opera: boolean; + firefox: boolean; + chrome: boolean; + device: boolean; + }; + phone: boolean; + tablet: boolean; + any: boolean; +}; + +export type IsMobileParameter = UserAgent | Navigator; + +export default function isMobile(param?: IsMobileParameter): IsMobileResult { + let nav: Navigator = { + userAgent: '', + platform: '', + maxTouchPoints: 0, + }; + + if (!param && typeof navigator !== 'undefined') { + nav = { + userAgent: navigator.userAgent, + platform: navigator.platform, + maxTouchPoints: navigator.maxTouchPoints || 0, + }; + } else if (typeof param === 'string') { + nav.userAgent = param; + } else if (param && param.userAgent) { + nav = { + userAgent: param.userAgent, + platform: param.platform, + maxTouchPoints: param.maxTouchPoints || 0, + }; + } + + let userAgent = nav.userAgent; + + // Facebook mobile app's integrated browser adds a bunch of strings that + // match everything. Strip it out if it exists. + let tmp = userAgent.split('[FBAN'); + if (typeof tmp[1] !== 'undefined') { + userAgent = tmp[0]; + } + + // Twitter mobile app's integrated browser on iPad adds a "Twitter for + // iPhone" string. Same probably happens on other tablet platforms. + // This will confuse detection so strip it out if it exists. + tmp = userAgent.split('Twitter'); + if (typeof tmp[1] !== 'undefined') { + userAgent = tmp[0]; + } + + const match = createMatch(userAgent); + + const result: IsMobileResult = { + apple: { + phone: match(appleIphone) && !match(windowsPhone), + ipod: match(appleIpod), + tablet: !match(appleIphone) && (match(appleTablet) || isAppleTabletOnIos13(nav)) && !match(windowsPhone), + universal: match(appleUniversal), + device: + (match(appleIphone) || + match(appleIpod) || + match(appleTablet) || + match(appleUniversal) || + isAppleTabletOnIos13(nav)) && + !match(windowsPhone), + }, + amazon: { + phone: match(amazonPhone), + tablet: !match(amazonPhone) && match(amazonTablet), + device: match(amazonPhone) || match(amazonTablet), + }, + android: { + phone: (!match(windowsPhone) && match(amazonPhone)) || (!match(windowsPhone) && match(androidPhone)), + tablet: + !match(windowsPhone) && + !match(amazonPhone) && + !match(androidPhone) && + (match(amazonTablet) || match(androidTablet)), + device: + (!match(windowsPhone) && + (match(amazonPhone) || match(amazonTablet) || match(androidPhone) || match(androidTablet))) || + match(/\bokhttp\b/i), + }, + windows: { + phone: match(windowsPhone), + tablet: match(windowsTablet), + device: match(windowsPhone) || match(windowsTablet), + }, + other: { + blackberry: match(otherBlackBerry), + blackberry10: match(otherBlackBerry10), + opera: match(otherOpera), + firefox: match(otherFirefox), + chrome: match(otherChrome), + device: + match(otherBlackBerry) || + match(otherBlackBerry10) || + match(otherOpera) || + match(otherFirefox) || + match(otherChrome), + }, + any: false, + phone: false, + tablet: false, + }; + + result.any = result.apple.device || result.android.device || result.windows.device || result.other.device; + // excludes 'other' devices and ipods, targeting touchscreen phones + result.phone = result.apple.phone || result.android.phone || result.windows.phone; + result.tablet = result.apple.tablet || result.android.tablet || result.windows.tablet; + + return result; +} + +export function isMobileDevices() { + const isM = isMobile(); + return isM.apple.device || isM.android.device; +} + +export function isAndroid() { + const isM = isMobile(); + return isM.android.device; +} + +export function isIOS() { + const isM = isMobile(); + return isM.apple.device; +} diff --git a/src/utils/link.tsx b/src/utils/link.tsx new file mode 100644 index 00000000..b7b89f3d --- /dev/null +++ b/src/utils/link.tsx @@ -0,0 +1,19 @@ +import { ChainId } from 'types'; +import { getExploreLink, shortenString } from 'utils'; +import { isMobileDevices } from './isMobile'; + +export function getTXLink(txId: string, chainId: ChainId, shortenChars?: number) { + const target = isMobileDevices() ? '_self' : '_blank'; + return ( + + {shortenString(txId, shortenChars)} + + ); +} + +export function openWithBlank(url: string): void { + const newWindow = window.open(url, '_blank'); + if (newWindow) { + newWindow.opener = null; + } +} diff --git a/src/utils/listingApplication.ts b/src/utils/listingApplication.ts new file mode 100644 index 00000000..78c49fab --- /dev/null +++ b/src/utils/listingApplication.ts @@ -0,0 +1,28 @@ +import queryString from 'query-string'; +import { ROUTE_PATHS } from 'constants/link'; +import { ListingStep, LISTING_STEP_PATHNAME_MAP } from 'constants/listingApplication'; +import { TSearchParams } from 'types/listingApplication'; + +export const getListingUrl = (step: ListingStep, params?: TSearchParams) => { + let search; + if (params) { + switch (step) { + case ListingStep.TOKEN_INFORMATION: + search = queryString.stringify({ + symbol: params.symbol, + }); + break; + case ListingStep.SELECT_CHAIN: + search = queryString.stringify({ + symbol: params.symbol, + }); + break; + case ListingStep.INITIALIZE_TOKEN_POOL: + search = queryString.stringify({ + networks: params.networks, + }); + break; + } + } + return `${ROUTE_PATHS.LISTING_APPLICATION}${LISTING_STEP_PATHNAME_MAP[step]}${search ? '?' + search : ''}`; +}; diff --git a/src/utils/localStorage.tsx b/src/utils/localStorage.tsx new file mode 100644 index 00000000..1eebbb14 --- /dev/null +++ b/src/utils/localStorage.tsx @@ -0,0 +1,8 @@ +export const clearWCStorageByDisconnect = () => { + const localStorage = window.localStorage; + Object.keys(localStorage).map((item: string) => { + if (/^wc/.test(item)) { + localStorage.removeItem(item); + } + }); +}; diff --git a/src/utils/message.tsx b/src/utils/message.tsx new file mode 100644 index 00000000..49c692a8 --- /dev/null +++ b/src/utils/message.tsx @@ -0,0 +1,131 @@ +import { message as antMessage } from 'antd'; +import IconFont from 'components/IconFont'; +import i18n from 'i18next'; +import { Trans } from 'react-i18next'; +import { ChainId } from 'types'; +import { getTXLink } from './link'; +import writeText from 'copy-to-clipboard'; +import CommonMessage from 'components/CommonMessage'; + +// message Configuration duration 5s +antMessage.config({ + duration: 5, +}); + +export function txSuccess({ req, chainId, message }: { req: any; chainId: ChainId; message: string }) { + const { TransactionId } = req || {}; + if (TransactionId) { + copyTxMessage({ req, chainId, message }); + } else { + antMessage.success(message); + } + // TransactionId && antMessage.success(getTXLink(TransactionId, chainId)); +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function formatMessage(s: string, decimals?: number) { + try { + if (s.includes('Insufficient balance') && s.includes('Current balance')) { + s = s.replace('AElf.Sdk.CSharp.AssertionException:', ''); + // format message + const strList = s.split('.'); + // let currentBalance = '', + // symbol = ''; + // strList.forEach((i) => { + // if (i.includes('Current balance')) { + // const lastStrList = i.split(';'); + // currentBalance = lastStrList[lastStrList.length - 1].split(':')[1].trim(); + // } + // if (i.includes('Insufficient balance of')) symbol = i.replace('Insufficient balance of', '').trim(); + // }); + return strList[1] + strList[2]; + } + } catch (error) { + console.debug(error); + } + return s; +} + +export function txError({ + req, + chainId, + message, + decimals, +}: { + req: any; + chainId: ChainId; + message: string; + decimals?: number; +}) { + const { TransactionId, isTransactionHash } = req || {}; + if (isTransactionHash) { + const msg = i18n.t(message); + const txt = ( + + {msg}: + {getTXLink(TransactionId, chainId)} + + ); + TransactionId && CommonMessage.error(txt); + } else { + TransactionId && CommonMessage.error(getTXLink(TransactionId, chainId)); + } + + const s = req?.error?.message || message; + const text = decimals ? formatMessage(s, decimals) : s; + !isTransactionHash && + CommonMessage.error( + { + writeText(text); + }}> + {text} + , + ); +} + +export function txMessage({ + req, + chainId, + copy, + errorMessage = 'Transaction request failed', + successMessage = 'Transaction request submitted', + decimals, +}: { + req: any; + chainId: ChainId; + errorMessage?: string; + successMessage?: string; + copy?: boolean; + decimals?: number; +}) { + if (req.error) return txError({ req, chainId, message: i18n.t(errorMessage), decimals }); + (copy ? copyTxMessage : txSuccess)({ req, chainId, message: i18n.t(successMessage) }); +} + +export function copyTxMessage({ req, chainId, message }: { req: any; chainId: ChainId; message?: string }) { + const { TransactionId } = req || {}; + antMessage.open({ + content: ( + <> +
+ + {message} +
+
+ Transaction Id +
+ {getTXLink(TransactionId, chainId, 6)} + { + writeText(TransactionId); + antMessage.success(i18n.t('Copy Success').toString()); + }} + /> +
+
+ + ), + }); +} diff --git a/src/utils/network.ts b/src/utils/network.ts new file mode 100644 index 00000000..b1030adc --- /dev/null +++ b/src/utils/network.ts @@ -0,0 +1,163 @@ +import { Connector } from 'wagmi'; +import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from 'constants/chain'; +import storages from 'constants/storages'; +import { eventBus } from 'utils/eBridgeEventBus'; +import { isELFChain } from './aelfUtils'; +import { COINBASE_WALLET_ID, METAMASK_WALLET_ID, NetworkType, WALLET_CONNECT_ID } from 'types'; +import { ChainId } from 'types'; +import { NetworkList } from 'constants/index'; +import { switchChainByWagmi } from './wagmi'; + +type Info = { + chainId: number | string; + rpcUrls?: string[]; + chainName?: string; + nativeCurrency?: { + name: string; + symbol: string; + decimals: number; + }; + blockExplorerUrls?: string[]; + iconUrls?: string[]; +}; +/** + * Prompt the user to add RPC as a network on Metamask, or switch to RPC if the wallet is on a different network + * @returns {boolean} true if the setup succeeded, false otherwise + */ +export const switchNetwork = async (info: Info): Promise => { + let provider = window.ethereum; + console.log('====== ====== switchNetwork provider', provider); + try { + if (provider?.providerMap) { + for (const [key, value] of provider.providerMap) { + if (key === 'MetaMask' && value?.isMetaMask) provider = value; + } + } + } catch (error) { + console.log(error, '======error'); + } + + const { chainId, chainName, nativeCurrency, rpcUrls, blockExplorerUrls, iconUrls } = info; + if (typeof info.chainId === 'string') { + eventBus.emit(storages.userELFChainId, info.chainId); + return true; + } + eventBus.emit(storages.userERCChainId, info.chainId); + if (!provider?.request) { + console.error("Can't setup the RPC network on metamask because window.ethereum is undefined"); + return false; + } + try { + if (nativeCurrency && chainName) { + console.log('====== ====== switchNetwork wallet_addEthereumChain', { + chainId: `0x${chainId.toString(16)}`, + chainName, + nativeCurrency, + rpcUrls, + iconUrls, + blockExplorerUrls, + }); + await provider.request({ + method: 'wallet_addEthereumChain', + params: [ + { + chainId: `0x${chainId.toString(16)}`, + chainName, + nativeCurrency, + rpcUrls, + iconUrls, + blockExplorerUrls, + }, + ], + }); + } else { + console.log('====== ====== switchNetwork wallet_switchEthereumChain', { + chainId: `0x${chainId.toString(16)}`, + }); + await provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: `0x${chainId.toString(16)}` }], + }); + } + return true; + } catch (error) { + console.error('switchNetwork', error); + return false; + } +}; +export function isChainAllowed(connector: Connector, chainId: number) { + switch (connector.id) { + case METAMASK_WALLET_ID: + case COINBASE_WALLET_ID: + case WALLET_CONNECT_ID: + return ALL_SUPPORTED_CHAIN_IDS.includes(chainId); + default: + return false; + } +} + +function getChainIdFromFormattedString(item: string): number | null { + const splitItem = item.startsWith('eip155:') ? item.split(':') : []; + return splitItem.length > 1 && !isNaN(Number(splitItem[1])) ? Number(splitItem[1]) : null; +} +export function getSupportedChainIdsFromWalletConnectSession(session?: any): SupportedChainId[] { + if (!session?.namespaces) return []; + + const eip155Keys = Object.keys(session.namespaces); + const namespaces = Object.values(session.namespaces); + + // Collect all arrays into one for unified processing + const allItems = [ + ...eip155Keys, + ...namespaces.flatMap((namespace: any) => namespace.chains), + ...namespaces.flatMap((namespace: any) => namespace.accounts), + ]; + + // Process all items to extract chainIds + const allChainIds = allItems + .map((item) => { + if (typeof item === 'string') { + return getChainIdFromFormattedString(item); + } + // Check if the item is a number + return isNaN(Number(item)) ? null : Number(item); + }) + .filter((item) => item !== null); // Filter out any null values + + return Array.from(new Set(allChainIds)) as SupportedChainId[]; +} +export const switchChain = async ( + info: NetworkType['info'] & Info, + connector?: Connector | string, + isWeb3Active?: boolean, + web3ChainId?: ChainId, +) => { + const { chainId } = info; + if (typeof chainId === 'string') { + eventBus.emit(storages.userELFChainId, info.chainId); + return true; + } + if (!isELFChain(info.chainId) && web3ChainId === info.chainId) return; + eventBus.emit(storages.userERCChainId, info.chainId); + if (!connector || typeof connector === 'string') return; + if (isWeb3Active) { + console.log('====== ====== ====== 0', connector, chainId); + if (!isChainAllowed(connector, chainId)) { + throw new Error(`Chain ${chainId} not supported for connector (${typeof connector})`); + } else { + const addChainParameter = { + chainId: chainId, + }; + console.log('====== ====== ====== 4', addChainParameter); + await switchChainByWagmi(addChainParameter); + } + } else { + console.log('====== ====== ====== 5', info); + // unlink metamask + await switchNetwork(info); + } +}; + +export const getNetworkInfo = (chainId: ChainId) => { + return NetworkList.find((info) => info.info.chainId === chainId); +}; diff --git a/src/utils/pools.tsx b/src/utils/pools.tsx new file mode 100644 index 00000000..f3a84a37 --- /dev/null +++ b/src/utils/pools.tsx @@ -0,0 +1,140 @@ +import { ChainId, TokenInfo } from 'types'; +import { getTokenInfoByWhitelist } from './whitelist'; +import { timesDecimals } from './calculate'; +import { isELFChain } from './aelfUtils'; +import { ContractBasic } from './contract'; +import { checkApprove } from 'contracts'; +import { REQ_CODE } from 'constants/misc'; +import { getBalanceByWagmi, readContractByWagmi } from './wagmi'; +import { POOLS_ABI } from 'constants/abis'; + +export const addLiquidity = async ({ + symbol, + account, + amount, + chainId, + poolContract, + tokenContract, + tokenInfo, +}: { + symbol?: string; + account: string; + amount: string; + chainId: ChainId; + poolContract: ContractBasic; + tokenContract?: ContractBasic; + tokenInfo?: TokenInfo; +}) => { + if (!tokenInfo?.symbol) tokenInfo = getTokenInfoByWhitelist(chainId as ChainId, symbol); + if (!symbol) symbol = tokenInfo?.symbol; + const bigAmount = timesDecimals(amount, tokenInfo?.decimals).toFixed(0); + if (!tokenInfo?.isNativeToken) { + const req = await checkApprove( + chainId, + (isELFChain(chainId) ? tokenInfo?.symbol : tokenInfo?.address) as string, + account, + poolContract.address || '', + bigAmount, + undefined, + isELFChain(chainId) ? tokenContract : undefined, + ); + + if (req !== REQ_CODE.Success) throw new Error('Failed to add liquidity.'); + } + if (isELFChain(chainId)) { + return poolContract?.callSendMethod('addLiquidity', account, { + tokenSymbol: symbol, + amount: bigAmount, + }); + } + + return poolContract?.callSendMethod('addLiquidity', account, [tokenInfo?.address, bigAmount], { + onMethod: 'receipt', + value: tokenInfo?.isNativeToken ? bigAmount : '0', + }); +}; + +export const removeLiquidity = async ({ + symbol, + account, + amount, + chainId, + poolContract, + tokenInfo, +}: { + symbol?: string; + account: string; + amount: string; + chainId: ChainId; + poolContract: ContractBasic; + tokenInfo?: TokenInfo; +}) => { + if (!tokenInfo?.symbol) tokenInfo = getTokenInfoByWhitelist(chainId as ChainId, symbol); + if (!symbol) symbol = tokenInfo?.symbol; + const bigAmount = timesDecimals(amount, tokenInfo?.decimals).toFixed(0); + + if (isELFChain(chainId)) { + return poolContract?.callSendMethod('removeLiquidity', account, { + tokenSymbol: symbol, + amount: bigAmount, + }); + } + + return poolContract?.callSendMethod('removeLiquidity', account, [tokenInfo?.address, bigAmount]); +}; + +export type TGetTotalLiquidityParams = { + poolContract?: ContractBasic; + tokenContract?: { address?: string; chainId?: ChainId }; + symbol?: string; +}; + +export const getTotalLiquidity = async ({ poolContract, tokenContract, symbol }: TGetTotalLiquidityParams) => { + if (poolContract?.contractType === 'ELF') { + const req = await poolContract.callViewMethod('GetTokenPoolInfo', { + tokenSymbol: symbol, + }); + if (req.error) throw req.error; + return req.liquidity; + } + + if (poolContract?.contractType === 'TON') { + const req = await poolContract.callViewMethod('GetTokenPoolInfo', []); + if (req.error) throw req.error; + return req.liquidity; + } + + const req = await getBalanceByWagmi({ + address: poolContract?.address as any, + token: tokenContract?.address as any, + chainId: tokenContract?.chainId, + }); + return req.value.toString(); +}; + +export type TGetMyLiquidityParams = { + poolContract?: ContractBasic; + account?: string; + tokenInfo?: TokenInfo; +}; + +export const getMyLiquidity = async ({ poolContract, account, tokenInfo }: TGetMyLiquidityParams) => { + if (poolContract?.contractType === 'ELF') { + const req = await poolContract.callViewMethod('GetLiquidity', { + tokenSymbol: tokenInfo?.symbol, + provider: account, + }); + if (req.error) throw req.error; + return req.value; + } + + const req = await readContractByWagmi({ + abi: POOLS_ABI, + functionName: 'getUserLiquidity', + address: poolContract?.address as string, + chainId: poolContract?.chainId, + args: [account, tokenInfo?.address], + }); + + return req?.toString(); +}; diff --git a/src/utils/portkey.ts b/src/utils/portkey.ts new file mode 100644 index 00000000..2c73d00b --- /dev/null +++ b/src/utils/portkey.ts @@ -0,0 +1,65 @@ +import { WalletTypeEnum } from '@aelf-web-login/wallet-adapter-base'; +import { PORTKEY_WEB_WALLET_INFO_KEY } from 'constants/misc'; +import { TWalletConnectorId } from 'types'; + +export function compareVersions(v1: string, v2: string) { + const v1Parts = v1.split('.').map(Number); + const v2Parts = v2.split('.').map(Number); + + for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) { + const v1Part = v1Parts[i] || 0; + const v2Part = v2Parts[i] || 0; + if (v1Part < v2Part) { + return -1; + } else if (v1Part > v2Part) { + return 1; + } + } + return 0; +} +export function isPortkey() { + if (typeof window === 'object') return window.navigator.userAgent.includes('Portkey'); +} + +export function isPortkeyConnectEagerly() { + if (!isPortkey()) return false; + const regex = /PortkeyV(\d+\.\d+\.\d+)/; + const match = window.navigator.userAgent.match(regex); + if (match && match[1]) { + const version = match[1]; + return compareVersions(version, '1.4.0') >= 0; + } + return false; +} + +export function isPortkeyConnector(connectorId?: TWalletConnectorId) { + return connectorId === 'PORTKEY'; +} + +export function isSelectPortkey(type?: string) { + return type?.includes('PORTKEY'); +} +export type TPortkeyWebWalletWalletInfo = { + caAddress: string; + caHash: string; + managerAddress: string; + managerPubkey: string; + originChainId: string; +}; +export function getPortkeyWebWalletInfo() { + const portkeyWebWalletInfo = localStorage.getItem(PORTKEY_WEB_WALLET_INFO_KEY); + if (!portkeyWebWalletInfo) return; + try { + return JSON.parse(portkeyWebWalletInfo) as TPortkeyWebWalletWalletInfo; + } catch (error) { + return; + } +} + +export function checkConnectedWallet() { + try { + if (localStorage.getItem('connectedWallet') === WalletTypeEnum.aa) localStorage.removeItem('connectedWallet'); + } catch (error) { + console.log(error, '====checkConnectedWallet'); + } +} diff --git a/src/utils/provider.ts b/src/utils/provider.ts new file mode 100644 index 00000000..31843516 --- /dev/null +++ b/src/utils/provider.ts @@ -0,0 +1,39 @@ +import { ERCChainConstants } from 'constants/ChainConstants'; +import { SupportedERCChain } from 'constants/index'; +import Web3 from 'web3'; +import { getBridgeChainInfo } from './chain'; +import { ChainId } from 'types'; +const Provider = Object.values(SupportedERCChain).map((i) => { + return new Web3.providers.HttpProvider(i.CHAIN_INFO.rpcUrl, { + keepAlive: true, + withCredentials: false, + timeout: 20000, // ms + }); +}); +const chainKeys = Object.keys(SupportedERCChain); +export const getProvider = (chainId?: number | string) => { + const index = chainKeys.findIndex((i) => Number(i) === chainId); + if (index !== -1) return Provider[index]; +}; + +export function isUserDenied(m: string) { + return typeof m === 'string' && m.includes('User denied'); +} + +export const getDefaultProvider = () => { + const defaultProvider = new Web3.providers.HttpProvider(ERCChainConstants.constants.CHAIN_INFO.rpcUrl, { + keepAlive: true, + withCredentials: false, + timeout: 20000, // ms + }); + return defaultProvider; +}; + +export const getDefaultProviderByChainId = (chainId: ChainId) => { + const defaultProvider = new Web3.providers.HttpProvider(getBridgeChainInfo(chainId)?.CHAIN_INFO?.rpcUrl, { + keepAlive: true, + withCredentials: false, + timeout: 20000, // ms + }); + return defaultProvider; +}; diff --git a/src/utils/reg.ts b/src/utils/reg.ts new file mode 100644 index 00000000..c0baae1b --- /dev/null +++ b/src/utils/reg.ts @@ -0,0 +1,44 @@ +/* eslint-disable no-useless-escape */ +const protocolAndDomainRE = /^(?:\w+:)?\/\/(\S+)$/; +const localhostDomainRE = /^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/; +const nonLocalhostDomainRE = /^[^\s\.]+\.\S{2,}$/; + +export function isUrl(string: string) { + if (typeof string !== 'string') { + return false; + } + + const match = string.match(protocolAndDomainRE); + if (!match) { + return false; + } + + const everythingAfterProtocol = match[1]; + if (!everythingAfterProtocol) { + return false; + } + + if (localhostDomainRE.test(everythingAfterProtocol) || nonLocalhostDomainRE.test(everythingAfterProtocol)) { + return true; + } + + return false; +} + +const SYMBOL_REG = /^[A-Za-z0-9]+$/; +export function isSymbol(symbol?: string) { + if (!symbol) return false; + return SYMBOL_REG.test(symbol); +} + +const P_N_REG = /^([0-9]+\.?[0-9]*|-[0-9]+\.?[0-9]*)$/; + +export function isValidNumber(n?: string) { + if (!n) return false; + if (n === '-') return true; + return P_N_REG.test(n); +} +export function isValidPositiveNumber(n?: string) { + if (!n || n.includes('-')) return false; + return P_N_REG.test(n); +} diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts new file mode 100644 index 00000000..9d82b9e1 --- /dev/null +++ b/src/utils/sentry.ts @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DNS, + tracesSampleRate: 1.0, + environment: process.env.NEXT_PUBLIC_APP_ENV, +}); diff --git a/src/utils/telegram/telegram-web-app.js b/src/utils/telegram/telegram-web-app.js new file mode 100644 index 00000000..a49625b2 --- /dev/null +++ b/src/utils/telegram/telegram-web-app.js @@ -0,0 +1,2169 @@ +// WebView +(function () { + var eventHandlers = {}; + + var locationHash = ''; + try { + locationHash = location.hash.toString(); + } catch (e) { + // + } + + var initParams = urlParseHashParams(locationHash); + var storedParams = sessionStorageGet('initParams'); + if (storedParams) { + for (var key in storedParams) { + if (typeof initParams[key] === 'undefined') { + initParams[key] = storedParams[key]; + } + } + } + sessionStorageSet('initParams', initParams); + + var isIframe = false, + iFrameStyle; + try { + isIframe = window.parent != null && window != window.parent; + if (isIframe) { + window.addEventListener('message', function (event) { + if (event.source !== window.parent) return; + try { + var dataParsed = JSON.parse(event.data); + } catch (e) { + return; + } + if (!dataParsed || !dataParsed.eventType) { + return; + } + if (dataParsed.eventType == 'set_custom_style') { + if (event.origin === 'https://web.telegram.org') { + iFrameStyle.innerHTML = dataParsed.eventData; + } + } else if (dataParsed.eventType == 'reload_iframe') { + try { + window.parent.postMessage(JSON.stringify({ eventType: 'iframe_will_reload' }), '*'); + } catch (e) { + // + } + location.reload(); + } else { + receiveEvent(dataParsed.eventType, dataParsed.eventData); + } + }); + iFrameStyle = document.createElement('style'); + document.head.appendChild(iFrameStyle); + try { + window.parent.postMessage( + JSON.stringify({ eventType: 'iframe_ready', eventData: { reload_supported: true } }), + '*', + ); + } catch (e) { + // + } + } + } catch (e) { + // + } + + function urlSafeDecode(urlencoded) { + try { + urlencoded = urlencoded.replace(/\+/g, '%20'); + return decodeURIComponent(urlencoded); + } catch (e) { + return urlencoded; + } + } + + function urlParseHashParams(locationHash) { + locationHash = locationHash.replace(/^#/, ''); + var params = {}; + if (!locationHash.length) { + return params; + } + if (locationHash.indexOf('=') < 0 && locationHash.indexOf('?') < 0) { + params._path = urlSafeDecode(locationHash); + return params; + } + var qIndex = locationHash.indexOf('?'); + if (qIndex >= 0) { + var pathParam = locationHash.substr(0, qIndex); + params._path = urlSafeDecode(pathParam); + locationHash = locationHash.substr(qIndex + 1); + } + var query_params = urlParseQueryString(locationHash); + for (var k in query_params) { + params[k] = query_params[k]; + } + return params; + } + + function urlParseQueryString(queryString) { + var params = {}; + if (!queryString.length) { + return params; + } + var queryStringParams = queryString.split('&'); + var i, param, paramName, paramValue; + for (i = 0; i < queryStringParams.length; i++) { + param = queryStringParams[i].split('='); + paramName = urlSafeDecode(param[0]); + paramValue = param[1] == null ? null : urlSafeDecode(param[1]); + params[paramName] = paramValue; + } + return params; + } + + // Telegram apps will implement this logic to add service params (e.g. tgShareScoreUrl) to game URL + function urlAppendHashParams(url, addHash) { + // url looks like 'https://game.com/path?query=1#hash' + // addHash looks like 'tgShareScoreUrl=' + encodeURIComponent('tgb://share_game_score?hash=very_long_hash123') + + var ind = url.indexOf('#'); + if (ind < 0) { + // https://game.com/path -> https://game.com/path#tgShareScoreUrl=etc + return url + '#' + addHash; + } + var curHash = url.substr(ind + 1); + if (curHash.indexOf('=') >= 0 || curHash.indexOf('?') >= 0) { + // https://game.com/#hash=1 -> https://game.com/#hash=1&tgShareScoreUrl=etc + // https://game.com/#path?query -> https://game.com/#path?query&tgShareScoreUrl=etc + return url + '&' + addHash; + } + // https://game.com/#hash -> https://game.com/#hash?tgShareScoreUrl=etc + if (curHash.length > 0) { + return url + '?' + addHash; + } + // https://game.com/# -> https://game.com/#tgShareScoreUrl=etc + return url + addHash; + } + + function postEvent(eventType, callback, eventData) { + if (!callback) { + callback = function () { + // + }; + } + if (eventData === undefined) { + eventData = ''; + } + console.log('[Telegram.WebView] > postEvent', eventType, eventData); + + if (window.TelegramWebviewProxy !== undefined) { + // eslint-disable-next-line no-undef + TelegramWebviewProxy.postEvent(eventType, JSON.stringify(eventData)); + callback(); + } else if (window.external && 'notify' in window.external) { + window.external.notify(JSON.stringify({ eventType: eventType, eventData: eventData })); + callback(); + } else if (isIframe) { + try { + var trustedTarget = 'https://web.telegram.org'; + // For now we don't restrict target, for testing purposes + trustedTarget = '*'; + window.parent.postMessage(JSON.stringify({ eventType: eventType, eventData: eventData }), trustedTarget); + callback(); + } catch (e) { + callback(e); + } + } else { + callback({ notAvailable: true }); + } + } + + function receiveEvent(eventType, eventData) { + console.log('[Telegram.WebView] < receiveEvent', eventType, eventData); + callEventCallbacks(eventType, function (callback) { + callback(eventType, eventData); + }); + } + + function callEventCallbacks(eventType, func) { + var curEventHandlers = eventHandlers[eventType]; + if (curEventHandlers === undefined || !curEventHandlers.length) { + return; + } + for (var i = 0; i < curEventHandlers.length; i++) { + try { + func(curEventHandlers[i]); + } catch (e) { + // + } + } + } + + function onEvent(eventType, callback) { + if (eventHandlers[eventType] === undefined) { + eventHandlers[eventType] = []; + } + var index = eventHandlers[eventType].indexOf(callback); + if (index === -1) { + eventHandlers[eventType].push(callback); + } + } + + function offEvent(eventType, callback) { + if (eventHandlers[eventType] === undefined) { + return; + } + var index = eventHandlers[eventType].indexOf(callback); + if (index === -1) { + return; + } + eventHandlers[eventType].splice(index, 1); + } + + function openProtoUrl(url) { + if (!url.match(/^(web\+)?tgb?:\/\/./)) { + return false; + } + var useIframe = navigator.userAgent.match(/iOS|iPhone OS|iPhone|iPod|iPad/i) ? true : false; + if (useIframe) { + var iframeContEl = document.getElementById('tgme_frame_cont') || document.body; + var iframeEl = document.createElement('iframe'); + iframeContEl.appendChild(iframeEl); + var pageHidden = false; + var enableHidden = function () { + pageHidden = true; + }; + window.addEventListener('pagehide', enableHidden, false); + window.addEventListener('blur', enableHidden, false); + if (iframeEl !== null) { + iframeEl.src = url; + } + setTimeout(function () { + if (!pageHidden) { + window.location = url; + } + window.removeEventListener('pagehide', enableHidden, false); + window.removeEventListener('blur', enableHidden, false); + }, 2000); + } else { + window.location = url; + } + return true; + } + + function sessionStorageSet(key, value) { + try { + window.sessionStorage.setItem('__telegram__' + key, JSON.stringify(value)); + return true; + } catch (e) { + // + } + return false; + } + function sessionStorageGet(key) { + try { + return JSON.parse(window.sessionStorage.getItem('__telegram__' + key)); + } catch (e) { + // + } + return null; + } + + if (!window.Telegram) { + window.Telegram = {}; + } + window.Telegram.WebView = { + initParams: initParams, + isIframe: isIframe, + onEvent: onEvent, + offEvent: offEvent, + postEvent: postEvent, + receiveEvent: receiveEvent, + callEventCallbacks: callEventCallbacks, + }; + + window.Telegram.Utils = { + urlSafeDecode: urlSafeDecode, + urlParseQueryString: urlParseQueryString, + urlParseHashParams: urlParseHashParams, + urlAppendHashParams: urlAppendHashParams, + sessionStorageSet: sessionStorageSet, + sessionStorageGet: sessionStorageGet, + }; + + // For Windows Phone app + window.TelegramGameProxy_receiveEvent = receiveEvent; + + // App backward compatibility + window.TelegramGameProxy = { + receiveEvent: receiveEvent, + }; +})(); + +// WebApp +(function () { + var Utils = window.Telegram.Utils; + var WebView = window.Telegram.WebView; + var initParams = WebView.initParams; + var isIframe = WebView.isIframe; + + var WebApp = {}; + var webAppInitData = '', + webAppInitDataUnsafe = {}; + var themeParams = {}, + colorScheme = 'light'; + var webAppVersion = '6.0'; + var webAppPlatform = 'unknown'; + + if (initParams.tgWebAppData && initParams.tgWebAppData.length) { + webAppInitData = initParams.tgWebAppData; + webAppInitDataUnsafe = Utils.urlParseQueryString(webAppInitData); + for (var key in webAppInitDataUnsafe) { + var val = webAppInitDataUnsafe[key]; + try { + if ((val.substr(0, 1) == '{' && val.substr(-1) == '}') || (val.substr(0, 1) == '[' && val.substr(-1) == ']')) { + webAppInitDataUnsafe[key] = JSON.parse(val); + } + } catch (e) { + // + } + } + } + if (initParams.tgWebAppThemeParams && initParams.tgWebAppThemeParams.length) { + var themeParamsRaw = initParams.tgWebAppThemeParams; + try { + var theme_params = JSON.parse(themeParamsRaw); + if (theme_params) { + setThemeParams(theme_params); + } + } catch (e) { + // + } + } + // eslint-disable-next-line no-redeclare + var theme_params = Utils.sessionStorageGet('themeParams'); + if (theme_params) { + setThemeParams(theme_params); + } + if (initParams.tgWebAppVersion) { + webAppVersion = initParams.tgWebAppVersion; + } + if (initParams.tgWebAppPlatform) { + webAppPlatform = initParams.tgWebAppPlatform; + } + + function onThemeChanged(eventType, eventData) { + if (eventData.theme_params) { + setThemeParams(eventData.theme_params); + window.Telegram.WebApp.MainButton.setParams({}); + updateBackgroundColor(); + receiveWebViewEvent('themeChanged'); + } + } + + var lastWindowHeight = window.innerHeight; + function onViewportChanged(eventType, eventData) { + if (eventData.height) { + window.removeEventListener('resize', onWindowResize); + setViewportHeight(eventData); + } + } + + function onWindowResize(e) { + if (lastWindowHeight != window.innerHeight) { + lastWindowHeight = window.innerHeight; + receiveWebViewEvent('viewportChanged', { + isStateStable: true, + }); + } + } + + function linkHandler(e) { + if (e.metaKey || e.ctrlKey) return; + var el = e.target; + while (el.tagName != 'A' && el.parentNode) { + el = el.parentNode; + } + if ( + el.tagName == 'A' && + el.target != '_blank' && + (el.protocol == 'http:' || el.protocol == 'https:') && + el.hostname == 't.me' + ) { + WebApp.openTgLink(el.href); + e.preventDefault(); + } + } + + function strTrim(str) { + return str.toString().replace(/^\s+|\s+$/g, ''); + } + + function receiveWebViewEvent(eventType) { + var args = Array.prototype.slice.call(arguments); + eventType = args.shift(); + WebView.callEventCallbacks('webview:' + eventType, function (callback) { + callback.apply(WebApp, args); + }); + } + + function onWebViewEvent(eventType, callback) { + WebView.onEvent('webview:' + eventType, callback); + } + + function offWebViewEvent(eventType, callback) { + WebView.offEvent('webview:' + eventType, callback); + } + + function setCssProperty(name, value) { + var root = document.documentElement; + if (root && root.style && root.style.setProperty) { + root.style.setProperty('--tg-' + name, value); + } + } + + function setThemeParams(theme_params) { + // temp iOS fix + if (theme_params.bg_color == '#1c1c1d' && theme_params.bg_color == theme_params.secondary_bg_color) { + theme_params.secondary_bg_color = '#2c2c2e'; + } + var color; + for (var key in theme_params) { + if ((color = parseColorToHex(theme_params[key]))) { + themeParams[key] = color; + if (key == 'bg_color') { + colorScheme = isColorDark(color) ? 'dark' : 'light'; + setCssProperty('color-scheme', colorScheme); + } + key = 'theme-' + key.split('_').join('-'); + setCssProperty(key, color); + } + } + Utils.sessionStorageSet('themeParams', themeParams); + } + + var webAppCallbacks = {}; + function generateCallbackId(len) { + var tries = 100; + while (--tries) { + var id = '', + chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', + chars_len = chars.length; + for (var i = 0; i < len; i++) { + id += chars[Math.floor(Math.random() * chars_len)]; + } + if (!webAppCallbacks[id]) { + webAppCallbacks[id] = {}; + return id; + } + } + throw Error('WebAppCallbackIdGenerateFailed'); + } + + var viewportHeight = false, + viewportStableHeight = false, + isExpanded = true; + function setViewportHeight(data) { + if (typeof data !== 'undefined') { + isExpanded = !!data.is_expanded; + viewportHeight = data.height; + if (data.is_state_stable) { + viewportStableHeight = data.height; + } + receiveWebViewEvent('viewportChanged', { + isStateStable: !!data.is_state_stable, + }); + } + var height, stable_height; + if (viewportHeight !== false) { + height = viewportHeight - mainButtonHeight + 'px'; + } else { + height = mainButtonHeight ? 'calc(100vh - ' + mainButtonHeight + 'px)' : '100vh'; + } + if (viewportStableHeight !== false) { + stable_height = viewportStableHeight - mainButtonHeight + 'px'; + } else { + stable_height = mainButtonHeight ? 'calc(100vh - ' + mainButtonHeight + 'px)' : '100vh'; + } + setCssProperty('viewport-height', height); + setCssProperty('viewport-stable-height', stable_height); + } + + var isClosingConfirmationEnabled = false; + function setClosingConfirmation(need_confirmation) { + if (!versionAtLeast('6.2')) { + console.warn('[Telegram.WebApp] Closing confirmation is not supported in version ' + webAppVersion); + return; + } + isClosingConfirmationEnabled = !!need_confirmation; + WebView.postEvent('web_app_setup_closing_behavior', false, { + need_confirmation: isClosingConfirmationEnabled, + }); + } + + var isVerticalSwipesEnabled = true; + function toggleVerticalSwipes(enable_swipes) { + if (!versionAtLeast('7.7')) { + console.warn('[Telegram.WebApp] Changing swipes behavior is not supported in version ' + webAppVersion); + return; + } + isVerticalSwipesEnabled = !!enable_swipes; + WebView.postEvent('web_app_setup_swipe_behavior', false, { + allow_vertical_swipe: isVerticalSwipesEnabled, + }); + } + + var headerColorKey = 'bg_color', + headerColor = null; + function getHeaderColor() { + if (headerColorKey == 'secondary_bg_color') { + return themeParams.secondary_bg_color; + } else if (headerColorKey == 'bg_color') { + return themeParams.bg_color; + } + return headerColor; + } + function setHeaderColor(color) { + if (!versionAtLeast('6.1')) { + console.warn('[Telegram.WebApp] Header color is not supported in version ' + webAppVersion); + return; + } + if (!versionAtLeast('6.9')) { + if (themeParams.bg_color && themeParams.bg_color == color) { + color = 'bg_color'; + } else if (themeParams.secondary_bg_color && themeParams.secondary_bg_color == color) { + color = 'secondary_bg_color'; + } + } + var head_color = null, + color_key = null; + if (color == 'bg_color' || color == 'secondary_bg_color') { + color_key = color; + } else if (versionAtLeast('6.9')) { + head_color = parseColorToHex(color); + if (!head_color) { + console.error('[Telegram.WebApp] Header color format is invalid', color); + throw Error('WebAppHeaderColorInvalid'); + } + } + if (!versionAtLeast('6.9') && color_key != 'bg_color' && color_key != 'secondary_bg_color') { + console.error( + "[Telegram.WebApp] Header color key should be one of Telegram.WebApp.themeParams.bg_color, Telegram.WebApp.themeParams.secondary_bg_color, 'bg_color', 'secondary_bg_color'", + color, + ); + throw Error('WebAppHeaderColorKeyInvalid'); + } + headerColorKey = color_key; + headerColor = head_color; + updateHeaderColor(); + } + var appHeaderColorKey = null, + appHeaderColor = null; + function updateHeaderColor() { + if (appHeaderColorKey != headerColorKey || appHeaderColor != headerColor) { + appHeaderColorKey = headerColorKey; + appHeaderColor = headerColor; + if (appHeaderColor) { + WebView.postEvent('web_app_set_header_color', false, { color: headerColor }); + } else { + WebView.postEvent('web_app_set_header_color', false, { color_key: headerColorKey }); + } + } + } + + var backgroundColor = 'bg_color'; + function getBackgroundColor() { + if (backgroundColor == 'secondary_bg_color') { + return themeParams.secondary_bg_color; + } else if (backgroundColor == 'bg_color') { + return themeParams.bg_color; + } + return backgroundColor; + } + function setBackgroundColor(color) { + if (!versionAtLeast('6.1')) { + console.warn('[Telegram.WebApp] Background color is not supported in version ' + webAppVersion); + return; + } + var bg_color; + if (color == 'bg_color' || color == 'secondary_bg_color') { + bg_color = color; + } else { + bg_color = parseColorToHex(color); + if (!bg_color) { + console.error('[Telegram.WebApp] Background color format is invalid', color); + throw Error('WebAppBackgroundColorInvalid'); + } + } + backgroundColor = bg_color; + updateBackgroundColor(); + } + var appBackgroundColor = null; + function updateBackgroundColor() { + var color = getBackgroundColor(); + if (appBackgroundColor != color) { + appBackgroundColor = color; + WebView.postEvent('web_app_set_background_color', false, { color: color }); + } + } + + function parseColorToHex(color) { + color += ''; + var match; + if ((match = /^\s*#([0-9a-f]{6})\s*$/i.exec(color))) { + return '#' + match[1].toLowerCase(); + } else if ((match = /^\s*#([0-9a-f])([0-9a-f])([0-9a-f])\s*$/i.exec(color))) { + return ('#' + match[1] + match[1] + match[2] + match[2] + match[3] + match[3]).toLowerCase(); + } else if ((match = /^\s*rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)\s*$/.exec(color))) { + var r = parseInt(match[1]), + g = parseInt(match[2]), + b = parseInt(match[3]); + r = (r < 16 ? '0' : '') + r.toString(16); + g = (g < 16 ? '0' : '') + g.toString(16); + b = (b < 16 ? '0' : '') + b.toString(16); + return '#' + r + g + b; + } + return false; + } + + function isColorDark(rgb) { + rgb = rgb.replace(/[\s#]/g, ''); + if (rgb.length == 3) { + rgb = rgb[0] + rgb[0] + rgb[1] + rgb[1] + rgb[2] + rgb[2]; + } + var r = parseInt(rgb.substr(0, 2), 16); + var g = parseInt(rgb.substr(2, 2), 16); + var b = parseInt(rgb.substr(4, 2), 16); + var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)); + return hsp < 120; + } + + function versionCompare(v1, v2) { + if (typeof v1 !== 'string') v1 = ''; + if (typeof v2 !== 'string') v2 = ''; + v1 = v1.replace(/^\s+|\s+$/g, '').split('.'); + v2 = v2.replace(/^\s+|\s+$/g, '').split('.'); + var a = Math.max(v1.length, v2.length), + i, + p1, + p2; + for (i = 0; i < a; i++) { + p1 = parseInt(v1[i]) || 0; + p2 = parseInt(v2[i]) || 0; + if (p1 == p2) continue; + if (p1 > p2) return 1; + return -1; + } + return 0; + } + + function versionAtLeast(ver) { + return versionCompare(webAppVersion, ver) >= 0; + } + + function byteLength(str) { + if (window.Blob) { + try { + return new Blob([str]).size; + } catch (e) { + // + } + } + var s = str.length; + for (var i = str.length - 1; i >= 0; i--) { + var code = str.charCodeAt(i); + if (code > 0x7f && code <= 0x7ff) s++; + else if (code > 0x7ff && code <= 0xffff) s += 2; + if (code >= 0xdc00 && code <= 0xdfff) i--; + } + return s; + } + + var BackButton = (function () { + var isVisible = false; + + var backButton = {}; + Object.defineProperty(backButton, 'isVisible', { + set: function (val) { + setParams({ is_visible: val }); + }, + get: function () { + return isVisible; + }, + enumerable: true, + }); + + var curButtonState = null; + + WebView.onEvent('back_button_pressed', onBackButtonPressed); + + function onBackButtonPressed() { + receiveWebViewEvent('backButtonClicked'); + } + + function buttonParams() { + return { is_visible: isVisible }; + } + + function buttonState(btn_params) { + if (typeof btn_params === 'undefined') { + btn_params = buttonParams(); + } + return JSON.stringify(btn_params); + } + + function buttonCheckVersion() { + if (!versionAtLeast('6.1')) { + console.warn('[Telegram.WebApp] BackButton is not supported in version ' + webAppVersion); + return false; + } + return true; + } + + function updateButton() { + var btn_params = buttonParams(); + var btn_state = buttonState(btn_params); + if (curButtonState === btn_state) { + return; + } + curButtonState = btn_state; + WebView.postEvent('web_app_setup_back_button', false, btn_params); + } + + function setParams(params) { + if (!buttonCheckVersion()) { + return backButton; + } + if (typeof params.is_visible !== 'undefined') { + isVisible = !!params.is_visible; + } + updateButton(); + return backButton; + } + + backButton.onClick = function (callback) { + if (buttonCheckVersion()) { + onWebViewEvent('backButtonClicked', callback); + } + return backButton; + }; + backButton.offClick = function (callback) { + if (buttonCheckVersion()) { + offWebViewEvent('backButtonClicked', callback); + } + return backButton; + }; + backButton.show = function () { + return setParams({ is_visible: true }); + }; + backButton.hide = function () { + return setParams({ is_visible: false }); + }; + return backButton; + })(); + + var mainButtonHeight = 0; + var MainButton = (function () { + var isVisible = false; + var isActive = true; + var isProgressVisible = false; + var buttonText = 'CONTINUE'; + var buttonColor = false; + var buttonTextColor = false; + + var mainButton = {}; + Object.defineProperty(mainButton, 'text', { + set: function (val) { + mainButton.setParams({ text: val }); + }, + get: function () { + return buttonText; + }, + enumerable: true, + }); + Object.defineProperty(mainButton, 'color', { + set: function (val) { + mainButton.setParams({ color: val }); + }, + get: function () { + return buttonColor || themeParams.button_color || '#2481cc'; + }, + enumerable: true, + }); + Object.defineProperty(mainButton, 'textColor', { + set: function (val) { + mainButton.setParams({ text_color: val }); + }, + get: function () { + return buttonTextColor || themeParams.button_text_color || '#ffffff'; + }, + enumerable: true, + }); + Object.defineProperty(mainButton, 'isVisible', { + set: function (val) { + mainButton.setParams({ is_visible: val }); + }, + get: function () { + return isVisible; + }, + enumerable: true, + }); + Object.defineProperty(mainButton, 'isProgressVisible', { + get: function () { + return isProgressVisible; + }, + enumerable: true, + }); + Object.defineProperty(mainButton, 'isActive', { + set: function (val) { + mainButton.setParams({ is_active: val }); + }, + get: function () { + return isActive; + }, + enumerable: true, + }); + + var curButtonState = null; + + WebView.onEvent('main_button_pressed', onMainButtonPressed); + + var debugBtn = null, + debugBtnStyle = {}; + if (initParams.tgWebAppDebug) { + debugBtn = document.createElement('tg-main-button'); + debugBtnStyle = { + font: '600 14px/18px sans-serif', + display: 'none', + width: '100%', + height: '48px', + borderRadius: '0', + background: 'no-repeat right center', + position: 'fixed', + left: '0', + right: '0', + bottom: '0', + margin: '0', + padding: '15px 20px', + textAlign: 'center', + boxSizing: 'border-box', + zIndex: '10000', + }; + for (var k in debugBtnStyle) { + debugBtn.style[k] = debugBtnStyle[k]; + } + document.addEventListener('DOMContentLoaded', function onDomLoaded(event) { + document.removeEventListener('DOMContentLoaded', onDomLoaded); + document.body.appendChild(debugBtn); + debugBtn.addEventListener('click', onMainButtonPressed, false); + }); + } + + function onMainButtonPressed() { + if (isActive) { + receiveWebViewEvent('mainButtonClicked'); + } + } + + function buttonParams() { + var color = mainButton.color; + var text_color = mainButton.textColor; + return isVisible + ? { + is_visible: true, + is_active: isActive, + is_progress_visible: isProgressVisible, + text: buttonText, + color: color, + text_color: text_color, + } + : { is_visible: false }; + } + + function buttonState(btn_params) { + if (typeof btn_params === 'undefined') { + btn_params = buttonParams(); + } + return JSON.stringify(btn_params); + } + + function updateButton() { + var btn_params = buttonParams(); + var btn_state = buttonState(btn_params); + if (curButtonState === btn_state) { + return; + } + curButtonState = btn_state; + WebView.postEvent('web_app_setup_main_button', false, btn_params); + if (initParams.tgWebAppDebug) { + updateDebugButton(btn_params); + } + } + + function updateDebugButton(btn_params) { + if (btn_params.is_visible) { + debugBtn.style.display = 'block'; + mainButtonHeight = 48; + + debugBtn.style.opacity = btn_params.is_active ? '1' : '0.8'; + debugBtn.style.cursor = btn_params.is_active ? 'pointer' : 'auto'; + debugBtn.disabled = !btn_params.is_active; + debugBtn.innerText = btn_params.text; + debugBtn.style.backgroundImage = btn_params.is_progress_visible + ? "url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20viewport%3D%220%200%2048%2048%22%20width%3D%2248px%22%20height%3D%2248px%22%3E%3Ccircle%20cx%3D%2250%25%22%20cy%3D%2250%25%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222.25%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%20stroke-dashoffset%3D%22106%22%20r%3D%229%22%20stroke-dasharray%3D%2256.52%22%20rotate%3D%22-90%22%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20attributeType%3D%22XML%22%20dur%3D%22360s%22%20from%3D%220%22%20to%3D%2212500%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3CanimateTransform%20attributeName%3D%22transform%22%20attributeType%3D%22XML%22%20type%3D%22rotate%22%20dur%3D%221s%22%20from%3D%22-90%2024%2024%22%20to%3D%22630%2024%2024%22%20repeatCount%3D%22indefinite%22%3E%3C%2FanimateTransform%3E%3C%2Fcircle%3E%3C%2Fsvg%3E')" + : 'none'; + debugBtn.style.backgroundColor = btn_params.color; + debugBtn.style.color = btn_params.text_color; + } else { + debugBtn.style.display = 'none'; + mainButtonHeight = 0; + } + if (document.documentElement) { + document.documentElement.style.boxSizing = 'border-box'; + document.documentElement.style.paddingBottom = mainButtonHeight + 'px'; + } + setViewportHeight(); + } + + function setParams(params) { + if (typeof params.text !== 'undefined') { + var text = strTrim(params.text); + if (!text.length) { + console.error('[Telegram.WebApp] Main button text is required', params.text); + throw Error('WebAppMainButtonParamInvalid'); + } + if (text.length > 64) { + console.error('[Telegram.WebApp] Main button text is too long', text); + throw Error('WebAppMainButtonParamInvalid'); + } + buttonText = text; + } + if (typeof params.color !== 'undefined') { + if (params.color === false || params.color === null) { + buttonColor = false; + } else { + var color = parseColorToHex(params.color); + if (!color) { + console.error('[Telegram.WebApp] Main button color format is invalid', params.color); + throw Error('WebAppMainButtonParamInvalid'); + } + buttonColor = color; + } + } + if (typeof params.text_color !== 'undefined') { + if (params.text_color === false || params.text_color === null) { + buttonTextColor = false; + } else { + var text_color = parseColorToHex(params.text_color); + if (!text_color) { + console.error('[Telegram.WebApp] Main button text color format is invalid', params.text_color); + throw Error('WebAppMainButtonParamInvalid'); + } + buttonTextColor = text_color; + } + } + if (typeof params.is_visible !== 'undefined') { + if (params.is_visible && !mainButton.text.length) { + console.error('[Telegram.WebApp] Main button text is required'); + throw Error('WebAppMainButtonParamInvalid'); + } + isVisible = !!params.is_visible; + } + if (typeof params.is_active !== 'undefined') { + isActive = !!params.is_active; + } + updateButton(); + return mainButton; + } + + mainButton.setText = function (text) { + return mainButton.setParams({ text: text }); + }; + mainButton.onClick = function (callback) { + onWebViewEvent('mainButtonClicked', callback); + return mainButton; + }; + mainButton.offClick = function (callback) { + offWebViewEvent('mainButtonClicked', callback); + return mainButton; + }; + mainButton.show = function () { + return mainButton.setParams({ is_visible: true }); + }; + mainButton.hide = function () { + return mainButton.setParams({ is_visible: false }); + }; + mainButton.enable = function () { + return mainButton.setParams({ is_active: true }); + }; + mainButton.disable = function () { + return mainButton.setParams({ is_active: false }); + }; + mainButton.showProgress = function (leaveActive) { + isActive = !!leaveActive; + isProgressVisible = true; + updateButton(); + return mainButton; + }; + mainButton.hideProgress = function () { + if (!mainButton.isActive) { + isActive = true; + } + isProgressVisible = false; + updateButton(); + return mainButton; + }; + mainButton.setParams = setParams; + return mainButton; + })(); + + var SettingsButton = (function () { + var isVisible = false; + + var settingsButton = {}; + Object.defineProperty(settingsButton, 'isVisible', { + set: function (val) { + setParams({ is_visible: val }); + }, + get: function () { + return isVisible; + }, + enumerable: true, + }); + + var curButtonState = null; + + WebView.onEvent('settings_button_pressed', onSettingsButtonPressed); + + function onSettingsButtonPressed() { + receiveWebViewEvent('settingsButtonClicked'); + } + + function buttonParams() { + return { is_visible: isVisible }; + } + + function buttonState(btn_params) { + if (typeof btn_params === 'undefined') { + btn_params = buttonParams(); + } + return JSON.stringify(btn_params); + } + + function buttonCheckVersion() { + if (!versionAtLeast('6.10')) { + console.warn('[Telegram.WebApp] SettingsButton is not supported in version ' + webAppVersion); + return false; + } + return true; + } + + function updateButton() { + var btn_params = buttonParams(); + var btn_state = buttonState(btn_params); + if (curButtonState === btn_state) { + return; + } + curButtonState = btn_state; + WebView.postEvent('web_app_setup_settings_button', false, btn_params); + } + + function setParams(params) { + if (!buttonCheckVersion()) { + return settingsButton; + } + if (typeof params.is_visible !== 'undefined') { + isVisible = !!params.is_visible; + } + updateButton(); + return settingsButton; + } + + settingsButton.onClick = function (callback) { + if (buttonCheckVersion()) { + onWebViewEvent('settingsButtonClicked', callback); + } + return settingsButton; + }; + settingsButton.offClick = function (callback) { + if (buttonCheckVersion()) { + offWebViewEvent('settingsButtonClicked', callback); + } + return settingsButton; + }; + settingsButton.show = function () { + return setParams({ is_visible: true }); + }; + settingsButton.hide = function () { + return setParams({ is_visible: false }); + }; + return settingsButton; + })(); + + var HapticFeedback = (function () { + var hapticFeedback = {}; + + function triggerFeedback(params) { + if (!versionAtLeast('6.1')) { + console.warn('[Telegram.WebApp] HapticFeedback is not supported in version ' + webAppVersion); + return hapticFeedback; + } + if (params.type == 'impact') { + if ( + params.impact_style != 'light' && + params.impact_style != 'medium' && + params.impact_style != 'heavy' && + params.impact_style != 'rigid' && + params.impact_style != 'soft' + ) { + console.error('[Telegram.WebApp] Haptic impact style is invalid', params.impact_style); + throw Error('WebAppHapticImpactStyleInvalid'); + } + } else if (params.type == 'notification') { + if ( + params.notification_type != 'error' && + params.notification_type != 'success' && + params.notification_type != 'warning' + ) { + console.error('[Telegram.WebApp] Haptic notification type is invalid', params.notification_type); + throw Error('WebAppHapticNotificationTypeInvalid'); + } + } else if (params.type == 'selection_change') { + // no params needed + } else { + console.error('[Telegram.WebApp] Haptic feedback type is invalid', params.type); + throw Error('WebAppHapticFeedbackTypeInvalid'); + } + WebView.postEvent('web_app_trigger_haptic_feedback', false, params); + return hapticFeedback; + } + + hapticFeedback.impactOccurred = function (style) { + return triggerFeedback({ type: 'impact', impact_style: style }); + }; + hapticFeedback.notificationOccurred = function (type) { + return triggerFeedback({ type: 'notification', notification_type: type }); + }; + hapticFeedback.selectionChanged = function () { + return triggerFeedback({ type: 'selection_change' }); + }; + return hapticFeedback; + })(); + + var CloudStorage = (function () { + var cloudStorage = {}; + + function invokeStorageMethod(method, params, callback) { + if (!versionAtLeast('6.9')) { + console.error('[Telegram.WebApp] CloudStorage is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + invokeCustomMethod(method, params, callback); + return cloudStorage; + } + + cloudStorage.setItem = function (key, value, callback) { + return invokeStorageMethod('saveStorageValue', { key: key, value: value }, callback); + }; + cloudStorage.getItem = function (key, callback) { + return cloudStorage.getItems( + [key], + callback + ? function (err, res) { + if (err) callback(err); + else callback(null, res[key]); + } + : null, + ); + }; + cloudStorage.getItems = function (keys, callback) { + return invokeStorageMethod('getStorageValues', { keys: keys }, callback); + }; + cloudStorage.removeItem = function (key, callback) { + return cloudStorage.removeItems([key], callback); + }; + cloudStorage.removeItems = function (keys, callback) { + return invokeStorageMethod('deleteStorageValues', { keys: keys }, callback); + }; + cloudStorage.getKeys = function (callback) { + return invokeStorageMethod('getStorageKeys', {}, callback); + }; + return cloudStorage; + })(); + + var BiometricManager = (function () { + var isInited = false; + var isBiometricAvailable = false; + var biometricType = 'unknown'; + var isAccessRequested = false; + var isAccessGranted = false; + var isBiometricTokenSaved = false; + var deviceId = ''; + + var biometricManager = {}; + Object.defineProperty(biometricManager, 'isInited', { + get: function () { + return isInited; + }, + enumerable: true, + }); + Object.defineProperty(biometricManager, 'isBiometricAvailable', { + get: function () { + return isInited && isBiometricAvailable; + }, + enumerable: true, + }); + Object.defineProperty(biometricManager, 'biometricType', { + get: function () { + return biometricType || 'unknown'; + }, + enumerable: true, + }); + Object.defineProperty(biometricManager, 'isAccessRequested', { + get: function () { + return isAccessRequested; + }, + enumerable: true, + }); + Object.defineProperty(biometricManager, 'isAccessGranted', { + get: function () { + return isAccessRequested && isAccessGranted; + }, + enumerable: true, + }); + Object.defineProperty(biometricManager, 'isBiometricTokenSaved', { + get: function () { + return isBiometricTokenSaved; + }, + enumerable: true, + }); + Object.defineProperty(biometricManager, 'deviceId', { + get: function () { + return deviceId || ''; + }, + enumerable: true, + }); + + var initRequestState = { callbacks: [] }; + var accessRequestState = false; + var authRequestState = false; + var tokenRequestState = false; + + WebView.onEvent('biometry_info_received', onBiometryInfoReceived); + WebView.onEvent('biometry_auth_requested', onBiometryAuthRequested); + WebView.onEvent('biometry_token_updated', onBiometryTokenUpdated); + + function onBiometryInfoReceived(eventType, eventData) { + isInited = true; + if (eventData.available) { + isBiometricAvailable = true; + biometricType = eventData.type || 'unknown'; + if (eventData.access_requested) { + isAccessRequested = true; + isAccessGranted = !!eventData.access_granted; + isBiometricTokenSaved = !!eventData.token_saved; + } else { + isAccessRequested = false; + isAccessGranted = false; + isBiometricTokenSaved = false; + } + } else { + isBiometricAvailable = false; + biometricType = 'unknown'; + isAccessRequested = false; + isAccessGranted = false; + isBiometricTokenSaved = false; + } + deviceId = eventData.device_id || ''; + + if (initRequestState.callbacks.length > 0) { + for (var i = 0; i < initRequestState.callbacks.length; i++) { + var callback = initRequestState.callbacks[i]; + callback(); + } + } + if (accessRequestState) { + var state = accessRequestState; + accessRequestState = false; + if (state.callback) { + state.callback(isAccessGranted); + } + } + receiveWebViewEvent('biometricManagerUpdated'); + } + function onBiometryAuthRequested(eventType, eventData) { + var isAuthenticated = eventData.status == 'authorized', + biometricToken = eventData.token || ''; + if (authRequestState) { + var state = authRequestState; + authRequestState = false; + if (state.callback) { + state.callback(isAuthenticated, isAuthenticated ? biometricToken : null); + } + } + receiveWebViewEvent( + 'biometricAuthRequested', + isAuthenticated + ? { + isAuthenticated: true, + biometricToken: biometricToken, + } + : { + isAuthenticated: false, + }, + ); + } + function onBiometryTokenUpdated(eventType, eventData) { + var applied = false; + if (isBiometricAvailable && isAccessRequested) { + if (eventData.status == 'updated') { + isBiometricTokenSaved = true; + applied = true; + } else if (eventData.status == 'removed') { + isBiometricTokenSaved = false; + applied = true; + } + } + if (tokenRequestState) { + var state = tokenRequestState; + tokenRequestState = false; + if (state.callback) { + state.callback(applied); + } + } + receiveWebViewEvent('biometricTokenUpdated', { + isUpdated: applied, + }); + } + + function checkVersion() { + if (!versionAtLeast('7.2')) { + console.warn('[Telegram.WebApp] BiometricManager is not supported in version ' + webAppVersion); + return false; + } + return true; + } + + function checkInit() { + if (!isInited) { + console.error('[Telegram.WebApp] BiometricManager should be inited before using.'); + throw Error('WebAppBiometricManagerNotInited'); + } + return true; + } + + biometricManager.init = function (callback) { + if (!checkVersion()) { + return biometricManager; + } + if (isInited) { + return biometricManager; + } + if (callback) { + initRequestState.callbacks.push(callback); + } + WebView.postEvent('web_app_biometry_get_info', false); + return biometricManager; + }; + biometricManager.requestAccess = function (params, callback) { + if (!checkVersion()) { + return biometricManager; + } + checkInit(); + if (!isBiometricAvailable) { + console.error('[Telegram.WebApp] Biometrics is not available on this device.'); + throw Error('WebAppBiometricManagerBiometricsNotAvailable'); + } + if (accessRequestState) { + console.error('[Telegram.WebApp] Access is already requested'); + throw Error('WebAppBiometricManagerAccessRequested'); + } + var popup_params = {}; + if (typeof params.reason !== 'undefined') { + var reason = strTrim(params.reason); + if (reason.length > 128) { + console.error('[Telegram.WebApp] Biometric reason is too long', reason); + throw Error('WebAppBiometricRequestAccessParamInvalid'); + } + if (reason.length > 0) { + popup_params.reason = reason; + } + } + + accessRequestState = { + callback: callback, + }; + WebView.postEvent('web_app_biometry_request_access', false, popup_params); + return biometricManager; + }; + biometricManager.authenticate = function (params, callback) { + if (!checkVersion()) { + return biometricManager; + } + checkInit(); + if (!isBiometricAvailable) { + console.error('[Telegram.WebApp] Biometrics is not available on this device.'); + throw Error('WebAppBiometricManagerBiometricsNotAvailable'); + } + if (!isAccessGranted) { + console.error('[Telegram.WebApp] Biometric access was not granted by the user.'); + throw Error('WebAppBiometricManagerBiometricAccessNotGranted'); + } + if (authRequestState) { + console.error('[Telegram.WebApp] Authentication request is already in progress.'); + throw Error('WebAppBiometricManagerAuthenticationRequested'); + } + var popup_params = {}; + if (typeof params.reason !== 'undefined') { + var reason = strTrim(params.reason); + if (reason.length > 128) { + console.error('[Telegram.WebApp] Biometric reason is too long', reason); + throw Error('WebAppBiometricRequestAccessParamInvalid'); + } + if (reason.length > 0) { + popup_params.reason = reason; + } + } + + authRequestState = { + callback: callback, + }; + WebView.postEvent('web_app_biometry_request_auth', false, popup_params); + return biometricManager; + }; + biometricManager.updateBiometricToken = function (token, callback) { + if (!checkVersion()) { + return biometricManager; + } + token = token || ''; + if (token.length > 1024) { + console.error('[Telegram.WebApp] Token is too long', token); + throw Error('WebAppBiometricManagerTokenInvalid'); + } + checkInit(); + if (!isBiometricAvailable) { + console.error('[Telegram.WebApp] Biometrics is not available on this device.'); + throw Error('WebAppBiometricManagerBiometricsNotAvailable'); + } + if (!isAccessGranted) { + console.error('[Telegram.WebApp] Biometric access was not granted by the user.'); + throw Error('WebAppBiometricManagerBiometricAccessNotGranted'); + } + if (tokenRequestState) { + console.error('[Telegram.WebApp] Token request is already in progress.'); + throw Error('WebAppBiometricManagerTokenUpdateRequested'); + } + tokenRequestState = { + callback: callback, + }; + WebView.postEvent('web_app_biometry_update_token', false, { token: token }); + return biometricManager; + }; + biometricManager.openSettings = function () { + if (!checkVersion()) { + return biometricManager; + } + checkInit(); + if (!isBiometricAvailable) { + console.error('[Telegram.WebApp] Biometrics is not available on this device.'); + throw Error('WebAppBiometricManagerBiometricsNotAvailable'); + } + if (!isAccessRequested) { + console.error('[Telegram.WebApp] Biometric access was not requested yet.'); + throw Error('WebAppBiometricManagerBiometricsAccessNotRequested'); + } + if (isAccessGranted) { + console.warn('[Telegram.WebApp] Biometric access was granted by the user, no need to go to settings.'); + return biometricManager; + } + WebView.postEvent('web_app_biometry_open_settings', false); + return biometricManager; + }; + return biometricManager; + })(); + + var webAppInvoices = {}; + function onInvoiceClosed(eventType, eventData) { + if (eventData.slug && webAppInvoices[eventData.slug]) { + var invoiceData = webAppInvoices[eventData.slug]; + delete webAppInvoices[eventData.slug]; + if (invoiceData.callback) { + invoiceData.callback(eventData.status); + } + receiveWebViewEvent('invoiceClosed', { + url: invoiceData.url, + status: eventData.status, + }); + } + } + + var webAppPopupOpened = false; + function onPopupClosed(eventType, eventData) { + if (webAppPopupOpened) { + var popupData = webAppPopupOpened; + webAppPopupOpened = false; + var button_id = null; + if (typeof eventData.button_id !== 'undefined') { + button_id = eventData.button_id; + } + if (popupData.callback) { + popupData.callback(button_id); + } + receiveWebViewEvent('popupClosed', { + button_id: button_id, + }); + } + } + + var webAppScanQrPopupOpened = false; + function onQrTextReceived(eventType, eventData) { + if (webAppScanQrPopupOpened) { + var popupData = webAppScanQrPopupOpened; + var data = null; + if (typeof eventData.data !== 'undefined') { + data = eventData.data; + } + if (popupData.callback) { + if (popupData.callback(data)) { + webAppScanQrPopupOpened = false; + WebView.postEvent('web_app_close_scan_qr_popup', false); + } + } + receiveWebViewEvent('qrTextReceived', { + data: data, + }); + } + } + function onScanQrPopupClosed(eventType, eventData) { + webAppScanQrPopupOpened = false; + receiveWebViewEvent('scanQrPopupClosed'); + } + + function onClipboardTextReceived(eventType, eventData) { + if (eventData.req_id && webAppCallbacks[eventData.req_id]) { + var requestData = webAppCallbacks[eventData.req_id]; + delete webAppCallbacks[eventData.req_id]; + var data = null; + if (typeof eventData.data !== 'undefined') { + data = eventData.data; + } + if (requestData.callback) { + requestData.callback(data); + } + receiveWebViewEvent('clipboardTextReceived', { + data: data, + }); + } + } + + var WebAppWriteAccessRequested = false; + function onWriteAccessRequested(eventType, eventData) { + if (WebAppWriteAccessRequested) { + var requestData = WebAppWriteAccessRequested; + WebAppWriteAccessRequested = false; + if (requestData.callback) { + requestData.callback(eventData.status == 'allowed'); + } + receiveWebViewEvent('writeAccessRequested', { + status: eventData.status, + }); + } + } + + function getRequestedContact(callback, timeout) { + var reqTo, + fallbackTo, + reqDelay = 0; + var reqInvoke = function () { + invokeCustomMethod('getRequestedContact', {}, function (err, res) { + if (res && res.length) { + clearTimeout(fallbackTo); + callback(res); + } else { + reqDelay += 50; + reqTo = setTimeout(reqInvoke, reqDelay); + } + }); + }; + var fallbackInvoke = function () { + clearTimeout(reqTo); + callback(''); + }; + fallbackTo = setTimeout(fallbackInvoke, timeout); + reqInvoke(); + } + + var WebAppContactRequested = false; + function onPhoneRequested(eventType, eventData) { + if (WebAppContactRequested) { + var requestData = WebAppContactRequested; + WebAppContactRequested = false; + var requestSent = eventData.status == 'sent'; + var webViewEvent = { + status: eventData.status, + }; + if (requestSent) { + getRequestedContact(function (res) { + if (res && res.length) { + webViewEvent.response = res; + webViewEvent.responseUnsafe = Utils.urlParseQueryString(res); + for (var key in webViewEvent.responseUnsafe) { + var val = webViewEvent.responseUnsafe[key]; + try { + if ( + (val.substr(0, 1) == '{' && val.substr(-1) == '}') || + (val.substr(0, 1) == '[' && val.substr(-1) == ']') + ) { + webViewEvent.responseUnsafe[key] = JSON.parse(val); + } + } catch (e) { + // + } + } + } + if (requestData.callback) { + requestData.callback(requestSent, webViewEvent); + } + receiveWebViewEvent('contactRequested', webViewEvent); + }, 3000); + } else { + if (requestData.callback) { + requestData.callback(requestSent, webViewEvent); + } + receiveWebViewEvent('contactRequested', webViewEvent); + } + } + } + + function onCustomMethodInvoked(eventType, eventData) { + if (eventData.req_id && webAppCallbacks[eventData.req_id]) { + var requestData = webAppCallbacks[eventData.req_id]; + delete webAppCallbacks[eventData.req_id]; + var res = null, + err = null; + if (typeof eventData.result !== 'undefined') { + res = eventData.result; + } + if (typeof eventData.error !== 'undefined') { + err = eventData.error; + } + if (requestData.callback) { + requestData.callback(err, res); + } + } + } + + function invokeCustomMethod(method, params, callback) { + if (!versionAtLeast('6.9')) { + console.error('[Telegram.WebApp] Method invokeCustomMethod is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + var req_id = generateCallbackId(16); + var req_params = { req_id: req_id, method: method, params: params || {} }; + webAppCallbacks[req_id] = { + callback: callback, + }; + WebView.postEvent('web_app_invoke_custom_method', false, req_params); + } + + if (!window.Telegram) { + window.Telegram = {}; + } + + Object.defineProperty(WebApp, 'initData', { + get: function () { + return webAppInitData; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'initDataUnsafe', { + get: function () { + return webAppInitDataUnsafe; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'version', { + get: function () { + return webAppVersion; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'platform', { + get: function () { + return webAppPlatform; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'colorScheme', { + get: function () { + return colorScheme; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'themeParams', { + get: function () { + return themeParams; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'isExpanded', { + get: function () { + return isExpanded; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'viewportHeight', { + get: function () { + return (viewportHeight === false ? window.innerHeight : viewportHeight) - mainButtonHeight; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'viewportStableHeight', { + get: function () { + return (viewportStableHeight === false ? window.innerHeight : viewportStableHeight) - mainButtonHeight; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'isClosingConfirmationEnabled', { + set: function (val) { + setClosingConfirmation(val); + }, + get: function () { + return isClosingConfirmationEnabled; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'isVerticalSwipesEnabled', { + set: function (val) { + toggleVerticalSwipes(val); + }, + get: function () { + return isVerticalSwipesEnabled; + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'headerColor', { + set: function (val) { + setHeaderColor(val); + }, + get: function () { + return getHeaderColor(); + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'backgroundColor', { + set: function (val) { + setBackgroundColor(val); + }, + get: function () { + return getBackgroundColor(); + }, + enumerable: true, + }); + Object.defineProperty(WebApp, 'BackButton', { + value: BackButton, + enumerable: true, + }); + Object.defineProperty(WebApp, 'MainButton', { + value: MainButton, + enumerable: true, + }); + Object.defineProperty(WebApp, 'SettingsButton', { + value: SettingsButton, + enumerable: true, + }); + Object.defineProperty(WebApp, 'HapticFeedback', { + value: HapticFeedback, + enumerable: true, + }); + Object.defineProperty(WebApp, 'CloudStorage', { + value: CloudStorage, + enumerable: true, + }); + Object.defineProperty(WebApp, 'BiometricManager', { + value: BiometricManager, + enumerable: true, + }); + WebApp.setHeaderColor = function (color_key) { + WebApp.headerColor = color_key; + }; + WebApp.setBackgroundColor = function (color) { + WebApp.backgroundColor = color; + }; + WebApp.enableClosingConfirmation = function () { + WebApp.isClosingConfirmationEnabled = true; + }; + WebApp.disableClosingConfirmation = function () { + WebApp.isClosingConfirmationEnabled = false; + }; + WebApp.enableVerticalSwipes = function () { + WebApp.isVerticalSwipesEnabled = true; + }; + WebApp.disableVerticalSwipes = function () { + WebApp.isVerticalSwipesEnabled = false; + }; + WebApp.isVersionAtLeast = function (ver) { + return versionAtLeast(ver); + }; + WebApp.onEvent = function (eventType, callback) { + onWebViewEvent(eventType, callback); + }; + WebApp.offEvent = function (eventType, callback) { + offWebViewEvent(eventType, callback); + }; + WebApp.sendData = function (data) { + if (!data || !data.length) { + console.error('[Telegram.WebApp] Data is required', data); + throw Error('WebAppDataInvalid'); + } + if (byteLength(data) > 4096) { + console.error('[Telegram.WebApp] Data is too long', data); + throw Error('WebAppDataInvalid'); + } + WebView.postEvent('web_app_data_send', false, { data: data }); + }; + WebApp.switchInlineQuery = function (query, choose_chat_types) { + if (!versionAtLeast('6.6')) { + console.error('[Telegram.WebApp] Method switchInlineQuery is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + if (!initParams.tgWebAppBotInline) { + console.error( + '[Telegram.WebApp] Inline mode is disabled for this bot. Read more about inline mode: https://core.telegram.org/bots/inline', + ); + throw Error('WebAppInlineModeDisabled'); + } + query = query || ''; + if (query.length > 256) { + console.error('[Telegram.WebApp] Inline query is too long', query); + throw Error('WebAppInlineQueryInvalid'); + } + var chat_types = []; + if (choose_chat_types) { + if (!Array.isArray(choose_chat_types)) { + console.error('[Telegram.WebApp] Choose chat types should be an array', choose_chat_types); + throw Error('WebAppInlineChooseChatTypesInvalid'); + } + var good_types = { users: 1, bots: 1, groups: 1, channels: 1 }; + for (var i = 0; i < choose_chat_types.length; i++) { + var chat_type = choose_chat_types[i]; + if (!good_types[chat_type]) { + console.error('[Telegram.WebApp] Choose chat type is invalid', chat_type); + throw Error('WebAppInlineChooseChatTypeInvalid'); + } + if (good_types[chat_type] != 2) { + good_types[chat_type] = 2; + chat_types.push(chat_type); + } + } + } + WebView.postEvent('web_app_switch_inline_query', false, { + query: query, + chat_types: chat_types, + }); + }; + WebApp.openLink = function (url, options) { + var a = document.createElement('A'); + a.href = url; + if (a.protocol != 'http:' && a.protocol != 'https:') { + console.error('[Telegram.WebApp] Url protocol is not supported', url); + throw Error('WebAppTgUrlInvalid'); + } + // eslint-disable-next-line no-redeclare + var url = a.href; + options = options || {}; + if (versionAtLeast('6.1')) { + var req_params = { url: url }; + if (versionAtLeast('6.4') && options.try_instant_view) { + req_params.try_instant_view = true; + } + if (versionAtLeast('7.6') && options.try_browser) { + req_params.try_browser = options.try_browser; + } + WebView.postEvent('web_app_open_link', false, req_params); + } else { + window.open(url, '_blank'); + } + }; + WebApp.openTelegramLink = function (url) { + var a = document.createElement('A'); + a.href = url; + if (a.protocol != 'http:' && a.protocol != 'https:') { + console.error('[Telegram.WebApp] Url protocol is not supported', url); + throw Error('WebAppTgUrlInvalid'); + } + if (a.hostname != 't.me') { + console.error('[Telegram.WebApp] Url host is not supported', url); + throw Error('WebAppTgUrlInvalid'); + } + var path_full = a.pathname + a.search; + if (isIframe || versionAtLeast('6.1')) { + WebView.postEvent('web_app_open_tg_link', false, { path_full: path_full }); + } else { + location.href = 'https://t.me' + path_full; + } + }; + WebApp.openInvoice = function (url, callback) { + var a = document.createElement('A'), + match, + slug; + a.href = url; + if ( + (a.protocol != 'http:' && a.protocol != 'https:') || + a.hostname != 't.me' || + !(match = a.pathname.match(/^\/(\$|invoice\/)([A-Za-z0-9\-_=]+)$/)) || + !(slug = match[2]) + ) { + console.error('[Telegram.WebApp] Invoice url is invalid', url); + throw Error('WebAppInvoiceUrlInvalid'); + } + if (!versionAtLeast('6.1')) { + console.error('[Telegram.WebApp] Method openInvoice is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + if (webAppInvoices[slug]) { + console.error('[Telegram.WebApp] Invoice is already opened'); + throw Error('WebAppInvoiceOpened'); + } + webAppInvoices[slug] = { + url: url, + callback: callback, + }; + WebView.postEvent('web_app_open_invoice', false, { slug: slug }); + }; + WebApp.showPopup = function (params, callback) { + if (!versionAtLeast('6.2')) { + console.error('[Telegram.WebApp] Method showPopup is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + if (webAppPopupOpened) { + console.error('[Telegram.WebApp] Popup is already opened'); + throw Error('WebAppPopupOpened'); + } + var title = ''; + var message = ''; + var buttons = []; + var popup_buttons = {}; + var popup_params = {}; + if (typeof params.title !== 'undefined') { + title = strTrim(params.title); + if (title.length > 64) { + console.error('[Telegram.WebApp] Popup title is too long', title); + throw Error('WebAppPopupParamInvalid'); + } + if (title.length > 0) { + popup_params.title = title; + } + } + if (typeof params.message !== 'undefined') { + message = strTrim(params.message); + } + if (!message.length) { + console.error('[Telegram.WebApp] Popup message is required', params.message); + throw Error('WebAppPopupParamInvalid'); + } + if (message.length > 256) { + console.error('[Telegram.WebApp] Popup message is too long', message); + throw Error('WebAppPopupParamInvalid'); + } + popup_params.message = message; + if (typeof params.buttons !== 'undefined') { + if (!Array.isArray(params.buttons)) { + console.error('[Telegram.WebApp] Popup buttons should be an array', params.buttons); + throw Error('WebAppPopupParamInvalid'); + } + for (var i = 0; i < params.buttons.length; i++) { + var button = params.buttons[i]; + var btn = {}; + var id = ''; + if (typeof button.id !== 'undefined') { + id = button.id.toString(); + if (id.length > 64) { + console.error('[Telegram.WebApp] Popup button id is too long', id); + throw Error('WebAppPopupParamInvalid'); + } + } + btn.id = id; + var button_type = button.type; + if (typeof button_type === 'undefined') { + button_type = 'default'; + } + btn.type = button_type; + if (button_type == 'ok' || button_type == 'close' || button_type == 'cancel') { + // no params needed + } else if (button_type == 'default' || button_type == 'destructive') { + var text = ''; + if (typeof button.text !== 'undefined') { + text = strTrim(button.text); + } + if (!text.length) { + console.error('[Telegram.WebApp] Popup button text is required for type ' + button_type, button.text); + throw Error('WebAppPopupParamInvalid'); + } + if (text.length > 64) { + console.error('[Telegram.WebApp] Popup button text is too long', text); + throw Error('WebAppPopupParamInvalid'); + } + btn.text = text; + } else { + console.error('[Telegram.WebApp] Popup button type is invalid', button_type); + throw Error('WebAppPopupParamInvalid'); + } + buttons.push(btn); + } + } else { + buttons.push({ id: '', type: 'close' }); + } + if (buttons.length < 1) { + console.error('[Telegram.WebApp] Popup should have at least one button'); + throw Error('WebAppPopupParamInvalid'); + } + if (buttons.length > 3) { + console.error('[Telegram.WebApp] Popup should not have more than 3 buttons'); + throw Error('WebAppPopupParamInvalid'); + } + popup_params.buttons = buttons; + + webAppPopupOpened = { + callback: callback, + }; + WebView.postEvent('web_app_open_popup', false, popup_params); + }; + WebApp.showAlert = function (message, callback) { + WebApp.showPopup( + { + message: message, + }, + callback + ? function () { + callback(); + } + : null, + ); + }; + WebApp.showConfirm = function (message, callback) { + WebApp.showPopup( + { + message: message, + buttons: [{ type: 'ok', id: 'ok' }, { type: 'cancel' }], + }, + callback + ? function (button_id) { + callback(button_id == 'ok'); + } + : null, + ); + }; + WebApp.showScanQrPopup = function (params, callback) { + if (!versionAtLeast('6.4')) { + console.error('[Telegram.WebApp] Method showScanQrPopup is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + if (webAppScanQrPopupOpened) { + console.error('[Telegram.WebApp] Popup is already opened'); + throw Error('WebAppScanQrPopupOpened'); + } + var text = ''; + var popup_params = {}; + if (typeof params.text !== 'undefined') { + text = strTrim(params.text); + if (text.length > 64) { + console.error('[Telegram.WebApp] Scan QR popup text is too long', text); + throw Error('WebAppScanQrPopupParamInvalid'); + } + if (text.length > 0) { + popup_params.text = text; + } + } + + webAppScanQrPopupOpened = { + callback: callback, + }; + WebView.postEvent('web_app_open_scan_qr_popup', false, popup_params); + }; + WebApp.closeScanQrPopup = function () { + if (!versionAtLeast('6.4')) { + console.error('[Telegram.WebApp] Method closeScanQrPopup is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + + webAppScanQrPopupOpened = false; + WebView.postEvent('web_app_close_scan_qr_popup', false); + }; + WebApp.readTextFromClipboard = function (callback) { + if (!versionAtLeast('6.4')) { + console.error('[Telegram.WebApp] Method readTextFromClipboard is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + var req_id = generateCallbackId(16); + var req_params = { req_id: req_id }; + webAppCallbacks[req_id] = { + callback: callback, + }; + WebView.postEvent('web_app_read_text_from_clipboard', false, req_params); + }; + WebApp.requestWriteAccess = function (callback) { + if (!versionAtLeast('6.9')) { + console.error('[Telegram.WebApp] Method requestWriteAccess is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + if (WebAppWriteAccessRequested) { + console.error('[Telegram.WebApp] Write access is already requested'); + throw Error('WebAppWriteAccessRequested'); + } + WebAppWriteAccessRequested = { + callback: callback, + }; + WebView.postEvent('web_app_request_write_access'); + }; + WebApp.requestContact = function (callback) { + if (!versionAtLeast('6.9')) { + console.error('[Telegram.WebApp] Method requestContact is not supported in version ' + webAppVersion); + throw Error('WebAppMethodUnsupported'); + } + if (WebAppContactRequested) { + console.error('[Telegram.WebApp] Contact is already requested'); + throw Error('WebAppContactRequested'); + } + WebAppContactRequested = { + callback: callback, + }; + WebView.postEvent('web_app_request_phone'); + }; + WebApp.invokeCustomMethod = function (method, params, callback) { + invokeCustomMethod(method, params, callback); + }; + WebApp.ready = function () { + WebView.postEvent('web_app_ready'); + }; + WebApp.expand = function () { + WebView.postEvent('web_app_expand'); + }; + WebApp.close = function (options) { + options = options || {}; + var req_params = {}; + if (versionAtLeast('7.6') && options.return_back) { + req_params.return_back = true; + } + WebView.postEvent('web_app_close', false, req_params); + }; + + window.Telegram.WebApp = WebApp; + + updateHeaderColor(); + updateBackgroundColor(); + setViewportHeight(); + if (initParams.tgWebAppShowSettings) { + SettingsButton.show(); + } + + window.addEventListener('resize', onWindowResize); + if (isIframe) { + document.addEventListener('click', linkHandler); + } + + WebView.onEvent('theme_changed', onThemeChanged); + WebView.onEvent('viewport_changed', onViewportChanged); + WebView.onEvent('invoice_closed', onInvoiceClosed); + WebView.onEvent('popup_closed', onPopupClosed); + WebView.onEvent('qr_text_received', onQrTextReceived); + WebView.onEvent('scan_qr_popup_closed', onScanQrPopupClosed); + WebView.onEvent('clipboard_text_received', onClipboardTextReceived); + WebView.onEvent('write_access_requested', onWriteAccessRequested); + WebView.onEvent('phone_requested', onPhoneRequested); + WebView.onEvent('custom_method_invoked', onCustomMethodInvoked); + WebView.postEvent('web_app_request_theme'); + WebView.postEvent('web_app_request_viewport'); +})(); diff --git a/src/utils/telegram/telegram.ts b/src/utils/telegram/telegram.ts new file mode 100644 index 00000000..d2750ba6 --- /dev/null +++ b/src/utils/telegram/telegram.ts @@ -0,0 +1,39 @@ +declare const window: Window & + typeof globalThis & { + Telegram?: ITelegram; + }; + +export class TelegramPlatform { + static getTelegram() { + return typeof window !== 'undefined' ? window?.Telegram : undefined; + } + + static getWebApp() { + return TelegramPlatform.getTelegram()?.WebApp; + } + + static isTelegramPlatform() { + const Telegram = TelegramPlatform.getTelegram(); + return !!(Telegram && Telegram.WebApp?.platform && Telegram.WebApp?.platform !== 'unknown'); + } + + static isTelegramPlatformWeb() { + const Telegram = TelegramPlatform.getTelegram(); + return !!(Telegram && Telegram.WebApp?.platform && Telegram.WebApp?.platform === 'web'); + } + + static isTelegramPlatformAndNotWeb() { + const Telegram = TelegramPlatform.getTelegram(); + return !!( + Telegram && + Telegram.WebApp?.platform && + Telegram.WebApp.platform !== 'unknown' && + Telegram.WebApp.platform !== 'web' + ); + } + + static isTelegramPlatformDesktop() { + const Telegram = TelegramPlatform.getTelegram(); + return !!(Telegram && Telegram.WebApp?.platform && Telegram.WebApp.platform.includes('desktop')); + } +} diff --git a/src/utils/themes.ts b/src/utils/themes.ts new file mode 100644 index 00000000..dcb6cc84 --- /dev/null +++ b/src/utils/themes.ts @@ -0,0 +1,10 @@ +import { Themes } from 'constants/theme'; +export function getThemes() { + const html = window.document.getElementsByTagName('html')[0]; + return html.getAttribute('data-theme'); +} +export function setThemes(theme: keyof typeof Themes) { + const html = window.document.getElementsByTagName('html')[0]; + const cTheme = getThemes(); + if (cTheme !== theme) html.setAttribute('data-theme', theme); +} diff --git a/src/utils/time.ts b/src/utils/time.ts new file mode 100644 index 00000000..efdc1d94 --- /dev/null +++ b/src/utils/time.ts @@ -0,0 +1,33 @@ +import { CrossChainTimeList } from 'constants/index'; +import Moment from 'moment'; +import { ChainId } from 'types'; +import { isIncludesChainId } from 'utils'; + +export const getMillisecond = (time: any) => { + const { seconds } = time || {}; + const tim = seconds || time; + if (String(tim).length <= 10) { + return tim * 1000; + } + if (typeof tim !== 'number') { + return Number(tim); + } + return tim; +}; +export function formatTime(t: string | number | Date, formats = 'YYYY MMM DD, HH:mm:ss') { + if (t && typeof t === 'string' && !t.includes('T') && !t.includes('-')) + return Moment(getMillisecond(t)).format(formats); + return Moment(t).format(formats); +} + +export function getCrossChainTime(fromChainId?: ChainId, toChainId?: ChainId) { + const item = CrossChainTimeList.find( + (i) => isIncludesChainId(i.fromChainId, fromChainId) && isIncludesChainId(i.toChainId, toChainId), + ); + return item?.time || '10'; +} + +export function getFullYear(): number { + const data: Date = new Date(); + return data.getFullYear(); +} diff --git a/src/utils/token.ts b/src/utils/token.ts new file mode 100644 index 00000000..7ba014a5 --- /dev/null +++ b/src/utils/token.ts @@ -0,0 +1,19 @@ +import { NATIVE_TOKEN_LIST } from 'constants/index'; +import { SYMBOL_FORMAT_MAP } from 'constants/misc'; +import { API_TOKEN_MAP } from 'constants/token'; + +export const formatSymbol = (symbol = '') => { + if (SYMBOL_FORMAT_MAP[symbol]) return SYMBOL_FORMAT_MAP[symbol]; + return symbol; +}; + +export const formatAPISymbol = (symbol = '') => { + if (API_TOKEN_MAP[symbol]) return API_TOKEN_MAP[symbol]; + return symbol; +}; + +export const formatSymbolAndNativeToken = (symbol = '') => { + if (SYMBOL_FORMAT_MAP[symbol]) return SYMBOL_FORMAT_MAP[symbol]; + if (NATIVE_TOKEN_LIST.includes(symbol)) return symbol?.replace('W', ''); + return symbol; +}; diff --git a/src/utils/ton.ts b/src/utils/ton.ts new file mode 100644 index 00000000..cfaf850b --- /dev/null +++ b/src/utils/ton.ts @@ -0,0 +1,57 @@ +import { IS_MAINNET } from 'constants/index'; +import TonWeb from 'tonweb'; +import * as TON_TESTNET from 'constants/platform/TON_Test'; +import * as TON_MAINNET from 'constants/platform/TON'; +import { Address, beginCell } from '@ton/core'; +import { SendTransactionResponse } from '@tonconnect/ui-react'; +const mainnetTonWeb = new TonWeb(new TonWeb.HttpProvider(TON_MAINNET.CHAIN_INFO.rpcUrl)); +const testnetTonWeb = new TonWeb(new TonWeb.HttpProvider(TON_TESTNET.CHAIN_INFO.rpcUrl)); +const defaultGetBalanceTonWeb = IS_MAINNET ? new TonWeb() : testnetTonWeb; + +export const TON_WEB = IS_MAINNET ? mainnetTonWeb : testnetTonWeb; +export const getTONJettonMinter = (tokenContractAddress: string, inlineTonWeb?: TonWeb) => { + const jettonMinter = new TonWeb.token.jetton.JettonMinter((inlineTonWeb || TON_WEB).provider, { + address: tokenContractAddress, + } as any); + return jettonMinter; +}; + +export const getTonChainBalance = async (contractAddress: string, address: string) => { + const jettonWalletAddress = await getJettonWalletAddress(contractAddress, address, defaultGetBalanceTonWeb); + const jettonWallet = new TonWeb.token.jetton.JettonWallet(defaultGetBalanceTonWeb.provider, { + address: jettonWalletAddress, + }); + const res = await jettonWallet.getData(); + return res.balance.toString(); +}; + +export const packCreateReceiptBody = (targetChainId: number, targetAddress: Buffer, jettonAddress: Address) => { + const payload = beginCell() + .storeUint(0x71a12142, 32) // 0x71a12142 + .storeUint(targetChainId, 32) + .storeBuffer(targetAddress, 32) + .storeAddress(jettonAddress) + .endCell(); + return payload; +}; + +export async function getTransactionResponseHash(result: SendTransactionResponse) { + const bocCellBytes = await TonWeb.boc.Cell.oneFromBoc(TonWeb.utils.base64ToBytes(result.boc)).hash(); + return TonWeb.utils.bytesToBase64(bocCellBytes); +} + +export function isTonAddress(addr: string) { + return Address.isFriendly(addr) || Address.isAddress(addr); +} + +const JettonWalletAddressMap: any = {}; + +export async function getJettonWalletAddress(tokenContractAddress: string, account: string, inlineTonWeb?: TonWeb) { + const key = (inlineTonWeb || TON_WEB).provider.host + tokenContractAddress + account; + + if (!JettonWalletAddressMap[key]) { + const jettonMinter = getTONJettonMinter(tokenContractAddress, inlineTonWeb); + JettonWalletAddressMap[key] = (await jettonMinter.getJettonWalletAddress(new TonWeb.utils.Address(account))) as any; + } + return JettonWalletAddressMap[key]; +} diff --git a/src/utils/tonContractCall.ts b/src/utils/tonContractCall.ts new file mode 100644 index 00000000..6547f448 --- /dev/null +++ b/src/utils/tonContractCall.ts @@ -0,0 +1,85 @@ +import TonWeb from 'tonweb'; +import { Address, beginCell, toNano } from '@ton/core'; +import { base58ToChainId } from './chain'; +import { ChainId } from 'types'; +import AElf from 'aelf-sdk'; +import { ZERO } from 'constants/misc'; +import { CHAIN, SendTransactionRequest } from '@tonconnect/ui-react'; +import { getTONJettonMinter, packCreateReceiptBody, TON_WEB } from './ton'; +export class TonContractCallData { + static async createReceipt(contractAddress: string, account: string, paramsOption: any[]) { + const forwardTonAmount = '0.15'; + const feeAmount = '0.3'; + const tokenJettonAddress = paramsOption[0]; + + const jettonMinter = getTONJettonMinter(tokenJettonAddress); + + const jettonWalletAddress = await jettonMinter.getJettonWalletAddress(new TonWeb.utils.Address(account)); + + const createReceiptPayload = packCreateReceiptBody( + base58ToChainId(paramsOption[2].slice(-4) as ChainId), // paramsOption[2] + // to address + AElf.utils.base58.decode(paramsOption[3]), + Address.parse(tokenJettonAddress), + ); + + const payload = beginCell() + .storeUint(0xf8a7ea5, 32) // op transfer + .storeUint(0, 64) // queryId + .storeCoins(ZERO.plus(paramsOption[1]).toNumber()) // transaction amount + .storeAddress(Address.parse(contractAddress)) // contract address + .storeAddress(Address.parse(account)) // TON wallet destination address response excess destination + .storeMaybeRef(beginCell().storeUint(0, 1).endCell()) + .storeCoins(toNano(forwardTonAmount)) // forward_ton_amount:(VarUInteger 16) - if >0, will send notification message + .storeMaybeRef(createReceiptPayload) + .endCell(); + + const transaction: SendTransactionRequest = { + validUntil: Math.floor(Date.now() / 1000) + 360, // 360 sec + network: CHAIN.TESTNET, + messages: [ + { + address: jettonWalletAddress.toString(), // eg: USDT wallet address + // unit is nanoton. for contract calls, the transaction amount is usually 0 + // for jetton transaction, the amount is the max ton coins fee + amount: toNano(feeAmount).toString(), + payload: payload.toBoc().toString('base64'), + }, + ], + }; + return transaction; + } +} + +export class CallTonContract { + static async getReceiptDailyLimit(contractAddress: string, paramsOption: any[]) { + const req = await TON_WEB.provider.call(contractAddress, 'get_receipt_daily_limit', [ + ['num', base58ToChainId(paramsOption[1].slice(-4) as ChainId)], + ]); + return { + tokenAmount: ZERO.plus(req.stack[0][1]), + refreshTime: ZERO.plus(req.stack[1][1]), + dailyLimit: ZERO.plus(req.stack[2][1]), + }; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + static async GetTokenPoolInfo(contractAddress: string, _paramsOption: any[]) { + const req = await TON_WEB.provider.call(contractAddress, 'get_pool_liquidity', []); + return { + liquidity: ZERO.plus(req.stack[0][1]).toFixed(), + }; + } + static async getCurrentReceiptTokenBucketState(contractAddress: string, paramsOption: any[]) { + const req = await TON_WEB.provider.call(contractAddress, 'get_receipt_rate_limit_state', [ + ['num', base58ToChainId(paramsOption[1].slice(-4) as ChainId)], + ]); + + return { + currentTokenAmount: ZERO.plus(req.stack[0][1]), + refreshTime: ZERO.plus(req.stack[1][1]), + tokenCapacity: ZERO.plus(req.stack[2][1]), + isEnable: ZERO.plus(req.stack[3][1]).toNumber() === -1, + rate: ZERO.plus(req.stack[4][1]), + }; + } +} diff --git a/src/utils/transfers.ts b/src/utils/transfers.ts new file mode 100644 index 00000000..88e7caf0 --- /dev/null +++ b/src/utils/transfers.ts @@ -0,0 +1,18 @@ +import type { APICrossChainItem, CrossChainItem, TokensToken } from 'types/api'; +import { getChainIdByAPI } from 'utils/chain'; +import { formatAPISymbol } from './token'; + +export function parseCrossChainTransfers(req?: { + items: APICrossChainItem[] | undefined; +}): CrossChainItem[] | undefined { + return req?.items?.map(({ toChainId, fromChainId, transferToken, ...item }) => { + const data: CrossChainItem = { + toChainId: getChainIdByAPI(toChainId), + fromChainId: getChainIdByAPI(fromChainId), + ...item, + }; + if (transferToken) + data.transferToken = { ...transferToken, symbol: formatAPISymbol(transferToken?.symbol) } as TokensToken; + return data; + }); +} diff --git a/src/utils/vconsole.ts b/src/utils/vconsole.ts new file mode 100644 index 00000000..c3b04aeb --- /dev/null +++ b/src/utils/vconsole.ts @@ -0,0 +1,4 @@ +import('vconsole').then((VConsole) => { + new VConsole.default(); +}); +export {}; diff --git a/src/utils/wagmi.ts b/src/utils/wagmi.ts new file mode 100644 index 00000000..d4a23252 --- /dev/null +++ b/src/utils/wagmi.ts @@ -0,0 +1,117 @@ +'use client'; +import { + getTransactionReceipt, + GetTransactionReceiptParameters, + getBalance, + GetBalanceParameters, + GetTransactionReceiptReturnType, + readContract, + ReadContractReturnType, + writeContract, + WriteContractReturnType, + getGasPrice, + GetGasPriceReturnType, + waitForTransactionReceipt, + WaitForTransactionReceiptReturnType, + getAccount, + switchChain, + SwitchChainReturnType, + getClient, + GetClientReturnType, +} from '@wagmi/core'; +import { EVMProviderConfig } from 'constants/evm'; +import { sleep } from 'utils'; +import { handleErrorMessage } from './error'; + +const PENDING_MESSAGE = 'The Transaction may not be processed on a block yet.'; + +export async function getTransactionReceiptAutoRetry( + params: GetTransactionReceiptParameters, + reGetCount = 0, + notExistedReGetCount = 0, +): Promise { + try { + const req = await getTransactionReceipt(EVMProviderConfig, params); + console.log('!! getTransactionReceiptAutoRetry req', req); + if (req.status === 'success') return req; + if (req.status === 'reverted') throw { message: 'Transaction is reverted', req }; + reGetCount++; + await sleep(3000); + return getTransactionReceiptAutoRetry(params, reGetCount, notExistedReGetCount); + } catch (error: any) { + console.log('!! getTransactionReceiptAutoRetry error', error); + if (handleErrorMessage(error)?.includes(PENDING_MESSAGE)) { + if (notExistedReGetCount > 200) throw error; + notExistedReGetCount++; + await sleep(3000); + return getTransactionReceiptAutoRetry(params, reGetCount, notExistedReGetCount); + } + throw error; + } +} + +export async function getBalanceByWagmi(params: GetBalanceParameters) { + return getBalance(EVMProviderConfig, params); +} + +export type TReadContractByWagmiParams = { + address: string; + abi?: any; + functionName: string; + args?: any[]; + chainId?: GetBalanceParameters['chainId']; + blockNumber?: number; +}; + +export async function readContractByWagmi(params: TReadContractByWagmiParams): Promise { + return await readContract(EVMProviderConfig, params as any); +} + +export type TWriteContractByWagmiParams = { + address: string; + account: `0x${string}`; + abi?: any; + functionName: string; + args?: any[]; + chainId?: number; + gasPrice?: bigint; + gas?: bigint; + value?: bigint; + nonce?: number; +}; + +export async function writeContractByWagmi(params: TWriteContractByWagmiParams): Promise { + const { connector } = getAccount(EVMProviderConfig); + return await writeContract(EVMProviderConfig, { ...params, connector, __mode: 'prepared' } as any); +} + +export type TGetGasPriceByWagmiParams = { + chainId?: number; +}; + +export async function getGasPriceByWagmi(params: TGetGasPriceByWagmiParams): Promise { + return await getGasPrice(EVMProviderConfig, params as any); +} + +export type TWaitForTransactionReceiptByWagmiParams = { + hash: string; + chainId?: number; +}; + +export async function waitForTransactionReceiptByWagmi( + params: TWaitForTransactionReceiptByWagmiParams, +): Promise { + return await waitForTransactionReceipt(EVMProviderConfig, params as any); +} + +export type TSwitchChainByWagmiParams = { + chainId: number; +}; + +export async function switchChainByWagmi(params: TSwitchChainByWagmiParams): Promise { + return await switchChain(EVMProviderConfig, params as any); +} + +export async function getClientByWagmi(chainId: any): Promise { + return getClient(EVMProviderConfig, chainId as any); +} diff --git a/src/utils/wallet.ts b/src/utils/wallet.ts new file mode 100644 index 00000000..92ebb865 --- /dev/null +++ b/src/utils/wallet.ts @@ -0,0 +1,36 @@ +import { CHAIN_ID_MAP, SupportedChainId, SupportedTONChainId } from 'constants/chain'; +import { SupportedELFChainList } from 'constants/index'; + +export type TAccounts = Record; + +export const getPortkeySDKAccount = (accounts?: Record): TAccounts => { + if (!accounts) throw new Error('getPortkeyV2SDKAccount: accounts invalid'); + const _accounts: TAccounts = {}; + const address = Object.values(accounts)[0] || ''; + SupportedELFChainList.forEach((chain) => { + const _chainId = chain.CHAIN_INFO.chainId as string; + _accounts[_chainId] = [`ELF_${address}_${_chainId}`]; + }); + return _accounts; +}; + +export const isEVMChain = (network: string) => { + const evmChainIds = [ + SupportedChainId.MAINNET, + SupportedChainId.BSC_MAINNET, + SupportedChainId.KOVAN, + SupportedChainId.GORELI, + SupportedChainId.BSC_TESTNET, + SupportedChainId.SEPOLIA, + SupportedChainId.BASE_SEPOLIA, + SupportedChainId.BASE, + ] as const; + + return evmChainIds.some((chainId) => CHAIN_ID_MAP[chainId] === network); +}; + +export const isTONChain = (network: string) => { + const tonChainIds = [SupportedTONChainId.TESTNET, SupportedTONChainId.MAINNET] as const; + + return tonChainIds.some((chainId) => CHAIN_ID_MAP[chainId] === network); +}; diff --git a/src/utils/whitelist.ts b/src/utils/whitelist.ts new file mode 100644 index 00000000..db73a641 --- /dev/null +++ b/src/utils/whitelist.ts @@ -0,0 +1,16 @@ +import { BRIDGE_TOKEN_MAP } from 'constants/index'; +import storages from 'constants/storages'; +import { ChainId, TokenInfo } from 'types'; + +export function getTokenInfoByWhitelist(chainId?: ChainId, symbol?: string): undefined | TokenInfo { + if (!chainId || !symbol) return; + try { + const data = localStorage.getItem(storages.useWhitelist); + if (!data) return; + const defaultWhitelistMap = JSON.parse(data).defaultWhitelistMap; + const tokenInfo = (defaultWhitelistMap as any)[BRIDGE_TOKEN_MAP?.[symbol] || symbol]?.[chainId]; + return tokenInfo; + } catch (error) { + console.debug(error, 'getTokenInfoByWhitelist'); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100755 index 00000000..e6ad21c8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "baseUrl": "src", + "incremental": true, + "types": ["@testing-library/jest-dom", "vitest/globals"] + }, + "include": ["src", "next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/vite.config.mts b/vite.config.mts new file mode 100644 index 00000000..73135b15 --- /dev/null +++ b/vite.config.mts @@ -0,0 +1,64 @@ +/// +import { defineConfig, configDefaults } from 'vitest/config'; +import tsconfigPaths from 'vite-tsconfig-paths'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [tsconfigPaths(), react()], + test: { + environment: 'happy-dom', + setupFiles: ['./vitest.setup'], + coverage: { + all: false, + enabled: true, + provider: 'v8', + include: [ + 'src/utils/**/*.[jt]s?(x)', + 'src/contracts/**/*.[jt]s?(x)', + 'src/contexts/**/*.[jt]s?(x)', + 'src/api/**/*.[jt]s?(x)', + ], + exclude: [ + ...configDefaults.exclude, + '**/dist/*.*', + '**/.*', + '**/*.setup.*', + '**/__tests__/*.*', + 'src/utils/telegram/telegram-web-app.js', + ], + reportsDirectory: './coverage', + reporter: [['json-summary', { file: 'coverage-summary.json' }], ['lcov', { file: 'lcov.info' }], ['text']], + }, + pool: 'vmThreads', + poolOptions: { + vmThreads: { + useAtomics: true, + }, + }, + deps: { + optimizer: { + web: { + enabled: true, + include: [], + }, + }, + moduleDirectories: ['node_modules'], + }, + server: { + deps: { + inline: ['@aelf-web-login', '@portkey/did-ui-react'], + }, + }, + globals: true, + css: false, + isolate: true, // only safe with the poolOptions above + reporters: ['junit', 'default'], + outputFile: { + junit: './jest-report.xml', + }, + // include: ['./src/**/*(*.)?{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + }, + worker: { + format: 'es', + }, +}); diff --git a/vitest.setup.ts b/vitest.setup.ts new file mode 100644 index 00000000..752cca67 --- /dev/null +++ b/vitest.setup.ts @@ -0,0 +1,110 @@ +import '@testing-library/jest-dom/vitest'; +import { cleanup } from '@testing-library/react'; +import { afterEach, beforeAll, vi } from 'vitest'; +import React from 'react'; + +// JSDom + Vitest don't play well with each other. Long story short - default +// TextEncoder produces Uint8Array objects that are _different_ from the global +// Uint8Array objects, so some functions that compare their types explode. +// https://github.com/vitest-dev/vitest/issues/4043#issuecomment-1905172846 +class ESBuildAndJSDOMCompatibleTextEncoder extends TextEncoder { + constructor() { + super(); + } + + encode(input: string) { + if (typeof input !== 'string') { + throw new TypeError('`input` must be a string'); + } + + const decodedURI = decodeURIComponent(encodeURIComponent(input)); + const arr = new Uint8Array(decodedURI.length); + const chars = decodedURI.split(''); + for (let i = 0; i < chars.length; i++) { + arr[i] = decodedURI[i].charCodeAt(0); + } + return arr; + } +} + +global.TextEncoder = ESBuildAndJSDOMCompatibleTextEncoder; +global.React = React; // this also works for other globally available libraries + +beforeAll(() => { + global.ResizeObserver = class ResizeObserver { + observe() { + // do nothing + } + unobserve() { + // do nothing + } + disconnect() { + // do nothing + } + }; +}); + +afterEach(() => { + cleanup(); + vi.clearAllMocks(); +}); + +// https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom +if (typeof window !== 'undefined') { + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), // deprecated + removeListener: vi.fn(), // deprecated + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), + }); +} +// https://github.com/wobsoriano/vitest-canvas-mock/issues/16 +if (typeof HTMLCanvasElement !== 'undefined') { + HTMLCanvasElement.prototype.getContext = vi.fn().mockReturnValue({ + fillRect: vi.fn(), + clearRect: vi.fn(), + getImageData: vi.fn().mockReturnValue({ data: [] }), + putImageData: vi.fn(), + createImageData: vi.fn().mockReturnValue([]), + setTransform: vi.fn(), + drawImage: vi.fn(), + save: vi.fn(), + fillText: vi.fn(), + restore: vi.fn(), + beginPath: vi.fn(), + moveTo: vi.fn(), + lineTo: vi.fn(), + closePath: vi.fn(), + stroke: vi.fn(), + translate: vi.fn(), + scale: vi.fn(), + rotate: vi.fn(), + arc: vi.fn(), + fill: vi.fn(), + measureText: vi.fn().mockReturnValue({ width: 0 }), + }); +} + +// https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom +if (typeof window !== 'undefined') { + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), // deprecated + removeListener: vi.fn(), // deprecated + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), + }); +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..697ecab1 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,14274 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@abp/core@~7.4.5": + version "7.4.5" + resolved "https://registry.npmjs.org/@abp/core/-/core-7.4.5.tgz#b3b7c28a53e9ac1d7670f8b4dabb1844828ac950" + integrity sha512-0SJonl2eg+bLqWmbbJzyqaJBeqXdtGL1078bwdB7lyxiXePfMeh2LvvAqMaH6pgllBQw2Emz08aNZH653Rl3dg== + dependencies: + "@abp/utils" "~7.4.5" + +"@abp/signalr@^7.0.0": + version "7.4.5" + resolved "https://registry.npmjs.org/@abp/signalr/-/signalr-7.4.5.tgz#95557fb72fa617ad5fa21cef3d4799b925d40f93" + integrity sha512-p4qKRX4r0IWV34qY0PqWEza+/VPtFl8o/HdAXw90Rnwn5Uiz53qq0TWOleVfLzKIzEiIm9EG1ceC3iSH/6fTMw== + dependencies: + "@abp/core" "~7.4.5" + "@microsoft/signalr" "~6.0.1" + +"@abp/utils@~7.4.5": + version "7.4.5" + resolved "https://registry.npmjs.org/@abp/utils/-/utils-7.4.5.tgz#0fbcdfa3311838236987dbf3d781ee318fc1582c" + integrity sha512-fuAU4wgiAFTW2xXUQFtt4J3dpFN4pa/TDsjVAEdX7fz/du/PDQ8EKV6bqxguLC2mXO3YXFaH8aYAgthH9oB4nA== + dependencies: + just-compare "^2.3.0" + +"@adobe/css-tools@^4.4.0": + version "4.4.2" + resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz#c836b1bd81e6d62cd6cdf3ee4948bcdce8ea79c8" + integrity sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A== + +"@adraffy/ens-normalize@^1.10.1": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" + integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== + +"@aelf-react/core@^0.1.19": + version "0.1.19" + resolved "https://registry.npmjs.org/@aelf-react/core/-/core-0.1.19.tgz#b2052c6c29acfc4abddaed316ab9cc5956420d4d" + integrity sha512-s329rJOfpNJkw9yRkKA4yqk9iLBn0d2tYZolVt82xwThdslGgvkjf2SUsqkfR4p5cqAMzX94Dm9UdBXVtktNCw== + dependencies: + "@aelf-react/types" "^0.1.10" + aelf-bridge "^0.0.10" + +"@aelf-react/types@^0.1.10": + version "0.1.10" + resolved "https://registry.npmjs.org/@aelf-react/types/-/types-0.1.10.tgz#dc8e426401140e138def17623f464ac22dc3489d" + integrity sha512-oKEI07EeL8dhUqp8ze50mCXOqAS5psoOpdmCNZaFT2R62/GPoAyvtN8oaZAUb1tycpE+eUdkdv8XDblEsIIiLA== + +"@aelf-web-login/utils@0.4.0-alpha.10", "@aelf-web-login/utils@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/utils/-/utils-0.4.0-alpha.10.tgz#8a048d510832f66f5667e56f58710fbdf8a91d00" + integrity sha512-VsOrYcS4SFGnpCfm6Vs3RCc8HNndbkKUCugA0K2ZNvo63V5t6cxOeg2uXw6CD6bTZlVTPhu9NXWEibm0T0W23w== + dependencies: + "@aelf-web-login/wallet-adapter-base" "0.4.0-alpha.10" + "@aelf-web-login/wallet-adapter-react" "0.4.0-alpha.10" + bignumber.js "^9.1.2" + dayjs "^1.11.13" + react "^18.3.1" + react-dom "^18.3.1" + +"@aelf-web-login/wallet-adapter-base@0.4.0-alpha.10", "@aelf-web-login/wallet-adapter-base@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/wallet-adapter-base/-/wallet-adapter-base-0.4.0-alpha.10.tgz#8fb9b12950ae68d8502cb4fd779466be2e6e40fe" + integrity sha512-ej3dY6mMS9JYTi5qT+toHDDpbrewPLqoINxpBgIPSHvmvRmSXmYQAh2Xpa73eon/N00tHf88aCmmlHR+VCnNaA== + dependencies: + eventemitter3 "^5.0.1" + +"@aelf-web-login/wallet-adapter-bridge@0.4.0-alpha.10", "@aelf-web-login/wallet-adapter-bridge@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/wallet-adapter-bridge/-/wallet-adapter-bridge-0.4.0-alpha.10.tgz#348ef7e20aff331b176f90c8b30948a9ad3c616e" + integrity sha512-WZdKvK0qZVUe7z9A5O8fubS5qdlNkwPn8xVdSuWO41VQyUBuBeTtd3oIf3R4F68bEa/DpxPdwFRVZIr29pMwOw== + dependencies: + "@aelf-web-login/wallet-adapter-base" "0.4.0-alpha.10" + "@aelf-web-login/wallet-adapter-portkey-web" "0.4.0-alpha.10" + "@portkey/connect-web-wallet" "^2.20.0-alpha.3" + "@portkey/contracts" "^2.20.0-alpha.3" + "@portkey/services" "^2.20.0-alpha.3" + "@reduxjs/toolkit" "^2.5.0" + +"@aelf-web-login/wallet-adapter-fairy-vault-discover@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/wallet-adapter-fairy-vault-discover/-/wallet-adapter-fairy-vault-discover-0.4.0-alpha.10.tgz#557f19adaf9d60a2c50b91a9cbbcfc8e3b272e91" + integrity sha512-ndx2k95q7nYq9SihR+/r2ngnmP//5O5H3bHbs/gwTdzIxNZdT/XQdhIlw4koi2tgluknWFQHDaWe/4V91c4bww== + dependencies: + "@aelf-web-login/wallet-adapter-base" "0.4.0-alpha.10" + "@portkey/contracts" "^2.20.0-alpha.3" + "@portkey/detect-provider" "2.4.0-alpha.12" + "@portkey/provider-types" "2.4.0-alpha.12" + "@portkey/utils" "^2.20.0-alpha.3" + bn.js "^5.2.1" + +"@aelf-web-login/wallet-adapter-night-elf@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/wallet-adapter-night-elf/-/wallet-adapter-night-elf-0.4.0-alpha.10.tgz#7c86b7315df4361e7551429dfbc92838a5b10c75" + integrity sha512-SQwfLRmTdMOR/vtwvFH+ONZgzIV6Ece6rEKAE0Mt5abB5w7ZjXvoBCPXOy4oTXKXuI5BzVNjoTiaRb3c/ILWtQ== + dependencies: + "@aelf-web-login/wallet-adapter-base" "0.4.0-alpha.10" + "@portkey/contracts" "^2.20.0-alpha.3" + aelf-bridge "0.0.11-alpha.10" + bn.js "^5.2.1" + +"@aelf-web-login/wallet-adapter-portkey-discover@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/wallet-adapter-portkey-discover/-/wallet-adapter-portkey-discover-0.4.0-alpha.10.tgz#d6a5b92fa4cfb98ff5c4e987c43911061207442f" + integrity sha512-FIY1/H+J1ZhKaY4NlRqGq3vI0ut24bBEA1oqLzeVTJa//j2+g0iw+0mBefObjVFhcBoFOMocF407OGz+vBsmJw== + dependencies: + "@aelf-web-login/wallet-adapter-base" "0.4.0-alpha.10" + "@portkey/contracts" "^2.20.0-alpha.3" + "@portkey/detect-provider" "^2.3.5" + "@portkey/provider-types" "^2.3.5" + "@portkey/utils" "^2.20.0-alpha.3" + bn.js "^5.2.1" + +"@aelf-web-login/wallet-adapter-portkey-web@0.4.0-alpha.10", "@aelf-web-login/wallet-adapter-portkey-web@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/wallet-adapter-portkey-web/-/wallet-adapter-portkey-web-0.4.0-alpha.10.tgz#56a655fa6e295d88a7a9b93da13cd622f9b61949" + integrity sha512-RW819inOLk2LV7BQRtzLw/wPu8xOwHyLR/x2PRAWw0AGmmpz2rhbUXsAYEgOCiIQD0FHyQYeAI27X6G7LgwXFw== + dependencies: + "@aelf-web-login/wallet-adapter-base" "0.4.0-alpha.10" + "@portkey/connect-web-wallet" "^2.20.0-alpha.3" + "@portkey/contracts" "^2.20.0-alpha.3" + "@portkey/detect-provider" "^2.4.0-alpha.6" + "@portkey/provider-types" "^2.4.0-alpha.6" + "@portkey/utils" "^2.20.0-alpha.3" + bn.js "^5.2.1" + +"@aelf-web-login/wallet-adapter-react@0.4.0-alpha.10", "@aelf-web-login/wallet-adapter-react@^0.4.0-alpha.10": + version "0.4.0-alpha.10" + resolved "https://registry.yarnpkg.com/@aelf-web-login/wallet-adapter-react/-/wallet-adapter-react-0.4.0-alpha.10.tgz#19613caddc729dbecd88134ee96508d2155f3167" + integrity sha512-pc9rB5d7jtzcjNjpsgOwH8s4sjtVveAAxr4MjRjh6WdgQBoW4WkqP017pshZoMizk+dyeoPTYOJMzpFs/JYSvg== + dependencies: + "@aelf-web-login/utils" "0.4.0-alpha.10" + "@aelf-web-login/wallet-adapter-base" "0.4.0-alpha.10" + "@aelf-web-login/wallet-adapter-bridge" "0.4.0-alpha.10" + "@portkey/connect-web-wallet" "^2.20.0-alpha.3" + "@portkey/utils" "^2.20.0-alpha.3" + antd "4.24.14" + react "^18.3.1" + react-dom "^18.3.1" + react-use "^17.6.0" + vconsole "^3.15.1" + +"@aelfqueen/fetch@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@aelfqueen/fetch/-/fetch-1.1.2.tgz#4012a24b724ab8f9cf680c345e0b01aa9719cda1" + integrity sha512-7ugCsVZB+wt2A5pc5VtuiDO1tZJJlKuphV+kNtSqQYY1NhZqS8RfxJDS4baa5QT8Wm8Gde4vttIY05SykizAtg== + dependencies: + "@aelfqueen/inquire" "^1.1.1" + "@protobufjs/aspromise" "^1.1.1" + +"@aelfqueen/inquire@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@aelfqueen/inquire/-/inquire-1.1.1.tgz#c25ecdd9ab35e054925e01937280235a6c2cb589" + integrity sha512-mGM34yOV5L7WE6yh0WyVGtPvzmvOmn7DKNbuFAcuSaBWoZq4pzSh1V5VoPLSLpbdXWHkrQ4q+XJ5hRqSbGSRxw== + +"@aelfqueen/protobufjs@^6.8.11": + version "6.8.11" + resolved "https://registry.yarnpkg.com/@aelfqueen/protobufjs/-/protobufjs-6.8.11.tgz#03d602cb0f2b94a0e79649b03729397c966dff96" + integrity sha512-vYwMWliBIcwB1P75LHmiXQAizun3iDPxylr1MszRE0wpNR0GuctSb8cZwDnV+JNm7OQX7Z+g7ZeEpJoz+beQHw== + dependencies: + "@aelfqueen/fetch" "^1.1.2" + "@aelfqueen/inquire" "^1.1.1" + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.0" + "@types/node" "^10.1.0" + long "^4.0.0" + +"@aelfqueen/protobufjs@^6.8.9": + version "6.8.9" + resolved "https://registry.npmjs.org/@aelfqueen/protobufjs/-/protobufjs-6.8.9.tgz#5eb0e6f2d649994501eb472d98052958e237fbfb" + integrity sha512-xa8i/rsC/Dj6Wh0DWpgr4eODVu8tVktyTENJU0QGjwmu317PCyp/8p7rEcvplljAteoQ31cFxUCrnzFQ0iLG1g== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.0" + "@types/node" "^10.1.0" + long "^4.0.0" + +"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@ant-design/colors@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298" + integrity sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ== + dependencies: + "@ctrl/tinycolor" "^3.4.0" + +"@ant-design/colors@^7.0.0": + version "7.1.0" + resolved "https://registry.npmjs.org/@ant-design/colors/-/colors-7.1.0.tgz#60eadfa2e21871d8948dac5d50b9f056062f8af3" + integrity sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg== + dependencies: + "@ctrl/tinycolor" "^3.6.1" + +"@ant-design/colors@^7.1.0": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.2.1.tgz#3bbc1c6c18550020d1622a0067ff03492318df98" + integrity sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ== + dependencies: + "@ant-design/fast-color" "^2.0.6" + +"@ant-design/cssinjs-utils@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz#5dd79126057920a6992d57b38dd84e2c0b707977" + integrity sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg== + dependencies: + "@ant-design/cssinjs" "^1.21.0" + "@babel/runtime" "^7.23.2" + rc-util "^5.38.0" + +"@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1": + version "1.24.0" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz#7db091f03f189abc77a13cbd27a2293802cd7285" + integrity sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg== + dependencies: + "@babel/runtime" "^7.11.1" + "@emotion/hash" "^0.8.0" + "@emotion/unitless" "^0.7.5" + classnames "^2.3.1" + csstype "^3.1.3" + rc-util "^5.35.0" + stylis "^4.3.4" + +"@ant-design/fast-color@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz#ab4d4455c1542c9017d367c2fa8ca3e4215d0ba2" + integrity sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA== + dependencies: + "@babel/runtime" "^7.24.7" + +"@ant-design/icons-svg@^4.3.0", "@ant-design/icons-svg@^4.4.0": + version "4.4.2" + resolved "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" + integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== + +"@ant-design/icons@^4.7.0", "@ant-design/icons@^4.8.2": + version "4.8.3" + resolved "https://registry.npmjs.org/@ant-design/icons/-/icons-4.8.3.tgz#41555408ed5e9b0c3d53f3f24fe6a73abfcf4000" + integrity sha512-HGlIQZzrEbAhpJR6+IGdzfbPym94Owr6JZkJ2QCCnOkPVIWMO2xgIVcOKnl8YcpijIo39V7l2qQL5fmtw56cMw== + dependencies: + "@ant-design/colors" "^6.0.0" + "@ant-design/icons-svg" "^4.3.0" + "@babel/runtime" "^7.11.2" + classnames "^2.2.6" + lodash "^4.17.15" + rc-util "^5.9.4" + +"@ant-design/icons@^5.3.0": + version "5.4.0" + resolved "https://registry.npmjs.org/@ant-design/icons/-/icons-5.4.0.tgz#4bd8f335c68207cc06fe9943d164a81cdfcfbeac" + integrity sha512-QZbWC5xQYexCI5q4/fehSEkchJr5UGtvAJweT743qKUQQGs9IH2DehNLP49DJ3Ii9m9CijD2HN6fNy3WKhIFdA== + dependencies: + "@ant-design/colors" "^7.0.0" + "@ant-design/icons-svg" "^4.4.0" + "@babel/runtime" "^7.24.8" + classnames "^2.2.6" + rc-util "^5.31.1" + +"@ant-design/icons@^5.5.2": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.6.1.tgz#7290fcdc3d96ff3fca793ed399053cd29ad5dbd3" + integrity sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg== + dependencies: + "@ant-design/colors" "^7.0.0" + "@ant-design/icons-svg" "^4.4.0" + "@babel/runtime" "^7.24.8" + classnames "^2.2.6" + rc-util "^5.31.1" + +"@ant-design/react-slick@~1.0.0", "@ant-design/react-slick@~1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.0.2.tgz#241bb412aeacf7ff5d50c61fa5db66773fde6b56" + integrity sha512-Wj8onxL/T8KQLFFiCA4t8eIRGpRR+UPgOdac2sYzonv+i0n3kXHmvHLLiOYL655DQx2Umii9Y9nNgL7ssu5haQ== + dependencies: + "@babel/runtime" "^7.10.4" + classnames "^2.2.5" + json2mq "^0.2.0" + resize-observer-polyfill "^1.5.1" + throttle-debounce "^5.0.0" + +"@ant-design/react-slick@~1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" + integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== + dependencies: + "@babel/runtime" "^7.10.4" + classnames "^2.2.5" + json2mq "^0.2.0" + resize-observer-polyfill "^1.5.1" + throttle-debounce "^5.0.0" + +"@apollo/client@^3.7.3": + version "3.11.8" + resolved "https://registry.npmjs.org/@apollo/client/-/client-3.11.8.tgz#f6bacdc7e1b243807c1387113e1d445a53471a9c" + integrity sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA== + dependencies: + "@graphql-typed-document-node/core" "^3.1.1" + "@wry/caches" "^1.0.0" + "@wry/equality" "^0.5.6" + "@wry/trie" "^0.5.0" + graphql-tag "^2.12.6" + hoist-non-react-statics "^3.3.2" + optimism "^0.18.0" + prop-types "^15.7.2" + rehackt "^0.1.0" + response-iterator "^0.2.6" + symbol-observable "^4.0.0" + ts-invariant "^0.10.3" + tslib "^2.3.0" + zen-observable-ts "^1.2.5" + +"@aws-crypto/sha256-js@^5.0.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/types@^3.222.0": + version "3.649.0" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.649.0.tgz#a6828e6338dc755e0c30b5f77321e63425a88aed" + integrity sha512-PuPw8RysbhJNlaD2d/PzOTf8sbf4Dsn2b7hwyGh7YVG3S75yTpxSAZxrnhKsz9fStgqFmnw/jUfV/G+uQAeTVw== + dependencies: + "@smithy/types" "^3.4.0" + tslib "^2.6.2" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.26.5": + version "7.26.8" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + +"@babel/core@^7.26.0": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" + integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.9" + "@babel/types" "^7.26.9" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.25.6": + version "7.25.6" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz#0df1ad8cb32fe4d2b01d8bf437f153d19342a87c" + integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw== + dependencies: + "@babel/types" "^7.25.6" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/generator@^7.26.9": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" + integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== + dependencies: + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.26.5": + version "7.26.5" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": + version "7.24.8" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz#94ee67e8ec0e5d44ea7baeb51e571bd26af07878" + integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg== + +"@babel/helper-plugin-utils@^7.25.9": + version "7.26.5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9" + integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-string-parser@^7.24.8": + version "7.24.8" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" + integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.9": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" + integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== + dependencies: + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.25.4", "@babel/parser@^7.26.9": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" + integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== + dependencies: + "@babel/types" "^7.26.9" + +"@babel/parser@^7.25.0", "@babel/parser@^7.25.6": + version "7.25.6" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f" + integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== + dependencies: + "@babel/types" "^7.25.6" + +"@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-transform-react-jsx-self@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz#c0b6cae9c1b73967f7f9eb2fca9536ba2fad2858" + integrity sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-react-jsx-source@^7.25.9": + version "7.25.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz#4c6b8daa520b5f155b5fb55547d7c9fa91417503" + integrity sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.4.5", "@babel/runtime@^7.6.3": + version "7.25.6" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz#9afc3289f7184d8d7f98b099884c26317b9264d2" + integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/runtime@^7.19.4", "@babel/runtime@^7.23.2": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz#aa4c6facc65b9cb3f87d75125ffd47781b475433" + integrity sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/runtime@^7.22.5", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.25.7": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.3.tgz#75c5034b55ba868121668be5d5bb31cc64e6e61a" + integrity sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA== + +"@babel/template@^7.25.0": + version "7.25.0" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" + integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.25.0" + "@babel/types" "^7.25.0" + +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/traverse@^7.24.7": + version "7.25.6" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" + integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.6" + "@babel/parser" "^7.25.6" + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.6" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.9": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" + integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.4", "@babel/types@^7.25.9", "@babel/types@^7.26.9": + version "7.26.9" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" + integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.6": + version "7.25.6" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" + integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== + dependencies: + "@babel/helper-string-parser" "^7.24.8" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa" + integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + +"@coinbase/wallet-sdk@4.0.4": + version "4.0.4" + resolved "https://registry.npmjs.org/@coinbase/wallet-sdk/-/wallet-sdk-4.0.4.tgz#634cd89bac93eeaf381a1f026476794e53431ed6" + integrity sha512-74c040CRnGhfRjr3ArnkAgud86erIqdkPHNt5HR1k9u97uTIZCJww9eGYT67Qf7gHPpGS/xW8Be1D4dvRm63FA== + dependencies: + buffer "^6.0.3" + clsx "^1.2.1" + eventemitter3 "^5.0.1" + keccak "^3.0.3" + preact "^10.16.0" + sha.js "^2.4.11" + +"@coinbase/wallet-sdk@^3.3.0": + version "3.9.3" + resolved "https://registry.npmjs.org/@coinbase/wallet-sdk/-/wallet-sdk-3.9.3.tgz#daf10cb0c85d0363315b7270cb3f02bedc408aab" + integrity sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw== + dependencies: + bn.js "^5.2.1" + buffer "^6.0.3" + clsx "^1.2.1" + eth-block-tracker "^7.1.0" + eth-json-rpc-filters "^6.0.0" + eventemitter3 "^5.0.1" + keccak "^3.0.3" + preact "^10.16.0" + sha.js "^2.4.11" + +"@commitlint/cli@^17.6.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz#10492114a022c91dcfb1d84dac773abb3db76d33" + integrity sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg== + dependencies: + "@commitlint/format" "^17.8.1" + "@commitlint/lint" "^17.8.1" + "@commitlint/load" "^17.8.1" + "@commitlint/read" "^17.8.1" + "@commitlint/types" "^17.8.1" + execa "^5.0.0" + lodash.isfunction "^3.0.9" + resolve-from "5.0.0" + resolve-global "1.0.0" + yargs "^17.0.0" + +"@commitlint/config-conventional@^17.6.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.8.1.tgz#e5bcf0cfec8da7ac50bc04dc92e0a4ea74964ce0" + integrity sha512-NxCOHx1kgneig3VLauWJcDWS40DVjg7nKOpBEEK9E5fjJpQqLCilcnKkIIjdBH98kEO1q3NpE5NSrZ2kl/QGJg== + dependencies: + conventional-changelog-conventionalcommits "^6.1.0" + +"@commitlint/config-validator@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.8.1.tgz#5cc93b6b49d5524c9cc345a60e5bf74bcca2b7f9" + integrity sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA== + dependencies: + "@commitlint/types" "^17.8.1" + ajv "^8.11.0" + +"@commitlint/ensure@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.8.1.tgz#59183557844999dbb6aab6d03629a3d104d01a8d" + integrity sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow== + dependencies: + "@commitlint/types" "^17.8.1" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.upperfirst "^4.3.1" + +"@commitlint/execute-rule@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz#504ed69eb61044eeb84fdfd10cc18f0dab14f34c" + integrity sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ== + +"@commitlint/format@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/format/-/format-17.8.1.tgz#6108bb6b4408e711006680649927e1b559bdc5f8" + integrity sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg== + dependencies: + "@commitlint/types" "^17.8.1" + chalk "^4.1.0" + +"@commitlint/is-ignored@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.8.1.tgz#cf25bcd8409c79684b63f8bdeb35df48edda244e" + integrity sha512-UshMi4Ltb4ZlNn4F7WtSEugFDZmctzFpmbqvpyxD3la510J+PLcnyhf9chs7EryaRFJMdAKwsEKfNK0jL/QM4g== + dependencies: + "@commitlint/types" "^17.8.1" + semver "7.5.4" + +"@commitlint/lint@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.1.tgz#bfc21215f6b18d41d4d43e2aa3cb79a5d7726cd8" + integrity sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA== + dependencies: + "@commitlint/is-ignored" "^17.8.1" + "@commitlint/parse" "^17.8.1" + "@commitlint/rules" "^17.8.1" + "@commitlint/types" "^17.8.1" + +"@commitlint/load@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/load/-/load-17.8.1.tgz#fa061e7bfa53281eb03ca8517ca26d66a189030c" + integrity sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA== + dependencies: + "@commitlint/config-validator" "^17.8.1" + "@commitlint/execute-rule" "^17.8.1" + "@commitlint/resolve-extends" "^17.8.1" + "@commitlint/types" "^17.8.1" + "@types/node" "20.5.1" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + cosmiconfig-typescript-loader "^4.0.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" + resolve-from "^5.0.0" + ts-node "^10.8.1" + typescript "^4.6.4 || ^5.2.2" + +"@commitlint/message@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz#a5cd226c419be20ee03c3d237db6ac37b95958b3" + integrity sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA== + +"@commitlint/parse@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/parse/-/parse-17.8.1.tgz#6e00b8f50ebd63562d25dcf4230da2c9f984e626" + integrity sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw== + dependencies: + "@commitlint/types" "^17.8.1" + conventional-changelog-angular "^6.0.0" + conventional-commits-parser "^4.0.0" + +"@commitlint/read@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/read/-/read-17.8.1.tgz#b3f28777607c756078356cc133368b0e8c08092f" + integrity sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w== + dependencies: + "@commitlint/top-level" "^17.8.1" + "@commitlint/types" "^17.8.1" + fs-extra "^11.0.0" + git-raw-commits "^2.0.11" + minimist "^1.2.6" + +"@commitlint/resolve-extends@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz#9af01432bf2fd9ce3dd5a00d266cce14e4c977e7" + integrity sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q== + dependencies: + "@commitlint/config-validator" "^17.8.1" + "@commitlint/types" "^17.8.1" + import-fresh "^3.0.0" + lodash.mergewith "^4.6.2" + resolve-from "^5.0.0" + resolve-global "^1.0.0" + +"@commitlint/rules@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/rules/-/rules-17.8.1.tgz#da49cab1b7ebaf90d108de9f58f684dc4ccb65a0" + integrity sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA== + dependencies: + "@commitlint/ensure" "^17.8.1" + "@commitlint/message" "^17.8.1" + "@commitlint/to-lines" "^17.8.1" + "@commitlint/types" "^17.8.1" + execa "^5.0.0" + +"@commitlint/to-lines@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.8.1.tgz#a5c4a7cf7dff3dbdd69289fc0eb19b66f3cfe017" + integrity sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA== + +"@commitlint/top-level@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.8.1.tgz#206d37d6782f33c9572e44fbe3758392fdeea7bc" + integrity sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA== + dependencies: + find-up "^5.0.0" + +"@commitlint/types@^17.8.1": + version "17.8.1" + resolved "https://registry.npmjs.org/@commitlint/types/-/types-17.8.1.tgz#883a0ad35c5206d5fef7bc6ce1bbe648118af44e" + integrity sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ== + dependencies: + chalk "^4.1.0" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@csstools/selector-specificity@^2.0.2": + version "2.2.0" + resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" + integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== + +"@ctrl/tinycolor@^3.4.0", "@ctrl/tinycolor@^3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" + integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== + +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + +"@emotion/unitless@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + +"@esbuild/aix-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" + integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== + +"@esbuild/android-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" + integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== + +"@esbuild/android-arm@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" + integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== + +"@esbuild/android-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" + integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== + +"@esbuild/darwin-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" + integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== + +"@esbuild/darwin-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" + integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== + +"@esbuild/freebsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" + integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== + +"@esbuild/freebsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" + integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== + +"@esbuild/linux-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" + integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== + +"@esbuild/linux-arm@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" + integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== + +"@esbuild/linux-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" + integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== + +"@esbuild/linux-loong64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" + integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== + +"@esbuild/linux-mips64el@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" + integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== + +"@esbuild/linux-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" + integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== + +"@esbuild/linux-riscv64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" + integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== + +"@esbuild/linux-s390x@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" + integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== + +"@esbuild/linux-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" + integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== + +"@esbuild/netbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" + integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== + +"@esbuild/netbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" + integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== + +"@esbuild/openbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" + integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== + +"@esbuild/openbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" + integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== + +"@esbuild/sunos-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" + integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== + +"@esbuild/win32-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" + integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== + +"@esbuild/win32-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" + integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== + +"@esbuild/win32-x64@0.24.2": + version "0.24.2" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" + integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.11.0" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/eslintrc@^1.3.0": + version "1.4.1" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" + integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@ethereumjs/common@2.6.5", "@ethereumjs/common@^2.6.4": + version "2.6.5" + resolved "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" + integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.5" + +"@ethereumjs/common@^3.2.0": + version "3.2.0" + resolved "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz#b71df25845caf5456449163012074a55f048e0a0" + integrity sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA== + dependencies: + "@ethereumjs/util" "^8.1.0" + crc-32 "^1.2.0" + +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + +"@ethereumjs/tx@3.5.2": + version "3.5.2" + resolved "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c" + integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw== + dependencies: + "@ethereumjs/common" "^2.6.4" + ethereumjs-util "^7.1.5" + +"@ethereumjs/tx@^4.1.2", "@ethereumjs/tx@^4.2.0": + version "4.2.0" + resolved "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz#5988ae15daf5a3b3c815493bc6b495e76009e853" + integrity sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw== + dependencies: + "@ethereumjs/common" "^3.2.0" + "@ethereumjs/rlp" "^4.0.1" + "@ethereumjs/util" "^8.1.0" + ethereum-cryptography "^2.0.0" + +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" + +"@ethersproject/abi@^5.6.3": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz#e79bb51940ac35fe6f3262d7fe2cdb25ad5f07d9" + integrity sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/hash" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + +"@ethersproject/abstract-provider@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz#7581f9be601afa1d02b95d26b9d9840926a35b0c" + integrity sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/networks" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/web" "^5.8.0" + +"@ethersproject/abstract-signer@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz#8d7417e95e4094c1797a9762e6789c7356db0754" + integrity sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA== + dependencies: + "@ethersproject/abstract-provider" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + +"@ethersproject/address@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz#3007a2c352eee566ad745dca1dbbebdb50a6a983" + integrity sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + +"@ethersproject/base64@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz#61c669c648f6e6aad002c228465d52ac93ee83eb" + integrity sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ== + dependencies: + "@ethersproject/bytes" "^5.8.0" + +"@ethersproject/bignumber@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz#c381d178f9eeb370923d389284efa19f69efa5d7" + integrity sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz#9074820e1cac7507a34372cadeb035461463be34" + integrity sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A== + dependencies: + "@ethersproject/logger" "^5.8.0" + +"@ethersproject/constants@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz#12f31c2f4317b113a4c19de94e50933648c90704" + integrity sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + +"@ethersproject/hash@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz#b8893d4629b7f8462a90102572f8cd65a0192b4c" + integrity sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA== + dependencies: + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + +"@ethersproject/keccak256@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz#d2123a379567faf2d75d2aaea074ffd4df349e6a" + integrity sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng== + dependencies: + "@ethersproject/bytes" "^5.8.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz#f0232968a4f87d29623a0481690a2732662713d6" + integrity sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA== + +"@ethersproject/networks@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz#8b4517a3139380cba9fb00b63ffad0a979671fde" + integrity sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg== + dependencies: + "@ethersproject/logger" "^5.8.0" + +"@ethersproject/properties@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz#405a8affb6311a49a91dabd96aeeae24f477020e" + integrity sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw== + dependencies: + "@ethersproject/logger" "^5.8.0" + +"@ethersproject/rlp@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz#5a0d49f61bc53e051532a5179472779141451de5" + integrity sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + +"@ethersproject/signing-key@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz#9797e02c717b68239c6349394ea85febf8893119" + integrity sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + bn.js "^5.2.1" + elliptic "6.6.1" + hash.js "1.1.7" + +"@ethersproject/strings@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz#ad79fafbf0bd272d9765603215ac74fd7953908f" + integrity sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + +"@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz#1e518822403abc99def5a043d1c6f6fe0007e46b" + integrity sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + "@ethersproject/signing-key" "^5.8.0" + +"@ethersproject/web@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz#3e54badc0013b7a801463a7008a87988efce8a37" + integrity sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw== + dependencies: + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + +"@etransfer/core@^1.0.0-alpha.9": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@etransfer/core/-/core-1.14.0.tgz#98917dcf71e74e66000d19256a1e86c6b6a0f043" + integrity sha512-iRnt8fYCjbmmWKvqNRH+Iifiya4CzkXx/O9H4e+4T7VU/dPN/8ZjlFtd/pTMSzzG/2BFn/AGJgVck/Nyg4WdLw== + dependencies: + "@etransfer/services" "^1.14.0" + "@etransfer/socket" "^1.14.0" + "@etransfer/types" "^1.14.0" + "@etransfer/utils" "^1.14.0" + +"@etransfer/request@^1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@etransfer/request/-/request-1.14.0.tgz#b6f3a9dc8926885cf3cd70e26afb5eef128ef53b" + integrity sha512-c3WYFww4n6eprDEFNtqQKZw3iS22ZijdO1Dlyj5l2/WXoJhJT9NqrNt9/nWiaPlWF5zsArhFyJQjVbr4GtKDIA== + dependencies: + "@etransfer/types" "^1.14.0" + "@etransfer/utils" "^1.14.0" + axios "^0.24.0" + +"@etransfer/services@^1.0.0-alpha.9", "@etransfer/services@^1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@etransfer/services/-/services-1.14.0.tgz#1db758fe6ca9aaa9b4109799fbbdcb5a2b6fa5b1" + integrity sha512-CwmVYTZ3b1Ht3wG4pbBHbRSdHwwia1/zGLPdcF7b8jaMn2shXZaVdJ0WNjH1Q/OeJgVzd4sx5/OVbIUxMvNvWQ== + dependencies: + "@etransfer/request" "^1.14.0" + "@etransfer/types" "^1.14.0" + "@etransfer/utils" "^1.14.0" + "@portkey/types" "2.15.10" + +"@etransfer/socket@^1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@etransfer/socket/-/socket-1.14.0.tgz#f8c0c8b107b16cd93e2926a4fc4e7af8a002e9b7" + integrity sha512-nSbscHq2nTaEFuQ26UwQDBUBQaRKGKnR07esJ3/sqIIUmUgEHtoVa/i/JYhYpSXLaizwVtraz9+9RBOzxOEocw== + dependencies: + "@abp/signalr" "^7.0.0" + "@etransfer/utils" "^1.14.0" + +"@etransfer/types@^1.0.0-alpha.9", "@etransfer/types@^1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@etransfer/types/-/types-1.14.0.tgz#a160926487403e2260c34836a7c2af4212e18aab" + integrity sha512-Cu+hhnsWDM6MNFfYUpb6B27djKfBQNq2uLAR2TKYrKAPoH4G0+W3bVeNfWmJ0jda2vHEYPSlPLZqeLAe+tQP2g== + +"@etransfer/utils@^1.0.0-alpha.9", "@etransfer/utils@^1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@etransfer/utils/-/utils-1.14.0.tgz#6c5be459c9f66a12ef9fc335aea03bc23194c053" + integrity sha512-tqOdoIl9XCFGMmLRLvqDjUO9uwR4suw4Y1UpOsQ/fpnu6ysLZ74ie5Tc/AI12bX0BEEIHWWp5bsQ39LKVKqTeg== + dependencies: + "@etransfer/services" "^1.14.0" + "@etransfer/types" "^1.14.0" + bn.js "^5.2.1" + +"@floating-ui/core@^1.6.0": + version "1.6.7" + resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz#7602367795a390ff0662efd1c7ae8ca74e75fb12" + integrity sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g== + dependencies: + "@floating-ui/utils" "^0.2.7" + +"@floating-ui/dom@^1.4.2": + version "1.6.10" + resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz#b74c32f34a50336c86dcf1f1c845cf3a39e26d6f" + integrity sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A== + dependencies: + "@floating-ui/core" "^1.6.0" + "@floating-ui/utils" "^0.2.7" + +"@floating-ui/utils@^0.2.7": + version "0.2.7" + resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz#d0ece53ce99ab5a8e37ebdfe5e32452a2bfc073e" + integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA== + +"@graphql-typed-document-node/core@^3.1.1": + version "3.2.0" + resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== + +"@humanwhocodes/config-array@^0.9.2": + version "0.9.5" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" + integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + 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.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + 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#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.23", "@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#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@ledgerhq/devices@^5.48.0", "@ledgerhq/devices@^5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.51.1.tgz#d741a4a5d8f17c2f9d282fd27147e6fe1999edb7" + integrity sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA== + dependencies: + "@ledgerhq/errors" "^5.50.0" + "@ledgerhq/logs" "^5.50.0" + rxjs "6" + semver "^7.3.5" + +"@ledgerhq/errors@^5.48.0", "@ledgerhq/errors@^5.50.0": + version "5.50.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" + integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== + +"@ledgerhq/hw-transport-web-ble@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-web-ble/-/hw-transport-web-ble-5.48.0.tgz#41b29293c263d8ed4200037eb6da4db3bd4268a6" + integrity sha512-ezgssFvxc4/UbfwIxwg9jEn0/yiND8TW6bDkaE3kAqKCa8ZYYgFtms8iRqjTOJlqcbSYkudbNhP74jtY0NxfdA== + dependencies: + "@ledgerhq/devices" "^5.48.0" + "@ledgerhq/errors" "^5.48.0" + "@ledgerhq/hw-transport" "^5.48.0" + "@ledgerhq/logs" "^5.48.0" + rxjs "^6.6.7" + +"@ledgerhq/hw-transport-webhid@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-5.48.0.tgz#aab2cffac3d5c97b04f093e2a0cbfe311dfdeab4" + integrity sha512-g6TYxgPX3MqP3jQ4SJaJjlM+2SwUSk4Si/9MeKLwz5ySbiD3bSTh/Gbzv8VBCaHPO4fILujc5vW/xejJuMzR8w== + dependencies: + "@ledgerhq/devices" "^5.48.0" + "@ledgerhq/errors" "^5.48.0" + "@ledgerhq/hw-transport" "^5.48.0" + "@ledgerhq/logs" "^5.48.0" + +"@ledgerhq/hw-transport-webusb@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-5.48.0.tgz#62e5bffffba853d4e06103f7c8e9b6c87ca0f994" + integrity sha512-tO+p11aRQx9q9ifmi/NCbCBKQ738lp+PROy1BWSzjCJcUEz1sKTLeRTLE2Xze25KebhuM2YR1NgkX5LN1z4upA== + dependencies: + "@ledgerhq/devices" "^5.48.0" + "@ledgerhq/errors" "^5.48.0" + "@ledgerhq/hw-transport" "^5.48.0" + "@ledgerhq/logs" "^5.48.0" + +"@ledgerhq/hw-transport@^5.48.0": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz#8dd14a8e58cbee4df0c29eaeef983a79f5f22578" + integrity sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw== + dependencies: + "@ledgerhq/devices" "^5.51.1" + "@ledgerhq/errors" "^5.50.0" + events "^3.3.0" + +"@ledgerhq/logs@^5.48.0", "@ledgerhq/logs@^5.50.0": + version "5.50.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" + integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== + +"@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0": + version "1.2.1" + resolved "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz#2f3a8f1d688935c704dbc89132394a41029acbb8" + integrity sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ== + +"@lit/reactive-element@^1.3.0", "@lit/reactive-element@^1.6.0": + version "1.6.3" + resolved "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz#25b4eece2592132845d303e091bad9b04cdcfe03" + integrity sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.0.0" + +"@matt-block/react-recaptcha-v2@^2.0.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@matt-block/react-recaptcha-v2/-/react-recaptcha-v2-2.1.0.tgz#da76be314154f678741fe1e008936082e9c1a3c1" + integrity sha512-smsI17u66UtmQYoVdAYBPclY+04qvlWAixx9FffSPqEbyDtMOSGeeBWSuJ6jHJT5aqmqI+XnkslJDCt/HvPLYg== + dependencies: + nanoid "^5.0.7" + +"@metamask/eth-json-rpc-provider@^1.0.0": + version "1.0.1" + resolved "https://registry.npmjs.org/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-1.0.1.tgz#3fd5316c767847f4ca107518b611b15396a5a32c" + integrity sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA== + dependencies: + "@metamask/json-rpc-engine" "^7.0.0" + "@metamask/safe-event-emitter" "^3.0.0" + "@metamask/utils" "^5.0.1" + +"@metamask/json-rpc-engine@^7.0.0": + version "7.3.3" + resolved "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.3.tgz#f2b30a2164558014bfcca45db10f5af291d989af" + integrity sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg== + dependencies: + "@metamask/rpc-errors" "^6.2.1" + "@metamask/safe-event-emitter" "^3.0.0" + "@metamask/utils" "^8.3.0" + +"@metamask/json-rpc-engine@^8.0.1", "@metamask/json-rpc-engine@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-8.0.2.tgz#29510a871a8edef892f838ee854db18de0bf0d14" + integrity sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA== + dependencies: + "@metamask/rpc-errors" "^6.2.1" + "@metamask/safe-event-emitter" "^3.0.0" + "@metamask/utils" "^8.3.0" + +"@metamask/json-rpc-middleware-stream@^7.0.1": + version "7.0.2" + resolved "https://registry.npmjs.org/@metamask/json-rpc-middleware-stream/-/json-rpc-middleware-stream-7.0.2.tgz#2e8b2cbc38968e3c6239a9144c35bbb08a8fb57d" + integrity sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg== + dependencies: + "@metamask/json-rpc-engine" "^8.0.2" + "@metamask/safe-event-emitter" "^3.0.0" + "@metamask/utils" "^8.3.0" + readable-stream "^3.6.2" + +"@metamask/object-multiplex@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@metamask/object-multiplex/-/object-multiplex-2.1.0.tgz#5e2e908fc46aee581cbba809870eeee0e571cbb6" + integrity sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA== + dependencies: + once "^1.4.0" + readable-stream "^3.6.2" + +"@metamask/onboarding@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@metamask/onboarding/-/onboarding-1.0.1.tgz#14a36e1e175e2f69f09598e2008ab6dc1b3297e6" + integrity sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ== + dependencies: + bowser "^2.9.0" + +"@metamask/providers@16.1.0": + version "16.1.0" + resolved "https://registry.npmjs.org/@metamask/providers/-/providers-16.1.0.tgz#7da593d17c541580fa3beab8d9d8a9b9ce19ea07" + integrity sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g== + dependencies: + "@metamask/json-rpc-engine" "^8.0.1" + "@metamask/json-rpc-middleware-stream" "^7.0.1" + "@metamask/object-multiplex" "^2.0.0" + "@metamask/rpc-errors" "^6.2.1" + "@metamask/safe-event-emitter" "^3.1.1" + "@metamask/utils" "^8.3.0" + detect-browser "^5.2.0" + extension-port-stream "^3.0.0" + fast-deep-equal "^3.1.3" + is-stream "^2.0.0" + readable-stream "^3.6.2" + webextension-polyfill "^0.10.0" + +"@metamask/rpc-errors@^6.2.1": + version "6.3.1" + resolved "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.3.1.tgz#d5bb4740e070c3d87e91717ff4c3c6061a081cab" + integrity sha512-ugDY7cKjF4/yH5LtBaOIKHw/AiGGSAmzptAUEiAEGr/78LwuzcXAxmzEQfSfMIfI+f9Djr8cttq1pRJJKfTuCg== + dependencies: + "@metamask/utils" "^9.0.0" + fast-safe-stringify "^2.0.6" + +"@metamask/safe-event-emitter@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" + integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== + +"@metamask/safe-event-emitter@^3.0.0": + version "3.1.1" + resolved "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-3.1.1.tgz#e89b840a7af8097a8ed4953d8dc8470d1302d3ef" + integrity sha512-ihb3B0T/wJm1eUuArYP4lCTSEoZsClHhuWyfo/kMX3m/odpqNcPfsz5O2A3NT7dXCAgWPGDQGPqygCpgeniKMw== + +"@metamask/safe-event-emitter@^3.1.1": + version "3.1.2" + resolved "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-3.1.2.tgz#bfac8c7a1a149b5bbfe98f59fbfea512dfa3bad4" + integrity sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA== + +"@metamask/sdk-communication-layer@0.26.4": + version "0.26.4" + resolved "https://registry.npmjs.org/@metamask/sdk-communication-layer/-/sdk-communication-layer-0.26.4.tgz#dda8e33a327f29962095b82c598799b852e40d81" + integrity sha512-+X4GEc5mV1gWK4moSswVlKsUh+RsA48qPlkxBLTUxQODSnyBe0TRMxE6mH+bSrfponnTzvBkGUXyEjvDwDjDHw== + dependencies: + bufferutil "^4.0.8" + date-fns "^2.29.3" + debug "^4.3.4" + utf-8-validate "^5.0.2" + uuid "^8.3.2" + +"@metamask/sdk-install-modal-web@0.26.5": + version "0.26.5" + resolved "https://registry.npmjs.org/@metamask/sdk-install-modal-web/-/sdk-install-modal-web-0.26.5.tgz#b696c78818adaff85d01a4f41fecc8fd2c80bc59" + integrity sha512-qVA9Nk+NorGx5hXyODy5wskptE8R7RNYTYt49VbQpJogqbbVe1dnJ98+KaA43PBN4XYMCXmcIhULNiEHGsLynA== + dependencies: + qr-code-styling "^1.6.0-rc.1" + +"@metamask/sdk@0.26.5": + version "0.26.5" + resolved "https://registry.npmjs.org/@metamask/sdk/-/sdk-0.26.5.tgz#8adf2957918d0ec06be499d995da15d2171c058e" + integrity sha512-HS/MPQCCYRS+m3dDdGLcAagwYHiPv9iUshDMBjINUywCtfUN4P2BH8xdvPOgtnzRIuRSMXqMWBbZnTvEvBeQvA== + dependencies: + "@metamask/onboarding" "^1.0.1" + "@metamask/providers" "16.1.0" + "@metamask/sdk-communication-layer" "0.26.4" + "@metamask/sdk-install-modal-web" "0.26.5" + "@types/dom-screen-wake-lock" "^1.0.0" + bowser "^2.9.0" + cross-fetch "^4.0.0" + debug "^4.3.4" + eciesjs "^0.3.15" + eth-rpc-errors "^4.0.3" + eventemitter2 "^6.4.7" + i18next "23.11.5" + i18next-browser-languagedetector "7.1.0" + obj-multiplex "^1.0.0" + pump "^3.0.0" + qrcode-terminal-nooctal "^0.12.1" + react-native-webview "^11.26.0" + readable-stream "^3.6.2" + rollup-plugin-visualizer "^5.9.2" + socket.io-client "^4.5.1" + util "^0.12.4" + uuid "^8.3.2" + +"@metamask/superstruct@^3.0.0", "@metamask/superstruct@^3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@metamask/superstruct/-/superstruct-3.1.0.tgz#148f786a674fba3ac885c1093ab718515bf7f648" + integrity sha512-N08M56HdOgBfRKkrgCMZvQppkZGcArEop3kixNEtVbJKm6P9Cfg0YkI6X0s1g78sNrj2fWUwvJADdZuzJgFttA== + +"@metamask/utils@^5.0.1": + version "5.0.2" + resolved "https://registry.npmjs.org/@metamask/utils/-/utils-5.0.2.tgz#140ba5061d90d9dac0280c19cab101bc18c8857c" + integrity sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g== + dependencies: + "@ethereumjs/tx" "^4.1.2" + "@types/debug" "^4.1.7" + debug "^4.3.4" + semver "^7.3.8" + superstruct "^1.0.3" + +"@metamask/utils@^8.3.0": + version "8.5.0" + resolved "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz#ddd0d4012d5191809404c97648a837ea9962cceb" + integrity sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ== + dependencies: + "@ethereumjs/tx" "^4.2.0" + "@metamask/superstruct" "^3.0.0" + "@noble/hashes" "^1.3.1" + "@scure/base" "^1.1.3" + "@types/debug" "^4.1.7" + debug "^4.3.4" + pony-cause "^2.1.10" + semver "^7.5.4" + uuid "^9.0.1" + +"@metamask/utils@^9.0.0": + version "9.2.1" + resolved "https://registry.npmjs.org/@metamask/utils/-/utils-9.2.1.tgz#d9f84706ff97e0c8d1bde5778549365b14269e81" + integrity sha512-/u663aUaB6+Xe75i3Mt/1cCljm41HDYIsna5oBrwGvgkY2zH7/9k9Zjd706cxoAbxN7QgLSVAReUiGnuxCuXrQ== + dependencies: + "@ethereumjs/tx" "^4.2.0" + "@metamask/superstruct" "^3.1.0" + "@noble/hashes" "^1.3.1" + "@scure/base" "^1.1.3" + "@types/debug" "^4.1.7" + debug "^4.3.4" + pony-cause "^2.1.10" + semver "^7.5.4" + uuid "^9.0.1" + +"@microsoft/signalr@~6.0.1": + version "6.0.25" + resolved "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.25.tgz#009f043066d383e2de41a483bd7e02bfd74d3cf8" + integrity sha512-8AzrpxS+E0yn1tXSlv7+UlURLmSxTQDgbvOT0pGKXjZT7MkhnDP+/GLuk7veRtUjczou/x32d9PHhYlr2NBy6Q== + dependencies: + abort-controller "^3.0.0" + eventsource "^1.0.7" + fetch-cookie "^0.11.0" + node-fetch "^2.6.7" + ws "^7.4.5" + +"@motionone/animation@^10.15.1", "@motionone/animation@^10.18.0": + version "10.18.0" + resolved "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz#868d00b447191816d5d5cf24b1cafa144017922b" + integrity sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw== + dependencies: + "@motionone/easing" "^10.18.0" + "@motionone/types" "^10.17.1" + "@motionone/utils" "^10.18.0" + tslib "^2.3.1" + +"@motionone/dom@^10.16.2", "@motionone/dom@^10.16.4": + version "10.18.0" + resolved "https://registry.npmjs.org/@motionone/dom/-/dom-10.18.0.tgz#7fd25dac04cab72def6d2b92b8e0cdc091576527" + integrity sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A== + dependencies: + "@motionone/animation" "^10.18.0" + "@motionone/generators" "^10.18.0" + "@motionone/types" "^10.17.1" + "@motionone/utils" "^10.18.0" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/easing@^10.18.0": + version "10.18.0" + resolved "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz#7b82f6010dfee3a1bb0ee83abfbaff6edae0c708" + integrity sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg== + dependencies: + "@motionone/utils" "^10.18.0" + tslib "^2.3.1" + +"@motionone/generators@^10.18.0": + version "10.18.0" + resolved "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz#fe09ab5cfa0fb9a8884097feb7eb60abeb600762" + integrity sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg== + dependencies: + "@motionone/types" "^10.17.1" + "@motionone/utils" "^10.18.0" + tslib "^2.3.1" + +"@motionone/svelte@^10.16.2": + version "10.16.4" + resolved "https://registry.npmjs.org/@motionone/svelte/-/svelte-10.16.4.tgz#5daf117cf5b2576fc6dd487c5e0500938a742470" + integrity sha512-zRVqk20lD1xqe+yEDZhMYgftsuHc25+9JSo+r0a0OWUJFocjSV9D/+UGhX4xgJsuwB9acPzXLr20w40VnY2PQA== + dependencies: + "@motionone/dom" "^10.16.4" + tslib "^2.3.1" + +"@motionone/types@^10.15.1", "@motionone/types@^10.17.1": + version "10.17.1" + resolved "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz#cf487badbbdc9da0c2cb86ffc1e5d11147c6e6fb" + integrity sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A== + +"@motionone/utils@^10.15.1", "@motionone/utils@^10.18.0": + version "10.18.0" + resolved "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz#a59ff8932ed9009624bca07c56b28ef2bb2f885e" + integrity sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw== + dependencies: + "@motionone/types" "^10.17.1" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/vue@^10.16.2": + version "10.16.4" + resolved "https://registry.npmjs.org/@motionone/vue/-/vue-10.16.4.tgz#07d09e3aa5115ca0bcc0076cb9e5322775277c09" + integrity sha512-z10PF9JV6SbjFq+/rYabM+8CVlMokgl8RFGvieSGNTmrkQanfHn+15XBrhG3BgUfvmTeSeyShfOHpG0i9zEdcg== + dependencies: + "@motionone/dom" "^10.16.4" + tslib "^2.3.1" + +"@next/bundle-analyzer@^12.2.4": + version "12.3.4" + resolved "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-12.3.4.tgz#37c587525288a3dea64213c991590532246e8bb8" + integrity sha512-eKjgRICzbLTmod0UnJcArFVs5uEAiuZwB6NCf84m+btW7jdylUVoOYf1wi5tA14xk5L9Lho7Prm6/XJ8gxYzfQ== + dependencies: + webpack-bundle-analyzer "4.3.0" + +"@next/env@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/env/-/env-12.2.0.tgz#17ce2d9f5532b677829840037e06f208b7eed66b" + integrity sha512-/FCkDpL/8SodJEXvx/DYNlOD5ijTtkozf4PPulYPtkPOJaMPpBSOkzmsta4fnrnbdH6eZjbwbiXFdr6gSQCV4w== + +"@next/eslint-plugin-next@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.2.0.tgz#38b36d3be244cc9a98c0e7d203bdb062f87df4ac" + integrity sha512-nIj5xV/z3dOfeBnE7qFAjUQZAi4pTlIMuusRM6s/T6lOz8x7mjY5s1ZkTUBmcjPVCb2VIv3CrMH0WZL6xfjZZg== + dependencies: + glob "7.1.7" + +"@next/eslint-plugin-next@^13.4.1": + version "13.5.6" + resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.5.6.tgz#cf279b94ddc7de49af8e8957f0c3b7349bc489bf" + integrity sha512-ng7pU/DDsxPgT6ZPvuprxrkeew3XaRf4LAT4FabaEO/hAbvVx4P7wqnqdbTdDn1kgTvsI4tpIgT4Awn/m0bGbg== + dependencies: + glob "7.1.7" + +"@next/swc-android-arm-eabi@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.0.tgz#f116756e668b267de84b76f068d267a12f18eb22" + integrity sha512-hbneH8DNRB2x0Nf5fPCYoL8a0osvdTCe4pvOc9Rv5CpDsoOlf8BWBs2OWpeP0U2BktGvIsuUhmISmdYYGyrvTw== + +"@next/swc-android-arm64@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.0.tgz#cbd9e329cef386271d4e746c08416b5d69342c24" + integrity sha512-1eEk91JHjczcJomxJ8X0XaUeNcp5Lx1U2Ic7j15ouJ83oRX+3GIslOuabW2oPkSgXbHkThMClhirKpvG98kwZg== + +"@next/swc-darwin-arm64@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.0.tgz#3473889157ba70b30ccdd4f59c46232d841744e2" + integrity sha512-x5U5gJd7ZvrEtTFnBld9O2bUlX8opu7mIQUqRzj7KeWzBwPhrIzTTsQXAiNqsaMuaRPvyHBVW/5d/6g6+89Y8g== + +"@next/swc-darwin-x64@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.0.tgz#b25198c3ef4c906000af49e4787a757965f760bb" + integrity sha512-iwMNFsrAPjfedjKDv9AXPAV16PWIomP3qw/FfPaxkDVRbUls7BNdofBLzkQmqxqWh93WrawLwaqyXpJuAaiwJA== + +"@next/swc-freebsd-x64@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.0.tgz#78e2213f8b703be0fef23a49507779b4a9842929" + integrity sha512-gRiAw8g3Akf6niTDLEm1Emfa7jXDjvaAj/crDO8hKASKA4Y1fS4kbi/tyWw5VtoFI4mUzRmCPmZ8eL0tBSG58A== + +"@next/swc-linux-arm-gnueabihf@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.0.tgz#80a4baf0ba699357e7420e2dea998908dcef5055" + integrity sha512-/TJZkxaIpeEwnXh6A40trgwd40C5+LJroLUOEQwMOJdavLl62PjCA6dGl1pgooWLCIb5YdBQ0EG4ylzvLwS2+Q== + +"@next/swc-linux-arm64-gnu@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.0.tgz#134a42ddea804d6bf04761607f774432c3126de6" + integrity sha512-++WAB4ElXCSOKG9H8r4ENF8EaV+w0QkrpjehmryFkQXmt5juVXz+nKDVlCRMwJU7A1O0Mie82XyEoOrf6Np1pA== + +"@next/swc-linux-arm64-musl@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.0.tgz#c781ac642ad35e0578d8a8d19c638b0f31c1a334" + integrity sha512-XrqkHi/VglEn5zs2CYK6ofJGQySrd+Lr4YdmfJ7IhsCnMKkQY1ma9Hv5THwhZVof3e+6oFHrQ9bWrw9K4WTjFA== + +"@next/swc-linux-x64-gnu@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.0.tgz#0e2235a59429eadd40ac8880aec18acdbc172a31" + integrity sha512-MyhHbAKVjpn065WzRbqpLu2krj4kHLi6RITQdD1ee+uxq9r2yg5Qe02l24NxKW+1/lkmpusl4Y5Lks7rBiJn4w== + +"@next/swc-linux-x64-musl@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.0.tgz#b0a10db0d9e16f079429588a58f71fa3c3d46178" + integrity sha512-Tz1tJZ5egE0S/UqCd5V6ZPJsdSzv/8aa7FkwFmIJ9neLS8/00za+OY5pq470iZQbPrkTwpKzmfTTIPRVD5iqDg== + +"@next/swc-win32-arm64-msvc@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.0.tgz#3063f850c9db7b774c69e9be74ad59986cf6fc34" + integrity sha512-0iRO/CPMCdCYUzuH6wXLnsfJX1ykBX4emOOvH0qIgtiZM0nVYbF8lkEyY2ph4XcsurpinS+ziWuYCXVqrOSqiw== + +"@next/swc-win32-ia32-msvc@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.0.tgz#001bbadf3d2cf006c4991f728d1d23e4d5c0e7cc" + integrity sha512-8A26RJVcJHwIKm8xo/qk2ePRquJ6WCI2keV2qOW/Qm+ZXrPXHMIWPYABae/nKN243YFBNyPiHytjX37VrcpUhg== + +"@next/swc-win32-x64-msvc@12.2.0": + version "12.2.0" + resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.0.tgz#9f66664f9122ca555b96a5f2fc6e2af677bf801b" + integrity sha512-OI14ozFLThEV3ey6jE47zrzSTV/6eIMsvbwozo+XfdWqOPwQ7X00YkRx4GVMKMC0rM44oGS2gmwMKYpe4EblnA== + +"@noble/ciphers@1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz#3812b72c057a28b44ff0ad4aff5ca846e5b9cdc9" + integrity sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA== + +"@noble/curves@1.4.2", "@noble/curves@~1.4.0": + version "1.4.2" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== + dependencies: + "@noble/hashes" "1.4.0" + +"@noble/curves@1.8.0": + version "1.8.0" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.0.tgz#fe035a23959e6aeadf695851b51a87465b5ba8f7" + integrity sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ== + dependencies: + "@noble/hashes" "1.7.0" + +"@noble/curves@1.8.1", "@noble/curves@~1.8.1": + version "1.8.1" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff" + integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ== + dependencies: + "@noble/hashes" "1.7.1" + +"@noble/curves@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" + integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== + dependencies: + "@noble/hashes" "1.7.2" + +"@noble/curves@^1.6.0", "@noble/curves@~1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45" + integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw== + dependencies: + "@noble/hashes" "1.6.0" + +"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@noble/hashes@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5" + integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ== + +"@noble/hashes@1.7.0": + version "1.7.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz#5d9e33af2c7d04fee35de1519b80c958b2e35e39" + integrity sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w== + +"@noble/hashes@1.7.1", "@noble/hashes@^1.4.0", "@noble/hashes@~1.7.1": + version "1.7.1" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" + integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== + +"@noble/hashes@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" + integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== + +"@noble/hashes@^1.2.0", "@noble/hashes@^1.3.1": + version "1.5.0" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" + integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== + +"@noble/hashes@^1.5.0", "@noble/hashes@~1.6.0": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.1.tgz#df6e5943edcea504bac61395926d6fd67869a0d5" + integrity sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@parcel/watcher-android-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" + integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== + +"@parcel/watcher-darwin-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" + integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== + +"@parcel/watcher-darwin-x64@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" + integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== + +"@parcel/watcher-freebsd-x64@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" + integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== + +"@parcel/watcher-linux-arm-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" + integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== + +"@parcel/watcher-linux-arm64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" + integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== + +"@parcel/watcher-linux-arm64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" + integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== + +"@parcel/watcher-linux-x64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" + integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== + +"@parcel/watcher-linux-x64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" + integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== + +"@parcel/watcher-wasm@^2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.4.1.tgz#c4353e4fdb96ee14389856f7f6f6d21b7dcef9e1" + integrity sha512-/ZR0RxqxU/xxDGzbzosMjh4W6NdYFMqq2nvo2b8SLi7rsl/4jkL8S5stIikorNkdR50oVDvqb/3JT05WM+CRRA== + dependencies: + is-glob "^4.0.3" + micromatch "^4.0.5" + napi-wasm "^1.1.0" + +"@parcel/watcher-win32-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" + integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== + +"@parcel/watcher-win32-ia32@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" + integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== + +"@parcel/watcher-win32-x64@2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" + integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== + +"@parcel/watcher@^2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" + integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.4.1" + "@parcel/watcher-darwin-arm64" "2.4.1" + "@parcel/watcher-darwin-x64" "2.4.1" + "@parcel/watcher-freebsd-x64" "2.4.1" + "@parcel/watcher-linux-arm-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-musl" "2.4.1" + "@parcel/watcher-linux-x64-glibc" "2.4.1" + "@parcel/watcher-linux-x64-musl" "2.4.1" + "@parcel/watcher-win32-arm64" "2.4.1" + "@parcel/watcher-win32-ia32" "2.4.1" + "@parcel/watcher-win32-x64" "2.4.1" + +"@peculiar/asn1-schema@^2.3.13", "@peculiar/asn1-schema@^2.3.8": + version "2.3.13" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.13.tgz#ec8509cdcbc0da3abe73fd7e690556b57a61b8f4" + integrity sha512-3Xq3a01WkHRZL8X04Zsfg//mGaA21xlL4tlVn4v2xGT0JStiztATRkMwa5b+f/HXmY2smsiLXYK46Gwgzvfg3g== + dependencies: + asn1js "^3.0.5" + pvtsutils "^1.3.5" + tslib "^2.6.2" + +"@peculiar/json-schema@^1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" + integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== + dependencies: + tslib "^2.0.0" + +"@peculiar/webcrypto@^1.0.22": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.5.0.tgz#9e57174c02c1291051c553600347e12b81469e10" + integrity sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg== + dependencies: + "@peculiar/asn1-schema" "^2.3.8" + "@peculiar/json-schema" "^1.1.12" + pvtsutils "^1.3.5" + tslib "^2.6.2" + webcrypto-core "^1.8.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.25" + resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817" + integrity sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ== + +"@portkey/accounts@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/accounts/-/accounts-2.19.0-alpha.24.tgz#7d36a7931517079619e059d315019b36e43d8278" + integrity sha512-AFvPE9IMkkw+qpgsPBykyubGHxsMcknwZPe+odqMBRZo1lkPFfHda6fCLq4VFsT4UyiTWsOgUH/39m5R31io8A== + dependencies: + "@portkey/types" "^2.19.0-alpha.24" + "@portkey/utils" "^2.19.0-alpha.24" + "@portkey/validator" "^2.19.0-alpha.24" + +"@portkey/chain@^2.4.0-alpha.12": + version "2.4.0-alpha.12" + resolved "https://registry.yarnpkg.com/@portkey/chain/-/chain-2.4.0-alpha.12.tgz#c01980bb4204d5f699d8e627347a03d8da8de3ce" + integrity sha512-k9T5peAcU9JePQIPDOclgtWoRrs9ipETWRpg4Hnuyvg+uwYtLqBnkr82VVPq06aZfV24LL48j3vPOWRIBZlaOQ== + dependencies: + "@portkey/contracts" "^2.0.4" + "@portkey/provider-types" "^2.4.0-alpha.12" + aelf-sdk "^3.2.45-beta.0" + +"@portkey/connect-web-wallet@^2.20.0-alpha.3": + version "2.20.0-alpha.4" + resolved "https://registry.yarnpkg.com/@portkey/connect-web-wallet/-/connect-web-wallet-2.20.0-alpha.4.tgz#13ef4dbec9502adcf03c610eecfeb7c97f1f1e56" + integrity sha512-Nlt3GeysVBcNJV2ci5KN8muZ5uVN6TU2ZQFwgD1y38+w9pFDvJcKT9IYQqd5de0wjdTkBzDjmqz3OHCBJNUVcg== + dependencies: + "@portkey/iframe-provider" "^2.4.0-alpha.9" + "@portkey/provider-types" "^2.4.0-alpha.9" + "@portkey/utils" "^2.20.0-alpha.4" + antd "5.22.7" + bignumber.js "^9.1.0" + clsx "^1.2.1" + lottie-web "5.9.6" + moment "^2.29.4" + query-string "^7.1.1" + react-use "^17.4.0" + +"@portkey/contracts@^2.0.4": + version "2.18.0" + resolved "https://registry.yarnpkg.com/@portkey/contracts/-/contracts-2.18.0.tgz#6f07bfedce8e99e7c0ed01b2beb890964805d7eb" + integrity sha512-CUceC9dYnZN/wtKhLsjk5ijTaYYA0w+sxO9GfJ+H3HvMM5ekuxAXj1my2HyKIo6t5pXcKNmgjZF+nvcCITXnoA== + dependencies: + "@portkey/provider-types" "^2.1.0" + "@portkey/types" "^2.18.0" + "@portkey/utils" "^2.18.0" + +"@portkey/contracts@^2.19.0-alpha.10", "@portkey/contracts@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/contracts/-/contracts-2.19.0-alpha.24.tgz#f1f20d59eeb56b5993d883517e52699ce14bd8f0" + integrity sha512-suq3Ez+X/b6P5pXG4na8nG6msu1yif/KV5NTsS7MyqQO6xFTpnBkxIK+R4vCGMEgjPu4sgJMqThibrzNaQXeug== + dependencies: + "@portkey/provider-types" "^2.4.0-alpha.9" + "@portkey/types" "^2.19.0-alpha.24" + "@portkey/utils" "^2.19.0-alpha.24" + +"@portkey/contracts@^2.20.0-alpha.3": + version "2.20.0-alpha.4" + resolved "https://registry.yarnpkg.com/@portkey/contracts/-/contracts-2.20.0-alpha.4.tgz#4fc62e14ac2ea8cfab9e766a8b3c7f3cc604eba3" + integrity sha512-afeBN/O3AVUo1R3gqcaeUMYIV6pqm4CmsZ7ETceO6VW4DMZ4am/kuOIGdT/soIthErRuddOENaCqNghvavd7Lg== + dependencies: + "@portkey/provider-types" "^2.1.0" + "@portkey/types" "^2.20.0-alpha.4" + "@portkey/utils" "^2.20.0-alpha.4" + +"@portkey/detect-provider@2.4.0-alpha.12", "@portkey/detect-provider@^2.4.0-alpha.6": + version "2.4.0-alpha.12" + resolved "https://registry.yarnpkg.com/@portkey/detect-provider/-/detect-provider-2.4.0-alpha.12.tgz#d32c8a8c75ec944b941f8c2be37bece2a594e853" + integrity sha512-oFfcYeUGkVumoKZAxZRR8EtmsQP9FQAY4EVuf27QepnUodBr5iBCqqn+vt8cDdTvIYDZvffh2XdH2iaLuOYPDw== + dependencies: + "@portkey/provider-types" "^2.4.0-alpha.12" + +"@portkey/detect-provider@^2.0.0-alpha.2": + version "2.2.4" + resolved "https://registry.npmjs.org/@portkey/detect-provider/-/detect-provider-2.2.4.tgz#f2554e3e46f0ec13b9dfaed0f40af48312d30af2" + integrity sha512-ycFTFGDg/9NSzo98HpncSihG+mhdYbkcFCgINcgEcIiHaJNeP5GgHVKb9cgcfEorqbCm3WLek4cAKDHh/ibRMQ== + dependencies: + "@portkey/provider-types" "^2.2.4" + +"@portkey/detect-provider@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@portkey/detect-provider/-/detect-provider-2.3.5.tgz#7d876684ca1667efd118d0d4d592cfaf085c68d3" + integrity sha512-x5Jl8IE4EGi6zdfup9LMncL04gXS/d0YxdaESC5vd7TPwvSnjra2ZJPGdZrydBzhQYHMPeKy/bZCyY/o7WH+Cg== + dependencies: + "@portkey/provider-types" "^2.3.5" + +"@portkey/did-ui-react@^2.19.0-alpha.10": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/did-ui-react/-/did-ui-react-2.19.0-alpha.24.tgz#d7efb37044725fc82fe127744a5304f2a6692eb8" + integrity sha512-5TBHvmnq1x8xWGny3tUoBBLDP7JnUraYPZaeebzWIW0QWAQuWJ8ZA7FTy1gAbxiLWJABjFnd2qSU+3LqWkmK9A== + dependencies: + "@etransfer/core" "^1.0.0-alpha.9" + "@etransfer/services" "^1.0.0-alpha.9" + "@etransfer/types" "^1.0.0-alpha.9" + "@etransfer/utils" "^1.0.0-alpha.9" + "@matt-block/react-recaptcha-v2" "^2.0.0" + "@portkey/accounts" "^2.19.0-alpha.24" + "@portkey/contracts" "^2.19.0-alpha.24" + "@portkey/did" "^2.19.0-alpha.24" + "@portkey/graphql" "^2.19.0-alpha.24" + "@portkey/onboarding" "^2.19.0-alpha.24" + "@portkey/ramp" "^2.19.0-alpha.24" + "@portkey/request" "^2.19.0-alpha.24" + "@portkey/services" "^2.19.0-alpha.24" + "@portkey/socket" "^2.19.0-alpha.24" + "@portkey/types" "^2.19.0-alpha.24" + "@portkey/utils" "^2.19.0-alpha.24" + "@portkey/validator" "^2.19.0-alpha.24" + "@rc-component/portal" "1.0.2" + antd "4.24.14" + antd-mobile "5.33.0" + bignumber.js "^9.1.0" + clsx "^1.2.1" + lottie-web "5.9.6" + moment "^2.29.4" + query-string "^7.1.1" + rc-notification "^4.0.0" + react-i18next "^11.18.6" + react-qrcode-logo "2.9.0" + react-use "^17.4.0" + spark-md5 "^3.0.2" + uuid "^8.3.2" + web3 "^1.8.0" + web3-eth-contract "^1.8.0" + web3-utils "^1.8.0" + +"@portkey/did@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/did/-/did-2.19.0-alpha.24.tgz#c8ec70cfe5e27cfeec96df0a4fb246b35852703c" + integrity sha512-umtIJcXsMS0iPDiVYRr+5keitIwILrA9ugTplEQrYLKqhprnZOzZv9ck7zzG8pqEmmMVhBDNBtRzc8yysesHBA== + dependencies: + "@portkey/accounts" "^2.19.0-alpha.24" + "@portkey/contracts" "^2.19.0-alpha.24" + "@portkey/graphql" "^2.19.0-alpha.24" + "@portkey/request" "^2.19.0-alpha.24" + "@portkey/services" "^2.19.0-alpha.24" + "@portkey/types" "^2.19.0-alpha.24" + "@portkey/utils" "^2.19.0-alpha.24" + +"@portkey/graphql@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/graphql/-/graphql-2.19.0-alpha.24.tgz#d1ec932406358b4c49713c280059f99400ef9045" + integrity sha512-qPzVFVieeq0FQgwx+LstsuFiKO/22L3g3bF5w2FsTC359UGGGkvbbSs+HhZ4WCaTRjxSPQo8bDEn8BimTht20A== + dependencies: + "@apollo/client" "^3.7.3" + "@portkey/types" "^2.19.0-alpha.24" + graphql "^16.6.0" + subscriptions-transport-ws "^0.11.0" + +"@portkey/graphql@^2.20.0-alpha.4": + version "2.20.0-alpha.4" + resolved "https://registry.yarnpkg.com/@portkey/graphql/-/graphql-2.20.0-alpha.4.tgz#74006071a868adb38935bebfe672b23921931afe" + integrity sha512-7EHmjuuCCJ54zQbAwpjhXrxpED/AfvpPonoqUz7MY8b9P43v3LGpdoMqQ8mR3IdYbiwEX+i8iZxrfZACBkmcMw== + dependencies: + "@apollo/client" "^3.7.3" + "@portkey/types" "^2.20.0-alpha.4" + graphql "^16.6.0" + subscriptions-transport-ws "^0.11.0" + +"@portkey/iframe-provider@^2.4.0-alpha.9": + version "2.4.0-alpha.12" + resolved "https://registry.yarnpkg.com/@portkey/iframe-provider/-/iframe-provider-2.4.0-alpha.12.tgz#85013bb551604379c177a1e664a39988562d8aac" + integrity sha512-dWpO88vF5UqvSbgvDoM9AlEkyJBG2THfTTqD/2zxpcaBOHvUnv5khaHZYNTY5BuzHsXRbm6n4j4gaFzaNNvwOA== + dependencies: + "@portkey/provider-types" "^2.4.0-alpha.12" + "@portkey/providers" "^2.4.0-alpha.12" + "@types/elliptic" "^6.4.14" + readable-stream "^4.4.0" + +"@portkey/onboarding@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/onboarding/-/onboarding-2.19.0-alpha.24.tgz#abf449bba95351cbfc9e758d3362a387046e85e4" + integrity sha512-DmxHv6cR/TCFB6d3s8o7dbiRFtp2hRoSU05igRQNjcZzNa+lcMt09VUTkb86kcPrPaDLK2NbuM9SYk9pP6X+hQ== + dependencies: + "@portkey/detect-provider" "^2.0.0-alpha.2" + "@portkey/types" "^2.19.0-alpha.24" + "@portkey/utils" "^2.19.0-alpha.24" + bowser "^2.11.0" + query-string "^7.1.1" + +"@portkey/provider-types@2.4.0-alpha.12", "@portkey/provider-types@^2.4.0-alpha.12", "@portkey/provider-types@^2.4.0-alpha.6", "@portkey/provider-types@^2.4.0-alpha.9": + version "2.4.0-alpha.12" + resolved "https://registry.yarnpkg.com/@portkey/provider-types/-/provider-types-2.4.0-alpha.12.tgz#46ab3b4d53d4fdae87c472c7b2c3032b8e01975c" + integrity sha512-s0319Oqh4y7VxfiIr6etVfB/0FaghZ+/wHqfIpHD9GlTMLxFW+Vg/hUEQ41CamygpzKGXXOptsrXPt7GQLX3LQ== + dependencies: + "@portkey/types" "^2.0.0-alpha.0" + "@types/readable-stream" "^2.3.15" + +"@portkey/provider-types@^2.1.0", "@portkey/provider-types@^2.2.4": + version "2.2.4" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-2.2.4.tgz#e9ca584db2877068036631988af86dd95034d4fb" + integrity sha512-80ebarxVJXfOPdIPkyoL4j2Qomga+MMgc2k99vwUOtmniKEl+QYxgly348cfY7JdaSjAU04RAf3p7vlxKlclxg== + dependencies: + "@portkey/types" "^2.0.0-alpha.0" + "@types/readable-stream" "^2.3.15" + +"@portkey/provider-types@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@portkey/provider-types/-/provider-types-2.3.5.tgz#8b4c40867eb186f5f8f283f6c0d80badcd339ad3" + integrity sha512-xQeY3dEz+yFptnKr/mB9eo1rW5hsLMntAjwzzpgNyJEWrV6BGBH4S1s+wiK0mdDhu2BzkJ0yVgJ2MwGhWkeLbw== + dependencies: + "@portkey/types" "^2.0.0-alpha.0" + "@types/readable-stream" "^2.3.15" + +"@portkey/provider-utils@^2.4.0-alpha.12": + version "2.4.0-alpha.12" + resolved "https://registry.yarnpkg.com/@portkey/provider-utils/-/provider-utils-2.4.0-alpha.12.tgz#6fa9545b63eb6ae62d4e3843c6fe997daa51572d" + integrity sha512-/hXxayGBeLyrl8T8VTP3Kfo0rKzsyjM8bNPbqJu2bs4VBIe2csK398zDXhjqnpI5YZZY9DI7c1JescgTslbyvQ== + dependencies: + "@portkey/provider-types" "^2.4.0-alpha.12" + +"@portkey/providers@^2.4.0-alpha.12": + version "2.4.0-alpha.12" + resolved "https://registry.yarnpkg.com/@portkey/providers/-/providers-2.4.0-alpha.12.tgz#dcd29dc0e3dbb11bcc21000e2fb123f78e8e3ba9" + integrity sha512-sU8Y6XC7kMRIY7loIhnNdIh/29j4rxA/zzLEBRGnty2iAaDTOIGdrOLxLz0UggEbqUGT82hG8KWxlSqaUbySbw== + dependencies: + "@portkey/chain" "^2.4.0-alpha.12" + "@portkey/provider-types" "^2.4.0-alpha.12" + "@portkey/provider-utils" "^2.4.0-alpha.12" + "@types/readable-stream" "^2.3.15" + lodash "^4.17.21" + readable-stream "^4.4.0" + +"@portkey/ramp@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/ramp/-/ramp-2.19.0-alpha.24.tgz#feee541b2c05985bc15b1739d7d7da3e3e841362" + integrity sha512-UeajZOie+BBRZkqRPUJa26To927taf+PxC6wCsIYGnz7VYILaQYMseTrIAnXJT2IvAOacyaw8LD6xH3M7z9chw== + dependencies: + "@portkey/request" "^2.19.0-alpha.24" + "@portkey/socket" "^2.19.0-alpha.24" + "@portkey/types" "^2.19.0-alpha.24" + "@portkey/utils" "^2.19.0-alpha.24" + query-string "^7.1.1" + +"@portkey/request@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/request/-/request-2.19.0-alpha.24.tgz#b3129b5b847d901dca80f124183dfcc82c57f93f" + integrity sha512-ZEYp6a4BtPOj1jqk5OUZiecJUD5nj0xyQ2HXSlSZjf8LVdkjVb8Azl04tMUanbIud0wy5r38XZJqw+06lUQ0Mw== + dependencies: + "@portkey/types" "^2.19.0-alpha.24" + query-string "^7.1.1" + +"@portkey/request@^2.20.0-alpha.4": + version "2.20.0-alpha.4" + resolved "https://registry.yarnpkg.com/@portkey/request/-/request-2.20.0-alpha.4.tgz#7c97c54025bbb7d2a0b2fead0ef6443a047b57a2" + integrity sha512-cbQ6QkgEuytbbJuVFkElkhGbU21B5m1NVlrQXEElgP0vXJjMDxRBMlvN0PzOZyPSIorf7b9YMB7x7W0yPdbLBA== + dependencies: + "@portkey/types" "^2.20.0-alpha.4" + query-string "^7.1.1" + +"@portkey/services@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/services/-/services-2.19.0-alpha.24.tgz#ce11376018b36ca2191c0894bc219021365be591" + integrity sha512-PrNwN4S13ous3OX0qLIka3wcKBD6Re3+TgJwcWbXof+ag07M6zR2MzvtdIVK3t6pN4ic1sgi4UvQ6Iv68HGrsQ== + dependencies: + "@portkey/graphql" "^2.19.0-alpha.24" + "@portkey/request" "^2.19.0-alpha.24" + "@portkey/types" "^2.19.0-alpha.24" + "@portkey/utils" "^2.19.0-alpha.24" + +"@portkey/services@^2.20.0-alpha.3": + version "2.20.0-alpha.4" + resolved "https://registry.yarnpkg.com/@portkey/services/-/services-2.20.0-alpha.4.tgz#7669582b0b64b289bbf204f4ded5fc264d787786" + integrity sha512-Rb0bcUxMVj631cd+L1eUvpXUaZEslgJKaht8c+xODVIZO+oDAUqPN5QSJHcZSI8uHAV8Pk6Li73wUFbAN7dIow== + dependencies: + "@portkey/graphql" "^2.20.0-alpha.4" + "@portkey/request" "^2.20.0-alpha.4" + "@portkey/types" "^2.20.0-alpha.4" + "@portkey/utils" "^2.20.0-alpha.4" + +"@portkey/socket@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/socket/-/socket-2.19.0-alpha.24.tgz#e8f1589fab1b36f2a6fccd48054bcefe6db14fa7" + integrity sha512-e2t0ybHPauZmLgFyiOQbcwb0EdI8ujPrIO2txLzN7e5FKWTkeNqNaUwnRDFlJswhR8uiDAprS2CA9jxxPN4EDg== + dependencies: + "@abp/signalr" "^7.0.0" + "@portkey/utils" "^2.19.0-alpha.24" + +"@portkey/types@2.15.10": + version "2.15.10" + resolved "https://registry.yarnpkg.com/@portkey/types/-/types-2.15.10.tgz#35c37803ee4796fe15b7967bea59a6f0fd931fc9" + integrity sha512-RQddcR9AfWbapYM4ovAkCXOZGULuuYCbTwYkUTaqRf0WflG3IHB+sQvDMUSMJAw0fo4dPND7Qg0hS2mvHVVWRw== + +"@portkey/types@^2.0.0-alpha.0": + version "2.10.1" + resolved "https://registry.npmjs.org/@portkey/types/-/types-2.10.1.tgz#e1bd8fa542c7a122f89634c782b25ec73d57b12a" + integrity sha512-UOregcAY1SVT4aT1rQ8wbl2H5VLu0oHATixoy0KKBbFab3TjBvlCB0oNbrGPJPr7FRbLDtNr0Wg8SSeJU/waIw== + +"@portkey/types@^2.18.0": + version "2.18.0" + resolved "https://registry.yarnpkg.com/@portkey/types/-/types-2.18.0.tgz#945927ab340bd4b2c00694af4bc20cee9e6c173d" + integrity sha512-L52YDVjYBa/xXvj2woNN84lYTzyDfh0/40JTzCaWaL66e1TIBpfDZfUoq8szsltzeNEeYkLdJ5eXbCjzsGGFDQ== + +"@portkey/types@^2.19.0-alpha.10", "@portkey/types@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/types/-/types-2.19.0-alpha.24.tgz#9c3df3ab32a43692b59b650d2a7fa90baa93507d" + integrity sha512-zJBWZFCi2t3vXia4/0H/D5Ws742x3tEKjTX4HNpYklx8cVZ4/nVuZIAjSFaR/mEImo47Q7gYpBZaFZOpPixYBg== + +"@portkey/types@^2.20.0-alpha.4": + version "2.20.0-alpha.4" + resolved "https://registry.yarnpkg.com/@portkey/types/-/types-2.20.0-alpha.4.tgz#32d171f4c1c6a2d61596a0e445d6b97dd6154144" + integrity sha512-K+shlyP0PSKBGVdTuHq7TUqv8wcDcUtHh4xXliUvjGP3vOh1xvg7eAx5Jhu3ilPv2wjqSQGngLKzuiG0UNrFUw== + +"@portkey/utils@^2.18.0": + version "2.18.0" + resolved "https://registry.yarnpkg.com/@portkey/utils/-/utils-2.18.0.tgz#e521ea87243caddb29ae822f84ac1f2ba08d7c33" + integrity sha512-HEMD9yb57Z4P70Tya7cyKC2BuGqKc7UxvhvNXTRD9wV/m5iE0r5SfUc/wIDcwPXlXkH6+NK9xDmVy56oqNc1EA== + dependencies: + node-forge "^1.3.1" + +"@portkey/utils@^2.19.0-alpha.10", "@portkey/utils@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/utils/-/utils-2.19.0-alpha.24.tgz#33adba55db2487ec3756ad5e285c02e10d67b7b6" + integrity sha512-q6BLA1qutarLMnKn/AZOfrP/11K+Kp0cCJqHr1ZV9lkJz+sPK+lBwpkzhb+4isnkDT5kufwSahgcuy1J2sbM/Q== + dependencies: + is-url "^1.2.4" + node-forge "^1.3.1" + +"@portkey/utils@^2.20.0-alpha.3", "@portkey/utils@^2.20.0-alpha.4": + version "2.20.0-alpha.4" + resolved "https://registry.yarnpkg.com/@portkey/utils/-/utils-2.20.0-alpha.4.tgz#973ae08bd13d2fea801a28e613433537afc2f6cf" + integrity sha512-73+iE+mELCLiJ2b8hkz/VroSX0fwLnubX00ktcSmNhuTG8Zj88Uhjh+KAawNhAsmKxMoxoZXmRciHDIIp1HjaA== + dependencies: + is-url "^1.2.4" + node-forge "^1.3.1" + +"@portkey/validator@^2.19.0-alpha.24": + version "2.19.0-alpha.24" + resolved "https://registry.yarnpkg.com/@portkey/validator/-/validator-2.19.0-alpha.24.tgz#0c08824f97a52c653c3588c4ad0f29f762468d5d" + integrity sha512-ATgB+gbA8AVmyTGWnBv6ciBYXytTtjq2fqaGqzLiHQh7IlDeSCq/GsGkbVjljBcKmsGsho3RzC20Gbl5RUS9Hw== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + +"@rc-component/async-validator@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948" + integrity sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== + dependencies: + "@babel/runtime" "^7.24.4" + +"@rc-component/color-picker@~2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz#6b9b96152466a9d4475cbe72b40b594bfda164be" + integrity sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== + dependencies: + "@ant-design/fast-color" "^2.0.6" + "@babel/runtime" "^7.23.6" + classnames "^2.2.6" + rc-util "^5.38.1" + +"@rc-component/context@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" + integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== + dependencies: + "@babel/runtime" "^7.10.1" + rc-util "^5.27.0" + +"@rc-component/mini-decimal@^1.0.1", "@rc-component/mini-decimal@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" + integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== + dependencies: + "@babel/runtime" "^7.18.0" + +"@rc-component/mutate-observer@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" + integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/portal@1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@rc-component/portal/-/portal-1.0.2.tgz#c14cdd21e7da42c0db417ce01c624df170349ec6" + integrity sha512-fEfbp5PcE+63zukZFLxqtPz7JSX4G5s6IUIqodLeoVeZ9I0mN1OGjOFDDX/BOpGxdoviH7xchIHxYxXJn2ReRg== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.16.0" + +"@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" + integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/qrcode@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.0.0.tgz#48a8de5eb11d0e65926f1377c4b1ef4c888997f5" + integrity sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg== + dependencies: + "@babel/runtime" "^7.24.7" + classnames "^2.3.2" + rc-util "^5.38.0" + +"@rc-component/tour@~1.15.1": + version "1.15.1" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.1.tgz#9b79808254185fc19e964172d99e25e8c6800ded" + integrity sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== + dependencies: + "@babel/runtime" "^7.18.0" + "@rc-component/portal" "^1.0.0-9" + "@rc-component/trigger" "^2.0.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.6": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.3.0.tgz#9499ada078daca9dd99d01f0f0743ee1ab9e398b" + integrity sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg== + dependencies: + "@babel/runtime" "^7.23.2" + "@rc-component/portal" "^1.1.0" + classnames "^2.3.2" + rc-motion "^2.0.0" + rc-resize-observer "^1.3.1" + rc-util "^5.44.0" + +"@react-spring/animated@~9.6.1": + version "9.6.1" + resolved "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz#ccc626d847cbe346f5f8815d0928183c647eb425" + integrity sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ== + dependencies: + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@react-spring/core@~9.6.1": + version "9.6.1" + resolved "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz#ebe07c20682b360b06af116ea24e2b609e778c10" + integrity sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ== + dependencies: + "@react-spring/animated" "~9.6.1" + "@react-spring/rafz" "~9.6.1" + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@react-spring/rafz@~9.6.1": + version "9.6.1" + resolved "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz#d71aafb92b78b24e4ff84639f52745afc285c38d" + integrity sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ== + +"@react-spring/shared@~9.6.1": + version "9.6.1" + resolved "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz#4e2e4296910656c02bd9fd54c559702bc836ac4e" + integrity sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw== + dependencies: + "@react-spring/rafz" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@react-spring/types@~9.6.1": + version "9.6.1" + resolved "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz#913d3a68c5cbc1124fdb18eff919432f7b6abdde" + integrity sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q== + +"@react-spring/web@~9.6.1": + version "9.6.1" + resolved "https://registry.npmjs.org/@react-spring/web/-/web-9.6.1.tgz#3e4c03b724d2b545dc2fa2649eb6109318ab9178" + integrity sha512-X2zR6q2Z+FjsWfGAmAXlQaoUHbPmfuCaXpuM6TcwXPpLE1ZD4A1eys/wpXboFQmDkjnrlTmKvpVna1MjWpZ5Hw== + dependencies: + "@react-spring/animated" "~9.6.1" + "@react-spring/core" "~9.6.1" + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@reduxjs/toolkit@^2.5.0": + version "2.8.2" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.8.2.tgz#f4e9f973c6fc930c1e0f3bf462cc95210c28f5f9" + integrity sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A== + dependencies: + "@standard-schema/spec" "^1.0.0" + "@standard-schema/utils" "^0.3.0" + immer "^10.0.3" + redux "^5.0.1" + redux-thunk "^3.1.0" + reselect "^5.1.0" + +"@rollup/plugin-commonjs@24.0.0": + version "24.0.0" + resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.0.tgz#fb7cf4a6029f07ec42b25daa535c75b05a43f75c" + integrity sha512-0w0wyykzdyRRPHOb0cQt14mIBLujfAv6GgP6g8nvg/iBxEm112t3YPPq+Buqe2+imvElTka+bjNlJ/gB56TD8g== + dependencies: + "@rollup/pluginutils" "^5.0.1" + commondir "^1.0.1" + estree-walker "^2.0.2" + glob "^8.0.3" + is-reference "1.2.1" + magic-string "^0.27.0" + +"@rollup/pluginutils@^5.0.1": + version "5.1.0" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + +"@rollup/rollup-android-arm-eabi@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz#e554185b1afa5509a7a4040d15ec0c3b4435ded1" + integrity sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw== + +"@rollup/rollup-android-arm64@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz#b1ee64bb413b2feba39803b0a1bebf2a9f3d70e1" + integrity sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg== + +"@rollup/rollup-darwin-arm64@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz#bfdce3e07a345dd1bd628f3b796050f39629d7f0" + integrity sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA== + +"@rollup/rollup-darwin-x64@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz#781a94a537c57bdf0a500e47a25ab5985e5e8dff" + integrity sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA== + +"@rollup/rollup-freebsd-arm64@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz#7a028357cbd12c5869c446ad18177c89f3405102" + integrity sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA== + +"@rollup/rollup-freebsd-x64@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz#f24836a6371cccc4408db74f0fd986dacf098950" + integrity sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q== + +"@rollup/rollup-linux-arm-gnueabihf@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz#95f27e96f0eb9b9ae9887739a8b6dffc90c1237f" + integrity sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ== + +"@rollup/rollup-linux-arm-musleabihf@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz#677b34fba9d070877736c3fe8b02aacb5e142d97" + integrity sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw== + +"@rollup/rollup-linux-arm64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz#32d3d19dedde54e91574a098f22ea43a09cf63dd" + integrity sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA== + +"@rollup/rollup-linux-arm64-musl@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz#a58dff44a18696df65ed8c0ad68a2945cf900484" + integrity sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw== + +"@rollup/rollup-linux-loongarch64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz#a7488ab078233111e8aeb370d1ecf107ec7e1716" + integrity sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw== + +"@rollup/rollup-linux-powerpc64le-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz#e9b9c0d6bd248a92b2d6ec01ebf99c62ae1f2e9a" + integrity sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w== + +"@rollup/rollup-linux-riscv64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz#0df84ce2bea48ee686fb55060d76ab47aff45c4c" + integrity sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw== + +"@rollup/rollup-linux-s390x-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz#73df374c57d036856e33dbd2715138922e91e452" + integrity sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw== + +"@rollup/rollup-linux-x64-gnu@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz#f27af0b55f0cdd84e182e6cd44a6d03da0458149" + integrity sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA== + +"@rollup/rollup-linux-x64-musl@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz#c7981ad5cfb8c3cd5d643d33ca54e4d2802b9201" + integrity sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ== + +"@rollup/rollup-win32-arm64-msvc@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz#06cedc0ef3cbf1cbd8abcf587090712e40ae6941" + integrity sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw== + +"@rollup/rollup-win32-ia32-msvc@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz#90b39b977b14961a769be6ea61238e7fc668dd4d" + integrity sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg== + +"@rollup/rollup-win32-x64-msvc@4.34.7": + version "4.34.7" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz#6531d61e7141091eaab0461ee8e0380c10e4ca57" + integrity sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w== + +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== + +"@rushstack/eslint-patch@^1.1.3": + version "1.10.4" + resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1" + integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA== + +"@safe-global/safe-apps-provider@0.18.3": + version "0.18.3" + resolved "https://registry.npmjs.org/@safe-global/safe-apps-provider/-/safe-apps-provider-0.18.3.tgz#805a42e24f5dde803cb96dac251a3c9e256de45b" + integrity sha512-f/0cNv3S4v7p8rowAjj0hDCg8Q8P/wBjp5twkNWeBdvd0RDr7BuRBPPk74LCqmjQ82P+1ltLlkmVFSmxTIT7XQ== + dependencies: + "@safe-global/safe-apps-sdk" "^9.1.0" + events "^3.3.0" + +"@safe-global/safe-apps-sdk@9.1.0", "@safe-global/safe-apps-sdk@^9.1.0": + version "9.1.0" + resolved "https://registry.npmjs.org/@safe-global/safe-apps-sdk/-/safe-apps-sdk-9.1.0.tgz#0e65913e0f202e529ed3c846e0f5a98c2d35aa98" + integrity sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q== + dependencies: + "@safe-global/safe-gateway-typescript-sdk" "^3.5.3" + viem "^2.1.1" + +"@safe-global/safe-gateway-typescript-sdk@^3.5.3": + version "3.22.4" + resolved "https://registry.yarnpkg.com/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.22.4.tgz#9109a538df40f778666a3e6776e7a08c757e893d" + integrity sha512-Z7Z8w3GEJdJ/paF+NK23VN4AwqWPadq0AeRYjYLjIBiPWpRB2UO/FKq7ONABEq0YFgNPklazIV4IExQU1gavXA== + +"@scure/base@^1.1.3", "@scure/base@~1.1.6": + version "1.1.8" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz#8f23646c352f020c83bca750a82789e246d42b50" + integrity sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg== + +"@scure/base@~1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.1.tgz#dd0b2a533063ca612c17aa9ad26424a2ff5aa865" + integrity sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ== + +"@scure/base@~1.2.2", "@scure/base@~1.2.4": + version "1.2.4" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9" + integrity sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ== + +"@scure/bip32@1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" + integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== + dependencies: + "@noble/curves" "~1.4.0" + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + +"@scure/bip32@1.6.2": + version "1.6.2" + resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz#093caa94961619927659ed0e711a6e4bf35bffd0" + integrity sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw== + dependencies: + "@noble/curves" "~1.8.1" + "@noble/hashes" "~1.7.1" + "@scure/base" "~1.2.2" + +"@scure/bip32@^1.5.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.0.tgz#6dbc6b4af7c9101b351f41231a879d8da47e0891" + integrity sha512-82q1QfklrUUdXJzjuRU7iG7D7XiFx5PHYVS0+oeNKhyDLT7WPqs6pBcM2W5ZdwOwKCwoE1Vy1se+DHjcXwCYnA== + dependencies: + "@noble/curves" "~1.7.0" + "@noble/hashes" "~1.6.0" + "@scure/base" "~1.2.1" + +"@scure/bip39@1.3.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" + integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== + dependencies: + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + +"@scure/bip39@1.5.4": + version "1.5.4" + resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz#07fd920423aa671be4540d59bdd344cc1461db51" + integrity sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA== + dependencies: + "@noble/hashes" "~1.7.1" + "@scure/base" "~1.2.4" + +"@scure/bip39@^1.4.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.0.tgz#c8f9533dbd787641b047984356531d84485f19be" + integrity sha512-Dop+ASYhnrwm9+HA/HwXg7j2ZqM6yk2fyLWb5znexjctFY3+E+eU8cIWI0Pql0Qx4hPZCijlGq4OL71g+Uz30A== + dependencies: + "@noble/hashes" "~1.6.0" + "@scure/base" "~1.2.1" + +"@sentry-internal/feedback@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.119.0.tgz#429b3ea0fd34e928d2e7de5dcbe9377272a3f221" + integrity sha512-om8TkAU5CQGO8nkmr7qsSBVkP+/vfeS4JgtW3sjoTK0fhj26+DljR6RlfCGWtYQdPSP6XV7atcPTjbSnsmG9FQ== + dependencies: + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry-internal/replay-canvas@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.119.0.tgz#85669d184ba79150e64d05de02f5e2b616e68371" + integrity sha512-NL02VQx6ekPxtVRcsdp1bp5Tb5w6vnfBKSIfMKuDRBy5A10Uc3GSoy/c3mPyHjOxB84452A+xZSx6bliEzAnuA== + dependencies: + "@sentry/core" "7.119.0" + "@sentry/replay" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry-internal/tracing@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.119.0.tgz#201561af2a4ad1837333287c26050a5e688537ca" + integrity sha512-oKdFJnn+56f0DHUADlL8o9l8jTib3VDLbWQBVkjD9EprxfaCwt2m8L5ACRBdQ8hmpxCEo4I8/6traZ7qAdBUqA== + dependencies: + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry/browser@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/browser/-/browser-7.119.0.tgz#65004015c107be5d2f49a852ebcffc5d19d90e0d" + integrity sha512-WwmW1Y4D764kVGeKmdsNvQESZiAn9t8LmCWO0ucBksrjL2zw9gBPtOpRcO6l064sCLeSxxzCN+kIxhRm1gDFEA== + dependencies: + "@sentry-internal/feedback" "7.119.0" + "@sentry-internal/replay-canvas" "7.119.0" + "@sentry-internal/tracing" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/integrations" "7.119.0" + "@sentry/replay" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry/cli@^1.77.1": + version "1.77.3" + resolved "https://registry.npmjs.org/@sentry/cli/-/cli-1.77.3.tgz#c40b4d09b0878d6565d42a915855add99db4fec3" + integrity sha512-c3eDqcDRmy4TFz2bFU5Y6QatlpoBPPa8cxBooaS4aMQpnIdLYPF1xhyyiW0LQlDUNc3rRjNF7oN5qKoaRoMTQQ== + dependencies: + https-proxy-agent "^5.0.0" + mkdirp "^0.5.5" + node-fetch "^2.6.7" + progress "^2.0.3" + proxy-from-env "^1.1.0" + which "^2.0.2" + +"@sentry/core@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/core/-/core-7.119.0.tgz#a6e41119bb03ec27689f9ad04e79d1fba5b7fc37" + integrity sha512-CS2kUv9rAJJEjiRat6wle3JATHypB0SyD7pt4cpX5y0dN5dZ1JrF57oLHRMnga9fxRivydHz7tMTuBhSSwhzjw== + dependencies: + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry/integrations@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.119.0.tgz#5b25c603026dbacfe1ae7bb8d768506a129149fb" + integrity sha512-OHShvtsRW0A+ZL/ZbMnMqDEtJddPasndjq+1aQXw40mN+zeP7At/V1yPZyFaURy86iX7Ucxw5BtmzuNy7hLyTA== + dependencies: + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + localforage "^1.8.1" + +"@sentry/nextjs@^7.48.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/nextjs/-/nextjs-7.119.0.tgz#d143305224a18172592355407b12c796e3b70965" + integrity sha512-D2P0LmgQbF/d7Ar6u2OKj+strM1id6OFfsHAKeTYd6KVipwc4YBpbS5OYkwH3BhtofCXvtfU3VmayVJD1onOiw== + dependencies: + "@rollup/plugin-commonjs" "24.0.0" + "@sentry/core" "7.119.0" + "@sentry/integrations" "7.119.0" + "@sentry/node" "7.119.0" + "@sentry/react" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + "@sentry/vercel-edge" "7.119.0" + "@sentry/webpack-plugin" "1.21.0" + chalk "3.0.0" + resolve "1.22.8" + rollup "2.78.0" + stacktrace-parser "^0.1.10" + +"@sentry/node@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/node/-/node-7.119.0.tgz#d429ecbba9ce22caf05e811cb666a045e167b6d7" + integrity sha512-9PFzN8xS6U0oZCflpVxS2SSIsHkCaj7qYBlsvHj4CTGWfao9ImwrU6+smy4qoG6oxwPfoVb5pOOMb4WpWOvXcQ== + dependencies: + "@sentry-internal/tracing" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/integrations" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry/react@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/react/-/react-7.119.0.tgz#79f2c9d94322a3afbfa8af9f5b872f7c2e9b0820" + integrity sha512-cf8Cei+qdSA26gx+IMAuc/k44PeBImNzIpXi3930SLhUe44ypT5OZ/44L6xTODHZzTIyMSJPduf59vT2+eW9yg== + dependencies: + "@sentry/browser" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + hoist-non-react-statics "^3.3.2" + +"@sentry/replay@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/replay/-/replay-7.119.0.tgz#50881079d013c77f87a994331d8bcad1d49e0960" + integrity sha512-BnNsYL+X5I4WCH6wOpY6HQtp4MgVt0NVlhLUsEyrvMUiTs0bPkDBrulsgZQBUKJsbOr3l9nHrFoNVB/0i6WNLA== + dependencies: + "@sentry-internal/tracing" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry/types@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/types/-/types-7.119.0.tgz#8b3d7a1405c362e75cd900d46089df4e70919d2a" + integrity sha512-27qQbutDBPKGbuJHROxhIWc1i0HJaGLA90tjMu11wt0E4UNxXRX+UQl4Twu68v4EV3CPvQcEpQfgsViYcXmq+w== + +"@sentry/utils@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/utils/-/utils-7.119.0.tgz#debe29020f6ef3786a5bd855cf1b97116b7be826" + integrity sha512-ZwyXexWn2ZIe2bBoYnXJVPc2esCSbKpdc6+0WJa8eutXfHq3FRKg4ohkfCBpfxljQGEfP1+kfin945lA21Ka+A== + dependencies: + "@sentry/types" "7.119.0" + +"@sentry/vercel-edge@7.119.0": + version "7.119.0" + resolved "https://registry.npmjs.org/@sentry/vercel-edge/-/vercel-edge-7.119.0.tgz#15bfe590045262515c2ecf45fb6e1f71e0dbe5f2" + integrity sha512-9gi5SrBSHhHFYBq/+vG1qC9r80eEskCf7ti/qYSvXc6zij5ieilurF5tunyAle+ayxfHdPTdkhUnDZT6G9jdmA== + dependencies: + "@sentry-internal/tracing" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/integrations" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" + +"@sentry/webpack-plugin@1.21.0": + version "1.21.0" + resolved "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-1.21.0.tgz#bbe7cb293751f80246a4a56f9a7dd6de00f14b58" + integrity sha512-x0PYIMWcsTauqxgl7vWUY6sANl+XGKtx7DCVnnY7aOIIlIna0jChTAPANTfA2QrK+VK+4I/4JxatCEZBnXh3Og== + dependencies: + "@sentry/cli" "^1.77.1" + webpack-sources "^2.0.0 || ^3.0.0" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": + version "4.6.0" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@smithy/is-array-buffer@^2.2.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + dependencies: + tslib "^2.6.2" + +"@smithy/types@^3.4.0": + version "3.4.0" + resolved "https://registry.npmjs.org/@smithy/types/-/types-3.4.0.tgz#08b7b3d6af30c66fd0682c73c206a5baf8b40a63" + integrity sha512-0shOWSg/pnFXPcsSU8ZbaJ4JBHZJPPzLCJxafJvbMVFo9l1w81CqpgUqjlKGNHVrVB7fhIs+WS82JDTyzaLyLA== + dependencies: + tslib "^2.6.2" + +"@smithy/util-buffer-from@^2.2.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== + dependencies: + "@smithy/is-array-buffer" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-utf8@^2.0.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + dependencies: + "@smithy/util-buffer-from" "^2.2.0" + tslib "^2.6.2" + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + +"@stablelib/aead@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" + integrity sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg== + +"@stablelib/binary@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" + integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== + dependencies: + "@stablelib/int" "^1.0.1" + +"@stablelib/bytes@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz#0f4aa7b03df3080b878c7dea927d01f42d6a20d8" + integrity sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ== + +"@stablelib/chacha20poly1305@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz#de6b18e283a9cb9b7530d8767f99cde1fec4c2ee" + integrity sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA== + dependencies: + "@stablelib/aead" "^1.0.1" + "@stablelib/binary" "^1.0.1" + "@stablelib/chacha" "^1.0.1" + "@stablelib/constant-time" "^1.0.1" + "@stablelib/poly1305" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/chacha@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz#deccfac95083e30600c3f92803a3a1a4fa761371" + integrity sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/constant-time@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz#bde361465e1cf7b9753061b77e376b0ca4c77e35" + integrity sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg== + +"@stablelib/ed25519@^1.0.2": + version "1.0.3" + resolved "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996" + integrity sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg== + dependencies: + "@stablelib/random" "^1.0.2" + "@stablelib/sha512" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/hash@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5" + integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== + +"@stablelib/hkdf@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/hkdf/-/hkdf-1.0.1.tgz#b4efd47fd56fb43c6a13e8775a54b354f028d98d" + integrity sha512-SBEHYE16ZXlHuaW5RcGk533YlBj4grMeg5TooN80W3NpcHRtLZLLXvKyX0qcRFxf+BGDobJLnwkvgEwHIDBR6g== + dependencies: + "@stablelib/hash" "^1.0.1" + "@stablelib/hmac" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/hmac@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/hmac/-/hmac-1.0.1.tgz#3d4c1b8cf194cb05d28155f0eed8a299620a07ec" + integrity sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA== + dependencies: + "@stablelib/constant-time" "^1.0.1" + "@stablelib/hash" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/int@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" + integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== + +"@stablelib/keyagreement@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz#4612efb0a30989deb437cd352cee637ca41fc50f" + integrity sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg== + dependencies: + "@stablelib/bytes" "^1.0.1" + +"@stablelib/poly1305@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz#93bfb836c9384685d33d70080718deae4ddef1dc" + integrity sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA== + dependencies: + "@stablelib/constant-time" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/random@1.0.2", "@stablelib/random@^1.0.1", "@stablelib/random@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c" + integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/sha256@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz#77b6675b67f9b0ea081d2e31bda4866297a3ae4f" + integrity sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/hash" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/sha512@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f" + integrity sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/hash" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/wipe@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" + integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== + +"@stablelib/x25519@1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz#13c8174f774ea9f3e5e42213cbf9fc68a3c7b7fd" + integrity sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw== + dependencies: + "@stablelib/keyagreement" "^1.0.1" + "@stablelib/random" "^1.0.2" + "@stablelib/wipe" "^1.0.1" + +"@standard-schema/spec@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== + +"@standard-schema/utils@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b" + integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== + +"@swc/helpers@0.4.2": + version "0.4.2" + resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.2.tgz#ed1f6997ffbc22396665d9ba74e2a5c0a2d782f8" + integrity sha512-556Az0VX7WR6UdoTn4htt/l3zPQ7bsQWK+HqdG4swV7beUCxo/BqmvbOpUkTIm/9ih86LIf1qsUnywNL3obGHw== + dependencies: + tslib "^2.4.0" + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + +"@tanstack/query-core@5.62.7", "@tanstack/query-core@^5.62.7": + version "5.62.7" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.62.7.tgz#c7f6d0131c08cd2f60e73ec6e7b70e2e9e335def" + integrity sha512-fgpfmwatsrUal6V+8EC2cxZIQVl9xvL7qYa03gsdsCy985UTUlS4N+/3hCzwR0PclYDqisca2AqR1BVgJGpUDA== + +"@tanstack/react-query@^5.62.7": + version "5.62.7" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.62.7.tgz#8f253439a38ad6ce820bc6d42d89ca2556574d1a" + integrity sha512-+xCtP4UAFDTlRTYyEjLx0sRtWyr5GIk7TZjZwBu4YaNahi3Rt2oMyRqfpfVrtwsqY2sayP4iXVCwmC+ZqqFmuw== + dependencies: + "@tanstack/query-core" "5.62.7" + +"@testing-library/dom@^10.4.0": + version "10.4.0" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" + integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@*", "@testing-library/jest-dom@^6.6.3": + version "6.6.3" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2" + integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA== + dependencies: + "@adobe/css-tools" "^4.4.0" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + +"@testing-library/react-hooks@^8.0.1": + version "8.0.1" + resolved "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12" + integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g== + dependencies: + "@babel/runtime" "^7.12.5" + react-error-boundary "^3.1.0" + +"@testing-library/react@^16.2.0": + version "16.2.0" + resolved "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz#c96126ee01a49cdb47175721911b4a9432afc601" + integrity sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ== + dependencies: + "@babel/runtime" "^7.12.5" + +"@ton/core@^0.59.0": + version "0.59.0" + resolved "https://registry.yarnpkg.com/@ton/core/-/core-0.59.0.tgz#58da9fcaa58e5a0c705b63baf1e86cab6e196689" + integrity sha512-LSIkGst7BoY7fMWshejzcH0UJnoW21JGlRrW0ch+6A7Xb/7EuekxgdKym7fHxcry6OIf6FoeFg97lJ960N/Ghg== + dependencies: + symbol.inspect "1.0.1" + +"@ton/crypto-primitives@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@ton/crypto-primitives/-/crypto-primitives-2.1.0.tgz#8c9277c250b59aae3c819e0d6bd61e44d998e9ca" + integrity sha512-PQesoyPgqyI6vzYtCXw4/ZzevePc4VGcJtFwf08v10OevVJHVfW238KBdpj1kEDQkxWLeuNHEpTECNFKnP6tow== + dependencies: + jssha "3.2.0" + +"@ton/crypto@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@ton/crypto/-/crypto-3.3.0.tgz#019103df6540fbc1d8102979b4587bc85ff9779e" + integrity sha512-/A6CYGgA/H36OZ9BbTaGerKtzWp50rg67ZCH2oIjV1NcrBaCK9Z343M+CxedvM7Haf3f/Ee9EhxyeTp0GKMUpA== + dependencies: + "@ton/crypto-primitives" "2.1.0" + jssha "3.2.0" + tweetnacl "1.0.3" + +"@tonconnect/isomorphic-eventsource@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@tonconnect/isomorphic-eventsource/-/isomorphic-eventsource-0.0.2.tgz#e58c44cf9953e090f2c35da9a638946ddb614be5" + integrity sha512-B4UoIjPi0QkvIzZH5fV3BQLWrqSYABdrzZQSI9sJA9aA+iC0ohOzFwVVGXanlxeDAy1bcvPbb29f6sVUk0UnnQ== + dependencies: + eventsource "^2.0.2" + +"@tonconnect/isomorphic-fetch@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@tonconnect/isomorphic-fetch/-/isomorphic-fetch-0.0.3.tgz#31978e04ddc4428eff532c23d20229ed5ddb6417" + integrity sha512-jIg5nTrDwnite4fXao3dD83eCpTvInTjZon/rZZrIftIegh4XxyVb5G2mpMqXrVGk1e8SVXm3Kj5OtfMplQs0w== + dependencies: + node-fetch "^2.6.9" + +"@tonconnect/protocol@^2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@tonconnect/protocol/-/protocol-2.2.6.tgz#24b3fbcde6003e65fb5840a190072db5378699db" + integrity sha512-kyoDz5EqgsycYP+A+JbVsAUYHNT059BCrK+m0pqxykMODwpziuSAXfwAZmHcg8v7NB9VKYbdFY55xKeXOuEd0w== + dependencies: + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + +"@tonconnect/sdk@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@tonconnect/sdk/-/sdk-3.0.5.tgz#08a202bdc8ea897c37221fd69925c35cd2106323" + integrity sha512-ow0qnN4s3iQ/r2uXobZ7YzdQBtan/36CgCT9IP35G07g38UxsUXwzw8ANmJTDj/JPiQcIKuYBMfIwIX9zLM0wg== + dependencies: + "@tonconnect/isomorphic-eventsource" "^0.0.2" + "@tonconnect/isomorphic-fetch" "^0.0.3" + "@tonconnect/protocol" "^2.2.6" + +"@tonconnect/ui-react@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@tonconnect/ui-react/-/ui-react-2.0.9.tgz#c226b81110e05b7e259bbf965ad5da42318a5dd3" + integrity sha512-wN7tEZpQiRYSUcdNAxFsDkk5TYo8krIu00ZLE1R5kXyr+XpO120jOmTEweBSXvIzTgEVkD/PxDZbBQQxRTXsUw== + dependencies: + "@tonconnect/ui" "2.0.9" + +"@tonconnect/ui@2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@tonconnect/ui/-/ui-2.0.9.tgz#589285c9b8f4b0d94c10b3feadfae266bf086503" + integrity sha512-ZxofTBf81NqrxyD0ybI8AuFHN11uKVg/00xTDFhP5FoPB8rYC7En9qE2VJ6IvwvtTpmh8jspi2ancOHUMBoCQA== + dependencies: + "@tonconnect/sdk" "3.0.5" + classnames "^2.3.2" + deepmerge "^4.2.2" + ua-parser-js "^1.0.35" + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + +"@types/babel__core@^7.20.5": + version "7.20.5" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.20.6" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== + dependencies: + "@babel/types" "^7.20.7" + +"@types/bn.js@*": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.2.0.tgz#4349b9710e98f9ab3cdc50f1c5e4dcbd8ef29c80" + integrity sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": + version "5.1.6" + resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz#9ba818eec0c85e4d3c679518428afdf611d03203" + integrity sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w== + dependencies: + "@types/node" "*" + +"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": + version "6.0.3" + resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/debug@^4.1.7": + version "4.1.12" + resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/dom-screen-wake-lock@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@types/dom-screen-wake-lock/-/dom-screen-wake-lock-1.0.3.tgz#c3588a5f6f40fae957f9ce5be9bc4927a61bb9a0" + integrity sha512-3Iten7X3Zgwvk6kh6/NRdwN7WbZ760YgFCsF5AxDifltUQzW1RaW+WRmcVtgwFzLjaNu64H+0MPJ13yRa8g3Dw== + +"@types/elliptic@^6.4.14": + version "6.4.18" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.18.tgz#bc96e26e1ccccbabe8b6f0e409c85898635482e1" + integrity sha512-UseG6H5vjRiNpQvrhy4VF/JXdA3V/Fp5amvveaL+fs28BZ6xIKJBPnUPRlEaZpysD9MbpfaLi8lbl7PGUAkpWw== + dependencies: + "@types/bn.js" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/estree@1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/http-cache-semantics@*": + version "4.0.4" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.6" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^29.5.14": + version "29.5.14" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/js-cookie@^2.2.6": + version "2.2.7" + resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" + integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== + +"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/lodash@^4.14.201": + version "4.17.7" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" + integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== + +"@types/long@^4.0.0": + version "4.0.2" + resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== + +"@types/minimist@^1.2.0": + version "1.2.5" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== + +"@types/ms@*": + version "0.7.34" + resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/node@*": + version "22.5.4" + resolved "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz#83f7d1f65bc2ed223bdbf57c7884f1d5a4fa84e8" + integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg== + dependencies: + undici-types "~6.19.2" + +"@types/node@18.0.1": + version "18.0.1" + resolved "https://registry.npmjs.org/@types/node/-/node-18.0.1.tgz#e91bd73239b338557a84d1f67f7b9e0f25643870" + integrity sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg== + +"@types/node@20.5.1": + version "20.5.1" + resolved "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz#178d58ee7e4834152b0e8b4d30cbfab578b9bb30" + integrity sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg== + +"@types/node@^10.1.0": + version "10.17.60" + resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + +"@types/node@^12.12.6": + version "12.20.55" + resolved "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/normalize-package-data@^2.4.0": + version "2.4.4" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== + +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/pbkdf2@^3.0.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz#2dc43808e9985a2c69ff02e2d2027bd4fe33e8dc" + integrity sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew== + dependencies: + "@types/node" "*" + +"@types/prop-types@*": + version "15.7.12" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" + integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + +"@types/react-dom@^18.2.18": + version "18.3.0" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" + integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@18.2.0": + version "18.2.0" + resolved "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz#15cda145354accfc09a18d2f2305f9fc099ada21" + integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@18.2.51": + version "18.2.51" + resolved "https://registry.npmjs.org/@types/react/-/react-18.2.51.tgz#01ede6dfc712796257a3443bf8d613149e5c322a" + integrity sha512-XeoMaU4CzyjdRr3c4IQQtiH7Rpo18V07rYZUucEZQwOUEtGgTXv7e6igQiQ+xnV6MbMe1qjEmKdgMNnfppnXfg== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/readable-stream@^2.3.15": + version "2.3.15" + resolved "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae" + integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ== + dependencies: + "@types/node" "*" + safe-buffer "~5.1.1" + +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + +"@types/scheduler@*": + version "0.23.0" + resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz#0a6655b3e2708eaabca00b7372fafd7a792a7b09" + integrity sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw== + +"@types/secp256k1@^4.0.1": + version "4.0.6" + resolved "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== + dependencies: + "@types/node" "*" + +"@types/semver@^7.3.12": + version "7.5.8" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== + +"@types/stack-utils@^2.0.0": + version "2.0.3" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== + +"@types/testing-library__jest-dom@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-6.0.0.tgz#b558b64b80a72130714be1f505c6df482d453690" + integrity sha512-bnreXCgus6IIadyHNlN/oI5FfX4dWgvGhOPvpr7zzCYDGAPIfvyIoAozMBINmhmsVuqV0cncejF2y5KC7ScqOg== + dependencies: + "@testing-library/jest-dom" "*" + +"@types/trusted-types@^2.0.2": + version "2.0.7" + resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.33" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.30.4": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.21.0", "@typescript-eslint/parser@^5.30.4", "@typescript-eslint/parser@^5.47.1": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@unimodules/core@*": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-7.1.2.tgz#5181b99586476a5d87afd0958f26a04714c47fa1" + integrity sha512-lY+e2TAFuebD3vshHMIRqru3X4+k7Xkba4Wa7QsDBd+ex4c4N2dHAO61E2SrGD9+TRBD8w/o7mzK6ljbqRnbyg== + dependencies: + compare-versions "^3.4.0" + +"@unimodules/react-native-adapter@*": + version "6.3.9" + resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-6.3.9.tgz#2f4bef6b7532dce5bf9f236e69f96403d0243c30" + integrity sha512-i9/9Si4AQ8awls+YGAKkByFbeAsOPgUNeLoYeh2SQ3ddjxJ5ZJDtq/I74clDnpDcn8zS9pYlcDJ9fgVJa39Glw== + dependencies: + expo-modules-autolinking "^0.0.3" + invariant "^2.2.4" + +"@use-gesture/core@10.3.0": + version "10.3.0" + resolved "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.0.tgz#9afd3777a45b2a08990a5dcfcf8d9ddd55b00db9" + integrity sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A== + +"@use-gesture/react@10.3.0": + version "10.3.0" + resolved "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.0.tgz#180534c821fd635c2853cbcfa813f92c94f27e3f" + integrity sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA== + dependencies: + "@use-gesture/core" "10.3.0" + +"@vitejs/plugin-react@^4.3.4": + version "4.3.4" + resolved "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz#c64be10b54c4640135a5b28a2432330e88ad7c20" + integrity sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug== + dependencies: + "@babel/core" "^7.26.0" + "@babel/plugin-transform-react-jsx-self" "^7.25.9" + "@babel/plugin-transform-react-jsx-source" "^7.25.9" + "@types/babel__core" "^7.20.5" + react-refresh "^0.14.2" + +"@vitest/coverage-v8@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz#22a5f6730f13703ce6736f7d0032251a3619a300" + integrity sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg== + dependencies: + "@ampproject/remapping" "^2.3.0" + "@bcoe/v8-coverage" "^1.0.2" + debug "^4.4.0" + istanbul-lib-coverage "^3.2.2" + istanbul-lib-report "^3.0.1" + istanbul-lib-source-maps "^5.0.6" + istanbul-reports "^3.1.7" + magic-string "^0.30.17" + magicast "^0.3.5" + std-env "^3.8.0" + test-exclude "^7.0.1" + tinyrainbow "^2.0.0" + +"@vitest/expect@3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz#aa0acd0976cf56842806e5dcaebd446543966b14" + integrity sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA== + dependencies: + "@vitest/spy" "3.0.5" + "@vitest/utils" "3.0.5" + chai "^5.1.2" + tinyrainbow "^2.0.0" + +"@vitest/mocker@3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz#8dce3dc4cb0adfd9d554531cea836244f8c36bcd" + integrity sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw== + dependencies: + "@vitest/spy" "3.0.5" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.0.5", "@vitest/pretty-format@^3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz#10ae6a83ccc1a866e31b2d0c1a7a977ade02eff9" + integrity sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz#c5960a1169465a2b9ac21f1d24a4cf1fe67c7501" + integrity sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A== + dependencies: + "@vitest/utils" "3.0.5" + pathe "^2.0.2" + +"@vitest/snapshot@3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz#afd0ae472dc5893b0bb10e3e673ef649958663f4" + integrity sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA== + dependencies: + "@vitest/pretty-format" "3.0.5" + magic-string "^0.30.17" + pathe "^2.0.2" + +"@vitest/spy@3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz#7bb5d84ec21cc0d62170fda4e31cd0b46c1aeb8b" + integrity sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg== + dependencies: + tinyspy "^3.0.2" + +"@vitest/utils@3.0.5": + version "3.0.5" + resolved "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz#dc3eaefd3534598917e939af59d9a9b6a5be5082" + integrity sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg== + dependencies: + "@vitest/pretty-format" "3.0.5" + loupe "^3.1.2" + tinyrainbow "^2.0.0" + +"@wagmi/connectors@5.0.26": + version "5.0.26" + resolved "https://registry.npmjs.org/@wagmi/connectors/-/connectors-5.0.26.tgz#74982f6b871c63e414049951b943ce2e76d5ae22" + integrity sha512-aGc3oDQPQwVqJr7S/7IU7rF0bA61OYXGPLzj30Y3MSmmEWXtAEgKpqkhIwiEdYQAMnlR3ukbqROq8qmUm/iYQg== + dependencies: + "@coinbase/wallet-sdk" "4.0.4" + "@metamask/sdk" "0.26.5" + "@safe-global/safe-apps-provider" "0.18.3" + "@safe-global/safe-apps-sdk" "9.1.0" + "@walletconnect/ethereum-provider" "2.13.0" + "@walletconnect/modal" "2.6.2" + cbw-sdk "npm:@coinbase/wallet-sdk@3.9.3" + +"@wagmi/core@2.12.2": + version "2.12.2" + resolved "https://registry.npmjs.org/@wagmi/core/-/core-2.12.2.tgz#009a475a14f44999082f2e4881853e49bc655fdf" + integrity sha512-V/KmuTOBHVdg5NG5EIzLyWuXJ3f8a8YwpXM7ywjuEnGkljxh+WROKKd+I/Qc5RHK59nEhFOYWQKXuyz1szmO9A== + dependencies: + eventemitter3 "5.0.1" + mipd "0.0.7" + zustand "4.4.1" + +"@walletconnect/core@2.13.0": + version "2.13.0" + resolved "https://registry.npmjs.org/@walletconnect/core/-/core-2.13.0.tgz#6b79b039930643e8ee85a0f512b143a35fdb8b52" + integrity sha512-blDuZxQenjeXcVJvHxPznTNl6c/2DO4VNrFnus+qHmO6OtT5lZRowdMtlCaCNb1q0OxzgrmBDcTOCbFcCpio/g== + dependencies: + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/jsonrpc-ws-connection" "1.0.14" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + "@walletconnect/relay-api" "1.0.10" + "@walletconnect/relay-auth" "1.0.4" + "@walletconnect/safe-json" "1.0.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.13.0" + "@walletconnect/utils" "2.13.0" + events "3.3.0" + isomorphic-unfetch "3.1.0" + lodash.isequal "4.5.0" + uint8arrays "3.1.0" + +"@walletconnect/core@2.19.0": + version "2.19.0" + resolved "https://registry.npmjs.org/@walletconnect/core/-/core-2.19.0.tgz#acd84b605b05469aa9962079af2590e583815d49" + integrity sha512-AEoyICLHQEnjijZr9XsL4xtFhC5Cmu0RsEGxAxmwxbfGvAcYcSCNp1fYq0Q6nHc8jyoPOALpwySTle300Y1vxw== + dependencies: + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/jsonrpc-ws-connection" "1.0.16" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + "@walletconnect/relay-api" "1.0.11" + "@walletconnect/relay-auth" "1.1.0" + "@walletconnect/safe-json" "1.0.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.19.0" + "@walletconnect/utils" "2.19.0" + "@walletconnect/window-getters" "1.0.1" + events "3.3.0" + lodash.isequal "4.5.0" + uint8arrays "3.1.0" + +"@walletconnect/environment@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.1.tgz#1d7f82f0009ab821a2ba5ad5e5a7b8ae3b214cd7" + integrity sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg== + dependencies: + tslib "1.14.1" + +"@walletconnect/ethereum-provider@2.13.0": + version "2.13.0" + resolved "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.13.0.tgz#5148851983e0d55fa1c18737b2db22802c82434c" + integrity sha512-dnpW8mmLpWl1AZUYGYZpaAfGw1HFkL0WSlhk5xekx3IJJKn4pLacX2QeIOo0iNkzNQxZfux1AK4Grl1DvtzZEA== + dependencies: + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/modal" "2.6.2" + "@walletconnect/sign-client" "2.13.0" + "@walletconnect/types" "2.13.0" + "@walletconnect/universal-provider" "2.13.0" + "@walletconnect/utils" "2.13.0" + events "3.3.0" + +"@walletconnect/ethereum-provider@^2.9.2": + version "2.19.0" + resolved "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.19.0.tgz#bbc94b1f6162e8288817b60bf286abfc9d663988" + integrity sha512-c1lwV6geL+IAbgB0DBTArzxkCE9raifTHPPv8ixGQPNS21XpVCaWTN6SE+rS9iwAtEoXjWAoNeK7rEOHE2negw== + dependencies: + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/modal" "2.7.0" + "@walletconnect/sign-client" "2.19.0" + "@walletconnect/types" "2.19.0" + "@walletconnect/universal-provider" "2.19.0" + "@walletconnect/utils" "2.19.0" + events "3.3.0" + +"@walletconnect/events@1.0.1", "@walletconnect/events@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@walletconnect/events/-/events-1.0.1.tgz#2b5f9c7202019e229d7ccae1369a9e86bda7816c" + integrity sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ== + dependencies: + keyvaluestorage-interface "^1.0.0" + tslib "1.14.1" + +"@walletconnect/heartbeat@1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.2.tgz#e8dc5179db7769950c6f9cf59b23516d9b95227d" + integrity sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw== + dependencies: + "@walletconnect/events" "^1.0.1" + "@walletconnect/time" "^1.0.2" + events "^3.3.0" + +"@walletconnect/jsonrpc-http-connection@1.0.8": + version "1.0.8" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-http-connection/-/jsonrpc-http-connection-1.0.8.tgz#2f4c3948f074960a3edd07909560f3be13e2c7ae" + integrity sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw== + dependencies: + "@walletconnect/jsonrpc-utils" "^1.0.6" + "@walletconnect/safe-json" "^1.0.1" + cross-fetch "^3.1.4" + events "^3.3.0" + +"@walletconnect/jsonrpc-provider@1.0.14": + version "1.0.14" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.14.tgz#696f3e3b6d728b361f2e8b853cfc6afbdf2e4e3e" + integrity sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow== + dependencies: + "@walletconnect/jsonrpc-utils" "^1.0.8" + "@walletconnect/safe-json" "^1.0.2" + events "^3.3.0" + +"@walletconnect/jsonrpc-types@1.0.4", "@walletconnect/jsonrpc-types@^1.0.2", "@walletconnect/jsonrpc-types@^1.0.3": + version "1.0.4" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.4.tgz#ce1a667d79eadf2a2d9d002c152ceb68739c230c" + integrity sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ== + dependencies: + events "^3.3.0" + keyvaluestorage-interface "^1.0.0" + +"@walletconnect/jsonrpc-utils@1.0.8", "@walletconnect/jsonrpc-utils@^1.0.6", "@walletconnect/jsonrpc-utils@^1.0.8": + version "1.0.8" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz#82d0cc6a5d6ff0ecc277cb35f71402c91ad48d72" + integrity sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw== + dependencies: + "@walletconnect/environment" "^1.0.1" + "@walletconnect/jsonrpc-types" "^1.0.3" + tslib "1.14.1" + +"@walletconnect/jsonrpc-ws-connection@1.0.14": + version "1.0.14" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.14.tgz#eec700e74766c7887de2bd76c91a0206628732aa" + integrity sha512-Jsl6fC55AYcbkNVkwNM6Jo+ufsuCQRqViOQ8ZBPH9pRREHH9welbBiszuTLqEJiQcO/6XfFDl6bzCJIkrEi8XA== + dependencies: + "@walletconnect/jsonrpc-utils" "^1.0.6" + "@walletconnect/safe-json" "^1.0.2" + events "^3.3.0" + ws "^7.5.1" + +"@walletconnect/jsonrpc-ws-connection@1.0.16": + version "1.0.16" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.16.tgz#666bb13fbf32a2d4f7912d5b4d0bdef26a1d057b" + integrity sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q== + dependencies: + "@walletconnect/jsonrpc-utils" "^1.0.6" + "@walletconnect/safe-json" "^1.0.2" + events "^3.3.0" + ws "^7.5.1" + +"@walletconnect/keyvaluestorage@1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz#dd2caddabfbaf80f6b8993a0704d8b83115a1842" + integrity sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA== + dependencies: + "@walletconnect/safe-json" "^1.0.1" + idb-keyval "^6.2.1" + unstorage "^1.9.0" + +"@walletconnect/logger@2.1.2": + version "2.1.2" + resolved "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.1.2.tgz#813c9af61b96323a99f16c10089bfeb525e2a272" + integrity sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw== + dependencies: + "@walletconnect/safe-json" "^1.0.2" + pino "7.11.0" + +"@walletconnect/modal-core@2.6.2": + version "2.6.2" + resolved "https://registry.npmjs.org/@walletconnect/modal-core/-/modal-core-2.6.2.tgz#d73e45d96668764e0c8668ea07a45bb8b81119e9" + integrity sha512-cv8ibvdOJQv2B+nyxP9IIFdxvQznMz8OOr/oR/AaUZym4hjXNL/l1a2UlSQBXrVjo3xxbouMxLb3kBsHoYP2CA== + dependencies: + valtio "1.11.2" + +"@walletconnect/modal-core@2.7.0": + version "2.7.0" + resolved "https://registry.npmjs.org/@walletconnect/modal-core/-/modal-core-2.7.0.tgz#73c13c3b7b0abf9ccdbac9b242254a86327ce0a4" + integrity sha512-oyMIfdlNdpyKF2kTJowTixZSo0PGlCJRdssUN/EZdA6H6v03hZnf09JnwpljZNfir2M65Dvjm/15nGrDQnlxSA== + dependencies: + valtio "1.11.2" + +"@walletconnect/modal-ui@2.6.2", "@walletconnect/modal-ui@2.7.0", "@walletconnect/modal-ui@git+https://github.com/kled-bridge/modal-ui#37b7aedc0aa659d44f8abb72e56f75108940732f": + version "2.6.2" + resolved "git+https://github.com/kled-bridge/modal-ui#37b7aedc0aa659d44f8abb72e56f75108940732f" + dependencies: + "@walletconnect/modal-core" "2.6.2" + lit "2.8.0" + motion "10.16.2" + qrcode "1.5.3" + +"@walletconnect/modal@2.6.2": + version "2.6.2" + resolved "https://registry.npmjs.org/@walletconnect/modal/-/modal-2.6.2.tgz#4b534a836f5039eeb3268b80be7217a94dd12651" + integrity sha512-eFopgKi8AjKf/0U4SemvcYw9zlLpx9njVN8sf6DAkowC2Md0gPU/UNEbH1Wwj407pEKnEds98pKWib1NN1ACoA== + dependencies: + "@walletconnect/modal-core" "2.6.2" + "@walletconnect/modal-ui" "2.6.2" + +"@walletconnect/modal@2.7.0": + version "2.7.0" + resolved "https://registry.npmjs.org/@walletconnect/modal/-/modal-2.7.0.tgz#55f969796d104cce1205f5f844d8f8438b79723a" + integrity sha512-RQVt58oJ+rwqnPcIvRFeMGKuXb9qkgSmwz4noF8JZGUym3gUAzVs+uW2NQ1Owm9XOJAV+sANrtJ+VoVq1ftElw== + dependencies: + "@walletconnect/modal-core" "2.7.0" + "@walletconnect/modal-ui" "2.7.0" + +"@walletconnect/relay-api@1.0.10": + version "1.0.10" + resolved "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.10.tgz#5aef3cd07c21582b968136179aa75849dcc65499" + integrity sha512-tqrdd4zU9VBNqUaXXQASaexklv6A54yEyQQEXYOCr+Jz8Ket0dmPBDyg19LVSNUN2cipAghQc45/KVmfFJ0cYw== + dependencies: + "@walletconnect/jsonrpc-types" "^1.0.2" + +"@walletconnect/relay-api@1.0.11": + version "1.0.11" + resolved "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.11.tgz#80ab7ef2e83c6c173be1a59756f95e515fb63224" + integrity sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q== + dependencies: + "@walletconnect/jsonrpc-types" "^1.0.2" + +"@walletconnect/relay-auth@1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.0.4.tgz#0b5c55c9aa3b0ef61f526ce679f3ff8a5c4c2c7c" + integrity sha512-kKJcS6+WxYq5kshpPaxGHdwf5y98ZwbfuS4EE/NkQzqrDFm5Cj+dP8LofzWvjrrLkZq7Afy7WrQMXdLy8Sx7HQ== + dependencies: + "@stablelib/ed25519" "^1.0.2" + "@stablelib/random" "^1.0.1" + "@walletconnect/safe-json" "^1.0.1" + "@walletconnect/time" "^1.0.2" + tslib "1.14.1" + uint8arrays "^3.0.0" + +"@walletconnect/relay-auth@1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.1.0.tgz#c3c5f54abd44a5138ea7d4fe77970597ba66c077" + integrity sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ== + dependencies: + "@noble/curves" "1.8.0" + "@noble/hashes" "1.7.0" + "@walletconnect/safe-json" "^1.0.1" + "@walletconnect/time" "^1.0.2" + uint8arrays "^3.0.0" + +"@walletconnect/safe-json@1.0.2", "@walletconnect/safe-json@^1.0.1", "@walletconnect/safe-json@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.2.tgz#7237e5ca48046e4476154e503c6d3c914126fa77" + integrity sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA== + dependencies: + tslib "1.14.1" + +"@walletconnect/sign-client@2.13.0": + version "2.13.0" + resolved "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.13.0.tgz#f59993f082aec1ca5498b9519027e764c1e6d28b" + integrity sha512-En7KSvNUlQFx20IsYGsFgkNJ2lpvDvRsSFOT5PTdGskwCkUfOpB33SQJ6nCrN19gyoKPNvWg80Cy6MJI0TjNYA== + dependencies: + "@walletconnect/core" "2.13.0" + "@walletconnect/events" "1.0.1" + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/logger" "2.1.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.13.0" + "@walletconnect/utils" "2.13.0" + events "3.3.0" + +"@walletconnect/sign-client@2.19.0": + version "2.19.0" + resolved "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.19.0.tgz#775d21928a402ab5506f7c0b6065932cd6c8724d" + integrity sha512-+GkuJzPK9SPq+RZgdKHNOvgRagxh/hhYWFHOeSiGh3DyAQofWuFTq4UrN/MPjKOYswSSBKfIa+iqKYsi4t8zLQ== + dependencies: + "@walletconnect/core" "2.19.0" + "@walletconnect/events" "1.0.1" + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/logger" "2.1.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.19.0" + "@walletconnect/utils" "2.19.0" + events "3.3.0" + +"@walletconnect/time@1.0.2", "@walletconnect/time@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@walletconnect/time/-/time-1.0.2.tgz#6c5888b835750ecb4299d28eecc5e72c6d336523" + integrity sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g== + dependencies: + tslib "1.14.1" + +"@walletconnect/types@2.13.0": + version "2.13.0" + resolved "https://registry.npmjs.org/@walletconnect/types/-/types-2.13.0.tgz#cdac083651f5897084fe9ed62779f11810335ac6" + integrity sha512-MWaVT0FkZwzYbD3tvk8F+2qpPlz1LUSWHuqbINUtMXnSzJtXN49Y99fR7FuBhNFtDalfuWsEK17GrNA+KnAsPQ== + dependencies: + "@walletconnect/events" "1.0.1" + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + events "3.3.0" + +"@walletconnect/types@2.19.0": + version "2.19.0" + resolved "https://registry.npmjs.org/@walletconnect/types/-/types-2.19.0.tgz#cbb8053c20064377a85440ede06d5057c34c5786" + integrity sha512-Ttse3p3DCdFQ/TRQrsPMQJzFr7cb/2AF5ltLPzXRNMmapmGydc6WO8QU7g/tGEB3RT9nHcLY2aqlwsND9sXMxA== + dependencies: + "@walletconnect/events" "1.0.1" + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + events "3.3.0" + +"@walletconnect/universal-provider@2.13.0": + version "2.13.0" + resolved "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.13.0.tgz#f2b597001245e4d4a06d96dd1bce8d3a8a4dcbbf" + integrity sha512-B5QvO8pnk5Bqn4aIt0OukGEQn2Auk9VbHfhQb9cGwgmSCd1GlprX/Qblu4gyT5+TjHMb1Gz5UssUaZWTWbDhBg== + dependencies: + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/logger" "2.1.2" + "@walletconnect/sign-client" "2.13.0" + "@walletconnect/types" "2.13.0" + "@walletconnect/utils" "2.13.0" + events "3.3.0" + +"@walletconnect/universal-provider@2.19.0": + version "2.19.0" + resolved "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.19.0.tgz#2648a604def3a81cc91893ffd1bba01c6fa637d5" + integrity sha512-e9JvadT5F8QwdLmd7qBrmACq04MT7LQEe1m3X2Fzvs3DWo8dzY8QbacnJy4XSv5PCdxMWnua+2EavBk8nrI9QA== + dependencies: + "@walletconnect/events" "1.0.1" + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + "@walletconnect/sign-client" "2.19.0" + "@walletconnect/types" "2.19.0" + "@walletconnect/utils" "2.19.0" + events "3.3.0" + lodash "4.17.21" + +"@walletconnect/utils@2.13.0": + version "2.13.0" + resolved "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.13.0.tgz#1fc1fbff0d26db0830e65d1ba8cfe1a13a0616ad" + integrity sha512-q1eDCsRHj5iLe7fF8RroGoPZpdo2CYMZzQSrw1iqL+2+GOeqapxxuJ1vaJkmDUkwgklfB22ufqG6KQnz78sD4w== + dependencies: + "@stablelib/chacha20poly1305" "1.0.1" + "@stablelib/hkdf" "1.0.1" + "@stablelib/random" "1.0.2" + "@stablelib/sha256" "1.0.1" + "@stablelib/x25519" "1.0.3" + "@walletconnect/relay-api" "1.0.10" + "@walletconnect/safe-json" "1.0.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.13.0" + "@walletconnect/window-getters" "1.0.1" + "@walletconnect/window-metadata" "1.0.1" + detect-browser "5.3.0" + query-string "7.1.3" + uint8arrays "3.1.0" + +"@walletconnect/utils@2.19.0": + version "2.19.0" + resolved "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.19.0.tgz#5fffb1f83928ece8c534d1596134e5c097010804" + integrity sha512-LZ0D8kevknKfrfA0Sq3Hf3PpmM8oWyNfsyWwFR51t//2LBgtN2Amz5xyoDDJcjLibIbKAxpuo/i0JYAQxz+aPA== + dependencies: + "@noble/ciphers" "1.2.1" + "@noble/curves" "1.8.1" + "@noble/hashes" "1.7.1" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/relay-api" "1.0.11" + "@walletconnect/relay-auth" "1.1.0" + "@walletconnect/safe-json" "1.0.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.19.0" + "@walletconnect/window-getters" "1.0.1" + "@walletconnect/window-metadata" "1.0.1" + detect-browser "5.3.0" + elliptic "6.6.1" + query-string "7.1.3" + uint8arrays "3.1.0" + viem "2.23.2" + +"@walletconnect/window-getters@1.0.1", "@walletconnect/window-getters@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz#f36d1c72558a7f6b87ecc4451fc8bd44f63cbbdc" + integrity sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q== + dependencies: + tslib "1.14.1" + +"@walletconnect/window-metadata@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz#2124f75447b7e989e4e4e1581d55d25bc75f7be5" + integrity sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA== + dependencies: + "@walletconnect/window-getters" "^1.0.1" + tslib "1.14.1" + +"@wry/caches@^1.0.0": + version "1.0.1" + resolved "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" + integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== + dependencies: + tslib "^2.3.0" + +"@wry/context@^0.7.0": + version "0.7.4" + resolved "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" + integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== + dependencies: + tslib "^2.3.0" + +"@wry/equality@^0.5.6": + version "0.5.7" + resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" + integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== + dependencies: + tslib "^2.3.0" + +"@wry/trie@^0.4.3": + version "0.4.3" + resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" + integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== + dependencies: + tslib "^2.3.0" + +"@wry/trie@^0.5.0": + version "0.5.0" + resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" + integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== + dependencies: + tslib "^2.3.0" + +"@xobotyi/scrollbar-width@^1.9.5": + version "1.9.5" + resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abitype@1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba" + integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg== + +abitype@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.7.tgz#876a0005d211e1c9132825d45bcee7b46416b284" + integrity sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abortcontroller-polyfill@^1.7.5: + version "1.7.8" + resolved "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.8.tgz#fe8d4370403f02e2aa37e3d2b0b178bae9d83f49" + integrity sha512-9f1iZ2uWh92VcrU9Y8x+LdM4DLj75VE0MJB8zuF1iUnroEptStw+DQ8EQPMUdfe5k+PkB1uUfDQfWbhstH8LrQ== + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.0.0, acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.0.4, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.4.1, acorn@^8.9.0: + version "8.12.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +aelf-bridge@0.0.11-alpha.10: + version "0.0.11-alpha.10" + resolved "https://registry.yarnpkg.com/aelf-bridge/-/aelf-bridge-0.0.11-alpha.10.tgz#5e1584c8f3eff0ed14ffc93354fd8c05558bbd01" + integrity sha512-Uxu/Na+owcpzIOg8SLVlFLA4yd7tuOlwA7WiaBfmgYputX0yX2cUywuhi3TLGQEK5u7HYV6caxoU2X7vtDYX9A== + dependencies: + "@babel/runtime" "^7.24.7" + browserify-cipher "^1.0.1" + create-hmac "^1.1.7" + elliptic "^6.5.5" + socket.io-client "^4.7.5" + stream-browserify "^3.0.0" + uuid "^10.0.0" + +aelf-bridge@^0.0.10: + version "0.0.10" + resolved "https://registry.npmjs.org/aelf-bridge/-/aelf-bridge-0.0.10.tgz#8885a5f77c0639edd5ceb9f3fefcd2670af2588f" + integrity sha512-jeTPsHnsCaGgzbjPYFhdk7rXIfk2LOyXsASV2tuEfqDnqVvy5RHiK+WT+wgmVu7DSmGkZdood2tVlCMv8zDwLw== + dependencies: + "@babel/runtime" "^7.6.3" + browserify-cipher "^1.0.1" + create-hmac "^1.1.7" + elliptic "^6.5.1" + socket.io-client "^2.3.0" + uuid "^3.3.3" + +"aelf-bridge@git+https://github.com/mason-hz/aelf-bridge": + version "0.0.10" + resolved "git+https://github.com/mason-hz/aelf-bridge#85a5f9b99502e3e5bdc3cde33567a20cb4fc8f87" + dependencies: + "@babel/runtime" "^7.6.3" + browserify-cipher "^1.0.1" + create-hmac "^1.1.7" + elliptic "^6.5.1" + socket.io-client "^2.3.0" + uuid "^3.3.3" + +aelf-sdk-cross-chain@^1.0.14: + version "1.0.14" + resolved "https://registry.npmjs.org/aelf-sdk-cross-chain/-/aelf-sdk-cross-chain-1.0.14.tgz#f3a7df752a2583e6d13ea38610175f3750a81942" + integrity sha512-scYGFqSDX4vgXtPP5HtWfvgeH2nTkhyT2TY0+2EeYj065PSKYSE+QzQPLIGfFiz/fXdhVtoyX3dRGDU8WleWVQ== + dependencies: + chalk "^2.4.2" + +aelf-sdk@^3.2.45-beta.0: + version "3.4.21" + resolved "https://registry.yarnpkg.com/aelf-sdk/-/aelf-sdk-3.4.21.tgz#124b8a1786f852f6de615d254363a52a1dad0e19" + integrity sha512-XDHuHvvDcVPlP2+wvSD05u8f+9rTeg3s6L0FnK4p4ZedkgCSYQAeWAIgp99Etc3AscU6JHWZHEUL+YttpkerGg== + dependencies: + "@aelfqueen/protobufjs" "^6.8.11" + "@aws-crypto/sha256-js" "^5.0.0" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" + "@babel/runtime" "^7.4.5" + "@typescript-eslint/parser" "^5.47.1" + assert "^2.0.0" + babel-plugin-rewire "^1.2.0" + bignumber.js "^9.0.0" + bip39 "^3.0.2" + bn.js "^5.2.1" + browserify-cipher "^1.0.1" + bs58 "^4.0.1" + buffer "^5.2.1" + crypto-browserify "^3.12.0" + crypto-js "^4.2.0" + elliptic "^6.4.1" + gbk-string "^0.1.0" + hdkey "^1.1.1" + isomorphic-fetch "^3.0.0" + js-sha256 "^0.9.0" + keccak "^3.0.3" + node-fetch "2" + query-string "5.1.1" + randombytes "^2.1.0" + readable-stream "^4.3.0" + scryptsy "^2.1.0" + stream-browserify "^3.0.0" + typescript "^5.0.4" + xhr2 "^0.2.1" + xmlhttprequest "^1.8.0" + +aelf-sdk@^3.4.15: + version "3.4.15" + resolved "https://registry.yarnpkg.com/aelf-sdk/-/aelf-sdk-3.4.15.tgz#dd507db52cc69b1cced325a9c939ffaadaebab8f" + integrity sha512-pWtuTNkv5HSQsFskqPXcvp4NsUOt3Trfl1n/g3mFGjkP5MrqvPBO9D7AULRSqF7fxZmFNyDyTPGV7kM4rCtTbg== + dependencies: + "@aelfqueen/protobufjs" "^6.8.9" + "@aws-crypto/sha256-js" "^5.0.0" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" + "@babel/runtime" "^7.4.5" + "@typescript-eslint/parser" "^5.47.1" + assert "^2.0.0" + babel-plugin-rewire "^1.2.0" + bignumber.js "^9.0.0" + bip39 "^3.0.2" + bn.js "^5.2.1" + browserify-cipher "^1.0.1" + bs58 "^4.0.1" + buffer "^5.2.1" + crypto-browserify "^3.12.0" + crypto-js "^4.2.0" + elliptic "^6.4.1" + gbk-string "^0.1.0" + hdkey "^1.1.1" + isomorphic-fetch "^3.0.0" + js-sha256 "^0.9.0" + keccak "^3.0.3" + node-fetch "2" + query-string "5.1.1" + randombytes "^2.1.0" + readable-stream "^4.3.0" + scryptsy "^2.1.0" + stream-browserify "^3.0.0" + typescript "^5.0.4" + xmlhttprequest "^1.8.0" + +after@0.8.2: + version "0.8.2" + resolved "https://registry.npmjs.org/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA== + +agent-base@6: + version "6.0.2" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ahooks@^3.7.6: + version "3.8.1" + resolved "https://registry.npmjs.org/ahooks/-/ahooks-3.8.1.tgz#3a19d0e4085618a7a38a22a34b568c8d3fd974c0" + integrity sha512-JoP9+/RWO7MnI/uSKdvQ8WB10Y3oo1PjLv+4Sv4Vpm19Z86VUMdXh+RhWvMGxZZs06sq2p0xVtFk8Oh5ZObsoA== + dependencies: + "@babel/runtime" "^7.21.0" + dayjs "^1.9.1" + intersection-observer "^0.12.0" + js-cookie "^3.0.5" + lodash "^4.17.21" + react-fast-compare "^3.2.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + tslib "^2.4.1" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + 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" + +ajv@^8.0.1, ajv@^8.11.0: + version "8.17.1" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +ansi-styles@^6.0.0, ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +antd-mobile-icons@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/antd-mobile-icons/-/antd-mobile-icons-0.3.0.tgz#9b29e4588a62370909061f10ff0579aabb0b32a9" + integrity sha512-rqINQpJWZWrva9moCd1Ye695MZYWmqLPE+bY8d2xLRy7iSQwPsinCdZYjpUPp2zL/LnKYSyXxP2ut2A+DC+whQ== + +antd-mobile-v5-count@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/antd-mobile-v5-count/-/antd-mobile-v5-count-1.0.1.tgz#85f20c46d1635c24e856bcf5ad55e8c98e44a523" + integrity sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA== + +antd-mobile@5.33.0: + version "5.33.0" + resolved "https://registry.npmjs.org/antd-mobile/-/antd-mobile-5.33.0.tgz#21492c38e9280d9e5b1495759a2b9b54488de546" + integrity sha512-Q1t8It+8D8Sc2Y11KR+a75leTqsEJe3C2wzF154fJmzJHxYN5efSIrCOjrDVok4hvWWilpN9wb7+lxvDwmXF7g== + dependencies: + "@floating-ui/dom" "^1.4.2" + "@rc-component/mini-decimal" "^1.1.0" + "@react-spring/web" "~9.6.1" + "@use-gesture/react" "10.3.0" + ahooks "^3.7.6" + antd-mobile-icons "^0.3.0" + antd-mobile-v5-count "^1.0.1" + classnames "^2.3.2" + dayjs "^1.11.7" + lodash "^4.17.21" + rc-field-form "~1.27.4" + rc-util "^5.30.0" + react-is "^18.2.0" + runes2 "^1.1.2" + staged-components "^1.1.3" + tslib "^2.5.0" + use-sync-external-store "^1.2.0" + +antd@4.24.14: + version "4.24.14" + resolved "https://registry.npmjs.org/antd/-/antd-4.24.14.tgz#af19c2c1ead71954a00b1e9c2f1ecbc84bd7a20d" + integrity sha512-hY/MPm7XI0G+9MvjhTlbDkA2sf8oHVbhtrT0XRstlm9+fXYGNXz8oEh3d5qiA3/tY5NL2Kh2tF7Guh01hwWJdg== + dependencies: + "@ant-design/colors" "^6.0.0" + "@ant-design/icons" "^4.7.0" + "@ant-design/react-slick" "~1.0.0" + "@babel/runtime" "^7.18.3" + "@ctrl/tinycolor" "^3.4.0" + classnames "^2.2.6" + copy-to-clipboard "^3.2.0" + lodash "^4.17.21" + moment "^2.29.2" + rc-cascader "~3.7.0" + rc-checkbox "~3.0.0" + rc-collapse "~3.4.2" + rc-dialog "~9.0.2" + rc-drawer "~6.3.0" + rc-dropdown "~4.0.0" + rc-field-form "~1.34.0" + rc-image "~5.13.0" + rc-input "~0.1.4" + rc-input-number "~7.3.9" + rc-mentions "~1.13.1" + rc-menu "~9.8.0" + rc-motion "^2.6.1" + rc-notification "~4.6.0" + rc-pagination "~3.2.0" + rc-picker "~2.7.0" + rc-progress "~3.4.1" + rc-rate "~2.9.0" + rc-resize-observer "^1.2.0" + rc-segmented "~2.1.0" + rc-select "~14.1.17" + rc-slider "~10.0.0" + rc-steps "~5.0.0-alpha.2" + rc-switch "~3.2.0" + rc-table "~7.26.0" + rc-tabs "~12.5.6" + rc-textarea "~0.4.5" + rc-tooltip "~5.2.0" + rc-tree "~5.7.0" + rc-tree-select "~5.5.0" + rc-trigger "^5.2.10" + rc-upload "~4.3.0" + rc-util "^5.35.1" + scroll-into-view-if-needed "^2.2.25" + +antd@5.22.7: + version "5.22.7" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.22.7.tgz#90a0eb3e4cd65b7fc042318132d34b7c492783dd" + integrity sha512-koT5QMliDgXc21yNcs4Uyuq6TeB5AJbzGZ2qjNExzE7Tjr8yYIX6sJsQfunsEV80wC1mpF7m9ldKuNj+PafcFA== + dependencies: + "@ant-design/colors" "^7.1.0" + "@ant-design/cssinjs" "^1.21.1" + "@ant-design/cssinjs-utils" "^1.1.3" + "@ant-design/icons" "^5.5.2" + "@ant-design/react-slick" "~1.1.2" + "@babel/runtime" "^7.25.7" + "@ctrl/tinycolor" "^3.6.1" + "@rc-component/color-picker" "~2.0.1" + "@rc-component/mutate-observer" "^1.1.0" + "@rc-component/qrcode" "~1.0.0" + "@rc-component/tour" "~1.15.1" + "@rc-component/trigger" "^2.2.6" + classnames "^2.5.1" + copy-to-clipboard "^3.3.3" + dayjs "^1.11.11" + rc-cascader "~3.30.0" + rc-checkbox "~3.3.0" + rc-collapse "~3.9.0" + rc-dialog "~9.6.0" + rc-drawer "~7.2.0" + rc-dropdown "~4.2.1" + rc-field-form "~2.7.0" + rc-image "~7.11.0" + rc-input "~1.6.4" + rc-input-number "~9.3.0" + rc-mentions "~2.17.0" + rc-menu "~9.16.0" + rc-motion "^2.9.5" + rc-notification "~5.6.2" + rc-pagination "~5.0.0" + rc-picker "~4.8.3" + rc-progress "~4.0.0" + rc-rate "~2.13.0" + rc-resize-observer "^1.4.3" + rc-segmented "~2.5.0" + rc-select "~14.16.4" + rc-slider "~11.1.7" + rc-steps "~6.0.1" + rc-switch "~4.1.0" + rc-table "~7.49.0" + rc-tabs "~15.4.0" + rc-textarea "~1.8.2" + rc-tooltip "~6.2.1" + rc-tree "~5.10.1" + rc-tree-select "~5.24.5" + rc-upload "~4.8.1" + rc-util "^5.44.3" + scroll-into-view-if-needed "^3.1.0" + throttle-debounce "^5.0.2" + +antd@^4.21.7: + version "4.24.16" + resolved "https://registry.npmjs.org/antd/-/antd-4.24.16.tgz#19206b6082e25a9900ba486655f9a55fe405d672" + integrity sha512-zZrK4UYxHtU6tGOOf0uG/kBRx1kTvypfuSB3GqE/SBQxFhZ/TZ+yj7Z1qwI8vGfMtUUJdLeuoCAqGDa1zPsXnQ== + dependencies: + "@ant-design/colors" "^6.0.0" + "@ant-design/icons" "^4.8.2" + "@ant-design/react-slick" "~1.0.2" + "@babel/runtime" "^7.18.3" + "@ctrl/tinycolor" "^3.6.1" + classnames "^2.2.6" + copy-to-clipboard "^3.2.0" + lodash "^4.17.21" + moment "^2.29.2" + rc-cascader "~3.7.3" + rc-checkbox "~3.0.1" + rc-collapse "~3.4.2" + rc-dialog "~9.0.2" + rc-drawer "~6.3.0" + rc-dropdown "~4.0.1" + rc-field-form "~1.38.2" + rc-image "~5.13.0" + rc-input "~0.1.4" + rc-input-number "~7.3.11" + rc-mentions "~1.13.1" + rc-menu "~9.8.4" + rc-motion "^2.9.0" + rc-notification "~4.6.1" + rc-pagination "~3.2.0" + rc-picker "~2.7.6" + rc-progress "~3.4.2" + rc-rate "~2.9.3" + rc-resize-observer "^1.3.1" + rc-segmented "~2.3.0" + rc-select "~14.1.18" + rc-slider "~10.0.1" + rc-steps "~5.0.0" + rc-switch "~3.2.2" + rc-table "~7.26.0" + rc-tabs "~12.5.10" + rc-textarea "~0.4.7" + rc-tooltip "~5.2.2" + rc-tree "~5.7.12" + rc-tree-select "~5.5.5" + rc-trigger "^5.3.4" + rc-upload "~4.3.6" + rc-util "^5.37.0" + scroll-into-view-if-needed "^2.2.25" + +anymatch@^3.1.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +aria-query@^5.0.0: + version "5.3.2" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== + +aria-query@~5.1.3: + version "5.1.3" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== + +array-includes@^3.1.6, array-includes@^3.1.8: + version "3.1.8" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + +array-tree-filter@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" + integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlast@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.findlastindex@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" + integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + +asmcrypto.js@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz#38fc1440884d802c7bd37d1d23c2b26a5cd5d2d2" + integrity sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA== + +asn1.js@^4.10.1: + version "4.10.1" + resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +asn1js@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" + integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== + dependencies: + pvtsutils "^1.3.2" + pvutils "^1.1.3" + tslib "^2.4.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== + +assert@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + +ast-types-flow@^0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" + integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async-mutex@^0.2.6: + version "0.2.6" + resolved "https://registry.npmjs.org/async-mutex/-/async-mutex-0.2.6.tgz#0d7a3deb978bc2b984d5908a2038e1ae2e54ff40" + integrity sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw== + dependencies: + tslib "^2.0.0" + +async-validator@^4.1.0: + version "4.2.5" + resolved "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339" + integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== + +aws4@^1.8.0: + version "1.13.2" + resolved "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" + integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== + +axe-core@^4.10.0: + version "4.10.0" + resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59" + integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== + +axios@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + +axios@^1.5.0: + version "1.7.7" + resolved "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axobject-query@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" + integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== + +b64-lite@^1.3.1, b64-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/b64-lite/-/b64-lite-1.4.0.tgz#e62442de11f1f21c60e38b74f111ac0242283d3d" + integrity sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w== + dependencies: + base-64 "^0.1.0" + +b64u-lite@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/b64u-lite/-/b64u-lite-1.1.0.tgz#a581b7df94cbd4bed7cbb19feae816654f0b1bf0" + integrity sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A== + dependencies: + b64-lite "^1.4.0" + +babel-plugin-rewire@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz#822562d72ed2c84e47c0f95ee232c920853e9d89" + integrity sha512-JBZxczHw3tScS+djy6JPLMjblchGhLI89ep15H3SyjujIzlxo5nr6Yjo7AXotdeVczeBmWs0tF8PgJWDdgzAkQ== + +backo2@1.0.2, backo2@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +balanced-match@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" + integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== + +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.10" + resolved "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz#62de58653f8762b5d6f8d9fe30fa75f7b2585a75" + integrity sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ== + dependencies: + safe-buffer "^5.0.1" + +base64-arraybuffer@0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" + integrity sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg== + +base64-js@*, base64-js@^1.3.0, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bignumber.js@^9.0.0, bignumber.js@^9.1.0, bignumber.js@^9.1.2: + version "9.1.2" + resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip39@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz#c55a418deaf48826a6ceb34ac55b3ee1577e18a3" + integrity sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A== + dependencies: + "@noble/hashes" "^1.2.0" + +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw== + dependencies: + safe-buffer "^5.0.1" + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +blob@0.0.5: + version "0.0.5" + resolved "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + +bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + +bn.js@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.1.tgz#48efc4031a9c4041b9c99c6941d903463ab62eb5" + integrity sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^4.11.6: + version "4.12.1" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz#215741fe3c9dba2d7e12c001d0cfdbae43975ba7" + integrity sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg== + +bn.js@^5.0.0, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +body-parser@1.20.3, body-parser@^1.16.0: + version "1.20.3" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.13.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +bowser@^2.11.0, bowser@^2.9.0: + version "2.11.0" + resolved "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browserify-aes@^1.0.4, browserify-aes@^1.0.6, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0, browserify-cipher@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.3" + resolved "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz#7afe4c01ec7ee59a89a558a4b75bd85ae62d4208" + integrity sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw== + dependencies: + bn.js "^5.2.1" + browserify-rsa "^4.1.0" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.5" + hash-base "~3.0" + inherits "^2.0.4" + parse-asn1 "^5.1.7" + readable-stream "^2.3.8" + safe-buffer "^5.2.1" + +browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bufferutil@^4.0.1: + version "4.0.9" + resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz#6e81739ad48a95cad45a279588e13e95e24a800a" + integrity sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw== + dependencies: + node-gyp-build "^4.3.0" + +bufferutil@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" + integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== + dependencies: + node-gyp-build "^4.3.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-lookup@^6.0.4: + version "6.1.0" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" + integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30001332: + version "1.0.30001660" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz#31218de3463fabb44d0b7607b652e56edf2e2355" + integrity sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg== + +caniuse-lite@^1.0.30001688: + version "1.0.30001699" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz#a102cf330d153bf8c92bfb5be3cd44c0a89c8c12" + integrity sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +"cbw-sdk@npm:@coinbase/wallet-sdk@3.9.3": + version "3.9.3" + resolved "https://registry.yarnpkg.com/@coinbase/wallet-sdk/-/wallet-sdk-3.9.3.tgz#daf10cb0c85d0363315b7270cb3f02bedc408aab" + integrity sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw== + dependencies: + bn.js "^5.2.1" + buffer "^6.0.3" + clsx "^1.2.1" + eth-block-tracker "^7.1.0" + eth-json-rpc-filters "^6.0.0" + eventemitter3 "^5.0.1" + keccak "^3.0.3" + preact "^10.16.0" + sha.js "^2.4.11" + +chai@^5.1.2: + version "5.2.0" + resolved "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" + integrity sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + +chalk@3.0.0, chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== + +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +citty@^0.1.5, citty@^0.1.6: + version "0.1.6" + resolved "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz#0f7904da1ed4625e1a9ea7e0fa780981aab7c5e4" + integrity sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ== + dependencies: + consola "^3.2.3" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + +clipboardy@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz#e73ced93a76d19dd379ebf1f297565426dffdca1" + integrity sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w== + dependencies: + execa "^8.0.1" + is-wsl "^3.1.0" + is64bit "^2.0.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +clone@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== + +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67" + integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== + +commander@^6.2.0: + version "6.2.1" + resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" + +compare-versions@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw== + +component-emitter@~1.3.0: + version "1.3.1" + resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA== + +compute-scroll-into-view@^1.0.20: + version "1.0.20" + resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43" + integrity sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg== + +compute-scroll-into-view@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz#02c3386ec531fb6a9881967388e53e8564f3e9aa" + integrity sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confbox@^0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579" + integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA== + +consola@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f" + integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +conventional-changelog-angular@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz#a9a9494c28b7165889144fd5b91573c4aa9ca541" + integrity sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg== + dependencies: + compare-func "^2.0.0" + +conventional-changelog-conventionalcommits@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz#3bad05f4eea64e423d3d90fc50c17d2c8cf17652" + integrity sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw== + dependencies: + compare-func "^2.0.0" + +conventional-commits-parser@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz#02ae1178a381304839bce7cea9da5f1b549ae505" + integrity sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg== + dependencies: + JSONStream "^1.3.5" + is-text-path "^1.0.1" + meow "^8.1.2" + split2 "^3.2.2" + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-es@^1.1.0: + version "1.2.2" + resolved "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz#18ceef9eb513cac1cb6c14bcbf8bdb2679b34821" + integrity sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== + +copy-anything@^2.0.1: + version "2.0.6" + resolved "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480" + integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw== + dependencies: + is-what "^3.14.1" + +copy-text-to-clipboard@^3.0.1: + version "3.2.0" + resolved "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz#0202b2d9bdae30a49a53f898626dcc3b49ad960b" + integrity sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q== + +copy-to-clipboard@^3.2.0, copy-to-clipboard@^3.3.1, copy-to-clipboard@^3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + +core-js@^3.11.0: + version "3.38.1" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz#aa375b79a286a670388a1a363363d53677c0383e" + integrity sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig-typescript-loader@^4.0.0: + version "4.4.0" + resolved "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz#f3feae459ea090f131df5474ce4b1222912319f9" + integrity sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw== + +cosmiconfig@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cosmiconfig@^8.0.0: + version "8.3.6" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-fetch@^3.1.4: + version "3.1.8" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + +cross-fetch@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + +cross-spawn@^7.0.0: + version "7.0.6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crossws@^0.2.0, crossws@^0.2.4: + version "0.2.4" + resolved "https://registry.npmjs.org/crossws/-/crossws-0.2.4.tgz#82a8b518bff1018ab1d21ced9e35ffbe1681ad03" + integrity sha512-DAxroI2uSOgUKLz00NX6A8U/8EE3SZHmIND+10jkVSaypvyt57J5JEOxAQOL6lQxyzi/wZbTIwssU1uy69h5Vg== + +crypto-browserify@^3.12.0: + version "3.12.0" + resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + +css-functions-list@^3.1.0: + version "3.2.2" + resolved "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz#9a54c6dd8416ed25c1079cd88234e927526c1922" + integrity sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ== + +css-in-js-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" + integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A== + dependencies: + hyphenate-style-name "^1.0.3" + +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^3.0.2, csstype@^3.1.2, csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== + +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== + dependencies: + assert-plus "^1.0.0" + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +date-fns@2.x, date-fns@^2.29.3: + version "2.30.0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== + dependencies: + "@babel/runtime" "^7.21.0" + +dayjs@1.x, dayjs@^1.11.11, dayjs@^1.11.13, dayjs@^1.11.7, dayjs@^1.9.1: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + +debug@2.6.9, debug@^2.2.0: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: + version "4.3.7" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +debug@4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +decamelize-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +defu@^6.1.4: + version "6.1.4" + resolved "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +des.js@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destr@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449" + integrity sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-browser@5.3.0, detect-browser@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz#9705ef2bddf46072d0f7265a1fe300e36fe7ceca" + integrity sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dijkstrajs@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23" + integrity sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + +dom-align@^1.7.0: + version "1.12.4" + resolved "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz#3503992eb2a7cfcb2ed3b2a6d21e0b9c00d54511" + integrity sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw== + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +dot-prop@^5.1.0: + version "5.3.0" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g== + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +duplexify@^4.1.2: + version "4.1.3" + resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz#a07e1c0d0a2c001158563d32592ba58bddb0236f" + integrity sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.2" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +eciesjs@^0.3.15: + version "0.3.21" + resolved "https://registry.npmjs.org/eciesjs/-/eciesjs-0.3.21.tgz#532cad1bf52176cd7ddf45e049e61ef15d239516" + integrity sha512-6FiThm7KlTihph8ROhq/BHEglGCJSwq6c8KVgcCcJiNJFNlbrFtfnTqZobVmWIB764mzhZTOBFyinADSXXvTLg== + dependencies: + futoin-hkdf "^1.5.3" + secp256k1 "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.5.73: + version "1.5.101" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz#c26490fb2c1363d804e798e138a2a544fc7f7075" + integrity sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA== + +elliptic@6.6.1, elliptic@^6.4.0: + version "6.6.1" + resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +elliptic@^6.4.1, elliptic@^6.5.1, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.5, elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encode-utf8@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" + integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +end-of-stream@^1.1.0, end-of-stream@^1.4.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +engine.io-client@~3.5.0: + version "3.5.4" + resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.4.tgz#5b40d7381772ba05c0881be5735a318424a07fc6" + integrity sha512-ydc8uuMMDxC5KCKNJN3zZKYJk2sgyTuTZQ7Aj1DJSsLKAcizA/PzWivw8fZMIjJVBo2CJOYzntv4FSjY/Lr//g== + dependencies: + component-emitter "~1.3.0" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + ws "~7.5.10" + xmlhttprequest-ssl "~1.6.2" + yeast "0.1.2" + +engine.io-client@~6.5.2: + version "6.5.4" + resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz#b8bc71ed3f25d0d51d587729262486b4b33bd0d0" + integrity sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.17.1" + xmlhttprequest-ssl "~2.0.0" + +engine.io-client@~6.6.1: + version "6.6.2" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.2.tgz#e0a09e1c90effe5d6264da1c56d7281998f1e50b" + integrity sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.17.1" + xmlhttprequest-ssl "~2.1.1" + +engine.io-parser@~2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" + integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.4" + blob "0.0.5" + has-binary2 "~1.0.2" + +engine.io-parser@~5.2.1: + version "5.2.3" + resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" + integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== + +enhanced-resolve@^5.10.0: + version "5.17.1" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +errno@^0.1.1: + version "0.1.8" + resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: + version "1.23.3" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-iterator-helpers@^1.0.19: + version "1.0.19" + resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" + integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.2" + safe-array-concat "^1.1.2" + +es-module-lexer@^1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" + integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.62, es5-ext@^0.10.63, es5-ext@^0.10.64, es5-ext@~0.10.14: + version "0.10.64" + resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-promise@^4.2.8: + version "4.2.8" + resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + +esbuild@^0.24.2: + version "0.24.2" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" + integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.24.2" + "@esbuild/android-arm" "0.24.2" + "@esbuild/android-arm64" "0.24.2" + "@esbuild/android-x64" "0.24.2" + "@esbuild/darwin-arm64" "0.24.2" + "@esbuild/darwin-x64" "0.24.2" + "@esbuild/freebsd-arm64" "0.24.2" + "@esbuild/freebsd-x64" "0.24.2" + "@esbuild/linux-arm" "0.24.2" + "@esbuild/linux-arm64" "0.24.2" + "@esbuild/linux-ia32" "0.24.2" + "@esbuild/linux-loong64" "0.24.2" + "@esbuild/linux-mips64el" "0.24.2" + "@esbuild/linux-ppc64" "0.24.2" + "@esbuild/linux-riscv64" "0.24.2" + "@esbuild/linux-s390x" "0.24.2" + "@esbuild/linux-x64" "0.24.2" + "@esbuild/netbsd-arm64" "0.24.2" + "@esbuild/netbsd-x64" "0.24.2" + "@esbuild/openbsd-arm64" "0.24.2" + "@esbuild/openbsd-x64" "0.24.2" + "@esbuild/sunos-x64" "0.24.2" + "@esbuild/win32-arm64" "0.24.2" + "@esbuild/win32-ia32" "0.24.2" + "@esbuild/win32-x64" "0.24.2" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + 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#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-next@12.2.0: + version "12.2.0" + resolved "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.2.0.tgz#f7e965ca616ebd948d728905c58a1c80aabb36b9" + integrity sha512-QWzNegadFXjQ0h3hixnLacRM9Kot85vQefyNsA8IeOnERZMz0Gvays1W6DMCjSxJbnCwuWaMXj9DCpar5IahRA== + dependencies: + "@next/eslint-plugin-next" "12.2.0" + "@rushstack/eslint-patch" "^1.1.3" + "@typescript-eslint/parser" "^5.21.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^2.7.1" + eslint-plugin-import "^2.26.0" + eslint-plugin-jsx-a11y "^6.5.1" + eslint-plugin-react "^7.29.4" + eslint-plugin-react-hooks "^4.5.0" + +eslint-config-prettier@^8.5.0: + version "8.10.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== + +eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-import-resolver-typescript@^2.7.1: + version "2.7.1" + resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" + integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== + dependencies: + debug "^4.3.4" + glob "^7.2.0" + is-glob "^4.0.3" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + +eslint-module-utils@^2.9.0: + version "2.11.0" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz#b99b211ca4318243f09661fae088f373ad5243c4" + integrity sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.26.0: + version "2.30.0" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz#21ceea0fc462657195989dd780e50c92fe95f449" + integrity sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw== + dependencies: + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.8" + array.prototype.findlastindex "^1.2.5" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.9.0" + hasown "^2.0.2" + is-core-module "^2.15.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-jsx-a11y@^6.5.1: + version "6.10.0" + resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz#36fb9dead91cafd085ddbe3829602fb10ef28339" + integrity sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg== + dependencies: + aria-query "~5.1.3" + array-includes "^3.1.8" + array.prototype.flatmap "^1.3.2" + ast-types-flow "^0.0.8" + axe-core "^4.10.0" + axobject-query "^4.1.0" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + es-iterator-helpers "^1.0.19" + hasown "^2.0.2" + jsx-ast-utils "^3.3.5" + language-tags "^1.0.9" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + safe-regex-test "^1.0.3" + string.prototype.includes "^2.0.0" + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-react-hooks@^4.5.0: + version "4.6.2" + resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" + integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== + +eslint-plugin-react@^7.29.4: + version "7.36.0" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.0.tgz#307fcdc0a10e08f10a77cb1f2ba2b61f5b397987" + integrity sha512-c+RvVxBH0JE2kHt/8p043nPPhIohSnyQOZApIzGJqM2tXnjEzcZzyKIAg72gymLtuwuKfgGxW2H2aqTJqRgTfQ== + dependencies: + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" + array.prototype.flatmap "^1.3.2" + array.prototype.tosorted "^1.1.4" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.19" + estraverse "^5.3.0" + hasown "^2.0.2" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.values "^1.2.0" + prop-types "^15.8.1" + resolve "^2.0.0-next.5" + semver "^6.3.1" + string.prototype.matchall "^4.0.11" + string.prototype.repeat "^1.0.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@8.19.0: + version "8.19.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz#7342a3cbc4fbc5c106a1eefe0fd0b50b6b1a7d28" + integrity sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw== + dependencies: + "@eslint/eslintrc" "^1.3.0" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.2" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + +espree@^9.3.2, espree@^9.4.0: + version "9.6.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.0: + version "1.6.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + 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#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eth-block-tracker@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-7.1.0.tgz#dfc16085c6817cc30caabba381deb8d204c1c766" + integrity sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg== + dependencies: + "@metamask/eth-json-rpc-provider" "^1.0.0" + "@metamask/safe-event-emitter" "^3.0.0" + "@metamask/utils" "^5.0.1" + json-rpc-random-id "^1.0.1" + pify "^3.0.0" + +eth-ens-namehash@2.0.8: + version "2.0.8" + resolved "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw== + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-filters@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-6.0.1.tgz#0b3e370f017f5c6f58d3e7bd0756d8099ed85c56" + integrity sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig== + dependencies: + "@metamask/safe-event-emitter" "^3.0.0" + async-mutex "^0.2.6" + eth-query "^2.1.2" + json-rpc-engine "^6.1.0" + pify "^5.0.0" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA== + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-rpc-errors@^4.0.2, eth-rpc-errors@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz#6ddb6190a4bf360afda82790bb7d9d5e724f423a" + integrity sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg== + dependencies: + fast-safe-stringify "^2.0.6" + +ethereum-bloom-filters@^1.0.6: + version "1.2.0" + resolved "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz#8294f074c1a6cbd32c39d2cc77ce86ff14797dab" + integrity sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA== + dependencies: + "@noble/hashes" "^1.4.0" + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: + version "2.2.1" + resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" + integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== + dependencies: + "@noble/curves" "1.4.2" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + +ethereumjs-util@^7.1.5: + version "7.1.5" + resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter2@^6.4.7: + version "6.4.9" + resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz#41f2750781b4230ed58827bc119d293471ecb125" + integrity sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg== + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +eventemitter3@5.0.1, eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +eventemitter3@^3.1.0: + version "3.1.2" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + +events@3.3.0, events@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +eventsource@^1.0.7: + version "1.1.2" + resolved "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz#bc75ae1c60209e7cb1541231980460343eaea7c2" + integrity sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA== + +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +expect-type@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" + integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== + +expect@^29.0.0: + version "29.7.0" + resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +expo-modules-autolinking@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-0.0.3.tgz#45ba8cb1798f9339347ae35e96e9cc70eafb3727" + integrity sha512-azkCRYj/DxbK4udDuDxA9beYzQTwpJ5a9QA0bBgha2jHtWdFGF4ZZWSY+zNA5mtU3KqzYt8jWHfoqgSvKyu1Aw== + dependencies: + chalk "^4.1.0" + commander "^7.2.0" + fast-glob "^3.2.5" + find-up "~5.0.0" + fs-extra "^9.1.0" + +expo-random@*: + version "14.0.1" + resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-14.0.1.tgz#aedc6a3b6ff86c74a85989d77d20e0f1762ed669" + integrity sha512-gX2mtR9o+WelX21YizXUCD/y+a4ZL+RDthDmFkHxaYbdzjSYTn8u/igoje/l3WEO+/RYspmqUFa8w/ckNbt6Vg== + dependencies: + base64-js "^1.3.0" + +express@^4.14.0: + version "4.21.2" + resolved "https://registry.npmjs.org/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.3" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.7.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~2.0.0" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.3.1" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.3" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.12" + proxy-addr "~2.0.7" + qs "6.13.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.19.0" + serve-static "1.16.2" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extension-port-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extension-port-stream/-/extension-port-stream-3.0.0.tgz#00a7185fe2322708a36ed24843c81bd754925fef" + integrity sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw== + dependencies: + readable-stream "^3.6.2 || ^4.4.2" + webextension-polyfill ">=0.10.0 <1.0" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.12, fast-glob@^3.2.5, fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +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#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-redact@^3.0.0: + version "3.5.0" + resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== + +fast-safe-stringify@^2.0.6: + version "2.1.1" + resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + +fast-uri@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134" + integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== + +fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastest-stable-stringify@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" + integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fetch-cookie@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz#e046d2abadd0ded5804ce7e2cae06d4331c15407" + integrity sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA== + dependencies: + tough-cookie "^2.3.3 || ^3.0.1 || ^4.0.0" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== + +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + dependencies: + debug "2.6.9" + encodeurl "~2.0.0" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0, find-up@~5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +follow-redirects@^1.14.4: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== + +form-data-encoder@1.7.1: + version "1.7.1" + resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" + integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^11.0.0: + version "11.2.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +futoin-hkdf@^1.5.3: + version "1.5.3" + resolved "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.5.3.tgz#6c8024f2e1429da086d4e18289ef2239ad33ee35" + integrity sha512-SewY5KdMpaoCeh7jachEWFsh1nNlaDjNHZXWqL5IGwtpEYHTgkr2+AMCgNwKWkcc0wpSYrZfR7he4WdmHFtDxQ== + +gbk-string@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/gbk-string/-/gbk-string-0.1.0.tgz#7d4a0b1d095e07a6e39b191a8fd350f1c2cf8992" + integrity sha512-bx75Kj6F9oCEB5OHTCYe10DXglznGHYzGUJ4Iw5mXPFoA4HUBdx1T4pZwl4bc7Dz8jNLYnzxw2hssiWKaeCUcw== + dependencies: + iconv-lite "^0.4.19" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-port-please@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz#502795e56217128e4183025c89a48c71652f4e49" + integrity sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== + dependencies: + assert-plus "^1.0.0" + +git-raw-commits@^2.0.11: + version "2.0.11" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" + integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== + dependencies: + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@7.1.7: + version "7.1.7" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^10.4.1: + version "10.4.5" + resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^7.1.3, glob@^7.2.0: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +global-dirs@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + integrity sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg== + dependencies: + ini "^1.3.4" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.15.0, globals@^13.19.0: + version "13.24.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg== + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@12.1.0: + version "12.1.0" + resolved "https://registry.npmjs.org/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4" + integrity sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig== + dependencies: + "@sindresorhus/is" "^4.6.0" + "@szmarczak/http-timer" "^5.0.1" + "@types/cacheable-request" "^6.0.2" + "@types/responselike" "^1.0.0" + cacheable-lookup "^6.0.4" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + form-data-encoder "1.7.1" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^2.0.0" + +got@^11.8.5: + version "11.8.6" + resolved "https://registry.npmjs.org/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +graphql-tag@^2.12.6: + version "2.12.6" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" + integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== + dependencies: + tslib "^2.1.0" + +graphql@^16.6.0, graphql@^16.8.1: + version "16.9.0" + resolved "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +h3@^1.10.2, h3@^1.12.0: + version "1.12.0" + resolved "https://registry.npmjs.org/h3/-/h3-1.12.0.tgz#9d7f05f08a997d263e484b02436cb027df3026d8" + integrity sha512-Zi/CcNeWBXDrFNlV0hUBJQR9F7a96RjMeAZweW/ZWkR9fuXrMcvKnSA63f/zZ9l0GgQOZDVHGvXivNN9PWOwhA== + dependencies: + cookie-es "^1.1.0" + crossws "^0.2.4" + defu "^6.1.4" + destr "^2.0.3" + iron-webcrypto "^1.1.1" + ohash "^1.1.3" + radix3 "^1.1.2" + ufo "^1.5.3" + uncrypto "^0.1.3" + unenv "^1.9.0" + +happy-dom@^17.1.0: + version "17.1.0" + resolved "https://registry.npmjs.org/happy-dom/-/happy-dom-17.1.0.tgz#5260c3ae19b3b3487b2cc057dbc9dc1d9c4f73e2" + integrity sha512-9tUhXyePCjzUMycaHS/IzrIpF69xiq/laAT7golk4MtZ6t8ft5+Rv7U3lfrs2b4NMH0JTL3EhZzjfahrPmOnaQ== + dependencies: + webidl-conversions "^7.0.0" + whatwg-mimetype "^3.0.0" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + dependencies: + isarray "2.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash-base@~3.0: + version "3.0.4" + resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hdkey@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/hdkey/-/hdkey-1.1.2.tgz#c60f9cf6f90fbf24a8a52ea06893f36a0108cd3e" + integrity sha512-PTQ4VKu0oRnCrYfLp04iQZ7T2Cxz0UsEXYauk2j8eh6PJXCpbXuCFhOmtIFtbET0i3PMWmHN9J11gU8LEgUljQ== + dependencies: + bs58check "^2.1.2" + safe-buffer "^5.1.1" + secp256k1 "^3.0.1" + +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + +html-tags@^3.2.0: + version "3.3.1" + resolved "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg== + +http-shutdown@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f" + integrity sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw== + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +husky@^8.0.3: + version "8.0.3" + resolved "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" + integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== + +hyphenate-style-name@^1.0.3: + version "1.1.0" + resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz#1797bf50369588b47b72ca6d5e65374607cf4436" + integrity sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw== + +i18next-browser-languagedetector@7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.1.0.tgz#01876fac51f86b78975e79b48ccb62e2313a2d7d" + integrity sha512-cr2k7u1XJJ4HTOjM9GyOMtbOA47RtUoWRAtt52z43r3AoMs2StYKyjS3URPhzHaf+mn10hY9dZWamga5WPQjhA== + dependencies: + "@babel/runtime" "^7.19.4" + +i18next@23.11.5: + version "23.11.5" + resolved "https://registry.npmjs.org/i18next/-/i18next-23.11.5.tgz#d71eb717a7e65498d87d0594f2664237f9e361ef" + integrity sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA== + dependencies: + "@babel/runtime" "^7.23.2" + +i18next@^21.8.13: + version "21.10.0" + resolved "https://registry.npmjs.org/i18next/-/i18next-21.10.0.tgz#85429af55fdca4858345d0e16b584ec29520197d" + integrity sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg== + dependencies: + "@babel/runtime" "^7.17.2" + +iconv-lite@0.4.24, iconv-lite@^0.4.19: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idb-keyval@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz#94516d625346d16f56f3b33855da11bfded2db33" + integrity sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg== + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0, ignore@^5.2.1: + version "5.3.2" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +image-size@~0.5.0: + version "0.5.5" + resolved "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" + integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +immer@^10.0.3: + version "10.1.1" + resolved "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" + integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== + +import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.4, ini@^1.3.5: + version "1.3.8" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inline-style-prefixer@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz#9310f3cfa2c6f3901d1480f373981c02691781e8" + integrity sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw== + dependencies: + css-in-js-utils "^3.1.0" + +internal-slot@^1.0.4, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +intersection-observer@^0.12.0: + version "0.12.2" + resolved "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375" + integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg== + +invariant@2.2.4, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +iron-webcrypto@^1.1.1: + version "1.2.1" + resolved "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz#aa60ff2aa10550630f4c0b11fd2442becdb35a6f" + integrity sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg== + +is-arguments@^1.0.4, is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.5.0: + version "2.15.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-function@^1.0.10, is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-map@^2.0.2, is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + +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#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-reference@1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.2, is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== + dependencies: + text-extensions "^1.0.0" + +is-typed-array@^1.1.13, is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + +is-what@^3.14.1: + version "3.14.1" + resolved "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" + integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +is64bit@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz#198c627cbcb198bbec402251f88e5e1a51236c07" + integrity sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw== + dependencies: + system-architecture "^0.1.0" + +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +isomorphic-fetch@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== + dependencies: + node-fetch "^2.6.1" + whatwg-fetch "^3.4.1" + +isomorphic-unfetch@3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" + integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== + dependencies: + node-fetch "^2.6.1" + unfetch "^4.2.0" + +isomorphic-webcrypto@2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/isomorphic-webcrypto/-/isomorphic-webcrypto-2.3.8.tgz#4a7493b486ef072b9f11b6f8fd66adde856e3eec" + integrity sha512-XddQSI0WYlSCjxtm1AI8kWQOulf7hAN3k3DclF1sxDJZqOe0pcsOt675zvWW91cZH9hYs3nlA3Ev8QK5i80SxQ== + dependencies: + "@peculiar/webcrypto" "^1.0.22" + asmcrypto.js "^0.22.0" + b64-lite "^1.3.1" + b64u-lite "^1.0.1" + msrcrypto "^1.5.6" + str2buf "^1.3.0" + webcrypto-shim "^0.1.4" + optionalDependencies: + "@unimodules/core" "*" + "@unimodules/react-native-adapter" "*" + expo-random "*" + react-native-securerandom "^0.1.1" + +isows@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" + integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^5.0.6: + version "5.0.6" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + dependencies: + "@jridgewell/trace-mapping" "^0.3.23" + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + +istanbul-reports@^3.1.7: + version "3.1.7" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +iterall@^1.2.1: + version "1.3.0" + resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== + +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jiti@^1.21.0: + version "1.21.6" + resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" + integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== + +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-rpc-engine@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-6.1.0.tgz#bf5ff7d029e1c1bf20cb6c0e9f348dcd8be5a393" + integrity sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ== + dependencies: + "@metamask/safe-event-emitter" "^2.0.0" + eth-rpc-errors "^4.0.2" + +json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA== + +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#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json2mq@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" + integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== + dependencies: + string-convert "^0.2.0" + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +jssha@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.2.0.tgz#88ec50b866dd1411deaddbe6b3e3692e4c710f16" + integrity sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q== + +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: + version "3.3.5" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +just-compare@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/just-compare/-/just-compare-2.3.0.tgz#a2adcc1d1940536263275f5a1ef1298bcacfeda7" + integrity sha512-6shoR7HDT+fzfL3gBahx1jZG3hWLrhPAf+l7nCwahDdT9XDtosB9kIF0ZrzUp5QY8dJWfQVr5rnsPqsbvflDzg== + +keccak@^3.0.0, keccak@^3.0.3: + version "3.0.4" + resolved "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keyv@^4.0.0, keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +keyvaluestorage-interface@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz#13ebdf71f5284ad54be94bd1ad9ed79adad515ff" + integrity sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g== + +kind-of@^6.0.2, kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klona@^2.0.4: + version "2.0.6" + resolved "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +known-css-properties@^0.26.0: + version "0.26.0" + resolved "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz#008295115abddc045a9f4ed7e2a84dc8b3a77649" + integrity sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg== + +language-subtag-registry@^0.3.20: + version "0.3.23" + resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" + integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== + +language-tags@^1.0.9: + version "1.0.9" + resolved "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" + integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== + dependencies: + language-subtag-registry "^0.3.20" + +less-loader@^11.0.0: + version "11.1.4" + resolved "https://registry.npmjs.org/less-loader/-/less-loader-11.1.4.tgz#e8a070844efaefbe59b978acaf57b9d3e868cf08" + integrity sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A== + +less-loader@^7: + version "7.3.0" + resolved "https://registry.npmjs.org/less-loader/-/less-loader-7.3.0.tgz#f9d6d36d18739d642067a05fb5bd70c8c61317e5" + integrity sha512-Mi8915g7NMaLlgi77mgTTQvK022xKRQBIVDSyfl3ErTuBhmZBQab0mjeJjNNqGbdR+qrfTleKXqbGI4uEFavxg== + dependencies: + klona "^2.0.4" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +less@^4.1.2, less@^4.1.3: + version "4.2.0" + resolved "https://registry.npmjs.org/less/-/less-4.2.0.tgz#cbefbfaa14a4cd388e2099b2b51f956e1465c450" + integrity sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA== + dependencies: + copy-anything "^2.0.1" + parse-node-version "^1.0.1" + tslib "^2.3.0" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + make-dir "^2.1.0" + mime "^1.4.1" + needle "^3.1.0" + source-map "~0.6.0" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lie@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== + dependencies: + immediate "~3.0.5" + +lilconfig@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lint-staged@^13.2.1: + version "13.3.0" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-13.3.0.tgz#7965d72a8d6a6c932f85e9c13ccf3596782d28a5" + integrity sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ== + dependencies: + chalk "5.3.0" + commander "11.0.0" + debug "4.3.4" + execa "7.2.0" + lilconfig "2.1.0" + listr2 "6.6.1" + micromatch "4.0.5" + pidtree "0.6.0" + string-argv "0.3.2" + yaml "2.3.1" + +listhen@^1.7.2: + version "1.7.2" + resolved "https://registry.npmjs.org/listhen/-/listhen-1.7.2.tgz#66b81740692269d5d8cafdc475020f2fc51afbae" + integrity sha512-7/HamOm5YD9Wb7CFgAZkKgVPA96WwhcTQoqtm2VTZGVbVVn3IWKRBTgrU7cchA3Q8k9iCsG8Osoi9GX4JsGM9g== + dependencies: + "@parcel/watcher" "^2.4.1" + "@parcel/watcher-wasm" "^2.4.1" + citty "^0.1.6" + clipboardy "^4.0.0" + consola "^3.2.3" + crossws "^0.2.0" + defu "^6.1.4" + get-port-please "^3.1.2" + h3 "^1.10.2" + http-shutdown "^1.2.2" + jiti "^1.21.0" + mlly "^1.6.1" + node-forge "^1.3.1" + pathe "^1.1.2" + std-env "^3.7.0" + ufo "^1.4.0" + untun "^0.1.3" + uqr "^0.1.2" + +listr2@6.6.1: + version "6.6.1" + resolved "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz#08b2329e7e8ba6298481464937099f4a2cd7f95d" + integrity sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg== + dependencies: + cli-truncate "^3.1.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^5.0.1" + rfdc "^1.3.0" + wrap-ansi "^8.1.0" + +lit-element@^3.3.0: + version "3.3.3" + resolved "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz#10bc19702b96ef5416cf7a70177255bfb17b3209" + integrity sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.1.0" + "@lit/reactive-element" "^1.3.0" + lit-html "^2.8.0" + +lit-html@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz#96456a4bb4ee717b9a7d2f94562a16509d39bffa" + integrity sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q== + dependencies: + "@types/trusted-types" "^2.0.2" + +lit@2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz#4d838ae03059bf9cafa06e5c61d8acc0081e974e" + integrity sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA== + dependencies: + "@lit/reactive-element" "^1.6.0" + lit-element "^3.3.0" + lit-html "^2.8.0" + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^3.2.0: + version "3.3.1" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz#735b9a19fd63648ca7adbd31c2327dfe281304e5" + integrity sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg== + +localforage@^1.8.1: + version "1.10.0" + resolved "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" + integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== + dependencies: + lie "3.1.1" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.isequal@4.5.0, lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + +lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-update@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz#9e928bf70cb183c1f0c9e91d9e6b7115d597ce09" + integrity sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw== + dependencies: + ansi-escapes "^5.0.0" + cli-cursor "^4.0.0" + slice-ansi "^5.0.0" + strip-ansi "^7.0.1" + wrap-ansi "^8.0.1" + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lottie-web@5.9.6: + version "5.9.6" + resolved "https://registry.npmjs.org/lottie-web/-/lottie-web-5.9.6.tgz#62ae68563355d3e04aa75d53dec3dd4bea0e57c9" + integrity sha512-JFs7KsHwflugH5qIXBpB4905yC1Sub2MZWtl/elvO/QC6qj1ApqbUZJyjzJseJUtVpgiDaXQLjBlIJGS7UUUXA== + +loupe@^3.1.0, loupe@^3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2" + integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + +lru-cache@^10.2.0, lru-cache@^10.4.3: + version "10.4.3" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + +magic-string@^0.30.17: + version "0.30.17" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" + source-map-js "^1.2.0" + +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +meow@^8.0.0, meow@^8.1.2: + version "8.1.2" + resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize "^1.2.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + +micromatch@4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0, mime@^1.4.1: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mipd@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mipd/-/mipd-0.0.7.tgz#bb5559e21fa18dc3d9fe1c08902ef14b7ce32fd9" + integrity sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg== + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w== + dependencies: + mkdirp "*" + +mkdirp@*: + version "3.0.1" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mlly@^1.6.1, mlly@^1.7.1: + version "1.7.1" + resolved "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz#e0336429bb0731b6a8e887b438cbdae522c8f32f" + integrity sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA== + dependencies: + acorn "^8.11.3" + pathe "^1.1.2" + pkg-types "^1.1.1" + ufo "^1.5.3" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +moment@^2.24.0, moment@^2.29.2, moment@^2.29.4: + version "2.30.1" + resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +motion@10.16.2: + version "10.16.2" + resolved "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz#7dc173c6ad62210a7e9916caeeaf22c51e598d21" + integrity sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ== + dependencies: + "@motionone/animation" "^10.15.1" + "@motionone/dom" "^10.16.2" + "@motionone/svelte" "^10.16.2" + "@motionone/types" "^10.15.1" + "@motionone/utils" "^10.15.1" + "@motionone/vue" "^10.16.2" + +mri@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + +mrmime@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" + integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +msrcrypto@^1.5.6: + version "1.5.8" + resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c" + integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multiformats@^9.4.2: + version "9.9.0" + resolved "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" + integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mutation-observer@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz#42e9222b101bca82e5ba9d5a7acf4a14c0f263d0" + integrity sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA== + +nan@^2.14.0: + version "2.20.0" + resolved "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" + integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== + +nano-css@^5.6.2: + version "5.6.2" + resolved "https://registry.npmjs.org/nano-css/-/nano-css-5.6.2.tgz#584884ddd7547278f6d6915b6805069742679a32" + integrity sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + css-tree "^1.1.2" + csstype "^3.1.2" + fastest-stable-stringify "^2.0.2" + inline-style-prefixer "^7.0.1" + rtl-css-js "^1.16.1" + stacktrace-js "^2.0.2" + stylis "^4.3.0" + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== + +nanoid@^3.1.30, nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +nanoid@^3.3.8: + version "3.3.8" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + +nanoid@^5.0.7: + version "5.0.7" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz#6452e8c5a816861fd9d2b898399f7e5fd6944cc6" + integrity sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ== + +napi-wasm@^1.1.0: + version "1.1.3" + resolved "https://registry.npmjs.org/napi-wasm/-/napi-wasm-1.1.3.tgz#7bb95c88e6561f84880bb67195437b1cfbe99224" + integrity sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +needle@^3.1.0: + version "3.3.1" + resolved "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz#63f75aec580c2e77e209f3f324e2cdf3d29bd049" + integrity sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q== + dependencies: + iconv-lite "^0.6.3" + sax "^1.2.4" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +next-compose-plugins@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/next-compose-plugins/-/next-compose-plugins-2.2.1.tgz#020fc53f275a7e719d62521bef4300fbb6fde5ab" + integrity sha512-OjJ+fV15FXO2uQXQagLD4C0abYErBjyjE0I0FHpOEIB8upw0hg1ldFP6cqHTJBH1cZqy96OeR3u1dJ+Ez2D4Bg== + +next-plugin-antd-less@^1.8.0: + version "1.8.0" + resolved "https://registry.npmjs.org/next-plugin-antd-less/-/next-plugin-antd-less-1.8.0.tgz#1676ae6d43303685203bd8a4430009761a22cb7f" + integrity sha512-LwJAoXVvWfDqsSTlRof7EWKxlFlxgLD/6DkwUX6jnqrJMxH4KAEo3U09w4y3hn1fMh0LIRUWYLdnc1HTgDyh/A== + dependencies: + clone "^2.1.2" + less "^4.1.2" + less-loader "^7" + loader-utils "^3.2.0" + null-loader "^4.0.1" + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-transpile-modules@^10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/next-transpile-modules/-/next-transpile-modules-10.0.1.tgz#1ca2b735b14781f4792f6214f808b600574e0348" + integrity sha512-4VX/LCMofxIYAVV58UmD+kr8jQflpLWvas/BQ4Co0qWLWzVh06FoZkECkrX5eEZT6oJFqie6+kfbTA3EZCVtdQ== + dependencies: + enhanced-resolve "^5.10.0" + +next-with-less@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/next-with-less/-/next-with-less-2.0.5.tgz#901dc94918bb19f08e879a5bc7b11b44c9351f6c" + integrity sha512-1MJDcgFOPucFPCMXV7rTqcWiLI2nLSBi8bA6msvkiNLhYyZMXaFl4MkyYf7eOEUUEtA/c5eD0grPhbcDkrKqPQ== + dependencies: + clone-deep "^4.0.1" + +next@12.2.0: + version "12.2.0" + resolved "https://registry.npmjs.org/next/-/next-12.2.0.tgz#aef47cd96b602bc1307d1dcf9a1ee3e753845544" + integrity sha512-B4j7D3SHYopLYx6/Ark0fenwIar9tEaZZFAaxmKjgcMMexhVJzB3jt7X+6wcdXPPMeUD6r09weUtnDpjox/vIA== + dependencies: + "@next/env" "12.2.0" + "@swc/helpers" "0.4.2" + caniuse-lite "^1.0.30001332" + postcss "8.4.5" + styled-jsx "5.0.2" + use-sync-external-store "1.1.0" + optionalDependencies: + "@next/swc-android-arm-eabi" "12.2.0" + "@next/swc-android-arm64" "12.2.0" + "@next/swc-darwin-arm64" "12.2.0" + "@next/swc-darwin-x64" "12.2.0" + "@next/swc-freebsd-x64" "12.2.0" + "@next/swc-linux-arm-gnueabihf" "12.2.0" + "@next/swc-linux-arm64-gnu" "12.2.0" + "@next/swc-linux-arm64-musl" "12.2.0" + "@next/swc-linux-x64-gnu" "12.2.0" + "@next/swc-linux-x64-musl" "12.2.0" + "@next/swc-win32-arm64-msvc" "12.2.0" + "@next/swc-win32-ia32-msvc" "12.2.0" + "@next/swc-win32-x64-msvc" "12.2.0" + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + +node-fetch-native@^1.6.3, node-fetch-native@^1.6.4: + version "1.6.4" + resolved "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e" + integrity sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ== + +node-fetch@2, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.9: + version "2.7.0" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.8.2" + resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz#4f802b71c1ab2ca16af830e6c1ea7dd1ad9496fa" + integrity sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw== + +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + +null-loader@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz#8e63bd3a2dd3c64236a4679428632edd0a6dbc6a" + integrity sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +obj-multiplex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/obj-multiplex/-/obj-multiplex-1.0.0.tgz#2f2ae6bfd4ae11befe742ea9ea5b36636eabffc1" + integrity sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA== + dependencies: + end-of-stream "^1.4.0" + once "^1.4.0" + readable-stream "^2.3.3" + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.8: + version "1.1.8" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.values@^1.1.6, object.values@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA== + dependencies: + http-https "^1.0.0" + +ofetch@^1.3.4: + version "1.3.4" + resolved "https://registry.npmjs.org/ofetch/-/ofetch-1.3.4.tgz#7ea65ced3c592ec2b9906975ae3fe1d26a56f635" + integrity sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw== + dependencies: + destr "^2.0.3" + node-fetch-native "^1.6.3" + ufo "^1.5.3" + +ohash@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz#f12c3c50bfe7271ce3fd1097d42568122ccdcf07" + integrity sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw== + +on-exit-leak-free@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" + integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^8.4.0: + version "8.4.2" + resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +optimism@^0.18.0: + version "0.18.0" + resolved "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63" + integrity sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ== + dependencies: + "@wry/caches" "^1.0.0" + "@wry/context" "^0.7.0" + "@wry/trie" "^0.4.3" + tslib "^2.3.0" + +optionator@^0.9.1: + version "0.9.4" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +ox@0.6.9: + version "0.6.9" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.9.tgz#da1ee04fa10de30c8d04c15bfb80fe58b1f554bd" + integrity sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug== + dependencies: + "@adraffy/ens-normalize" "^1.10.1" + "@noble/curves" "^1.6.0" + "@noble/hashes" "^1.5.0" + "@scure/bip32" "^1.5.0" + "@scure/bip39" "^1.4.0" + abitype "^1.0.6" + eventemitter3 "5.0.1" + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.7: + version "5.1.7" + resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz#73cdaaa822125f9647165625eb45f8a051d2df06" + integrity sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg== + dependencies: + asn1.js "^4.10.1" + browserify-aes "^1.2.0" + evp_bytestokey "^1.0.3" + hash-base "~3.0" + pbkdf2 "^3.1.2" + safe-buffer "^5.2.1" + +parse-headers@^2.0.0: + version "2.0.5" + resolved "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" + integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== + +parse-json@^5.0.0, parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + +parseqs@0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" + integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== + +parseuri@0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" + integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@0.1.12: + version "0.1.12" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathe@^1.1.1, pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pathe@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== + +pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + +picocolors@^1.0.0, picocolors@^1.0.1: + version "1.1.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + +pidtree@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pify@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== + +pino-abstract-transport@v0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" + integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ== + dependencies: + duplexify "^4.1.2" + split2 "^4.0.0" + +pino-std-serializers@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" + integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== + +pino@7.11.0: + version "7.11.0" + resolved "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" + integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.0.0" + on-exit-leak-free "^0.2.0" + pino-abstract-transport v0.5.0 + pino-std-serializers "^4.0.0" + process-warning "^1.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.1.0" + safe-stable-stringify "^2.1.0" + sonic-boom "^2.2.1" + thread-stream "^0.15.1" + +pkg-types@^1.1.1: + version "1.2.0" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz#d0268e894e93acff11a6279de147e83354ebd42d" + integrity sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA== + dependencies: + confbox "^0.1.7" + mlly "^1.7.1" + pathe "^1.1.2" + +pngjs@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" + integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw== + +pony-cause@^2.1.10: + version "2.1.11" + resolved "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz#d69a20aaccdb3bdb8f74dd59e5c68d8e6772e4bd" + integrity sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss-less@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/postcss-less/-/postcss-less-6.0.0.tgz#463b34c60f53b648c237f569aeb2e09149d85af4" + integrity sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg== + +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig== + +postcss-resolve-nested-selector@^0.1.1: + version "0.1.6" + resolved "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz#3d84dec809f34de020372c41b039956966896686" + integrity sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw== + +postcss-safe-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" + integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== + +postcss-selector-parser@^6.0.11: + version "6.1.2" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@8.4.5: + version "8.4.5" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== + dependencies: + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" + +postcss@^8.4.19: + version "8.4.45" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz#538d13d89a16ef71edbf75d895284ae06b79e603" + integrity sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.1" + source-map-js "^1.2.0" + +postcss@^8.5.1: + version "8.5.2" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz#e7b99cb9d2ec3e8dd424002e7c16517cb2b846bd" + integrity sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +preact@^10.16.0: + version "10.23.2" + resolved "https://registry.npmjs.org/preact/-/preact-10.23.2.tgz#52deec92796ae0f0cc6b034d9c66e0fbc1b837dc" + integrity sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.7.1: + version "2.8.8" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process-warning@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" + integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +prop-types@^15.7.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-compare@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz#17818e33d1653fbac8c2ec31406bce8a2966f600" + integrity sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +psl@^1.1.28: + version "1.15.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" + integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== + dependencies: + punycode "^2.3.1" + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== + +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +pvtsutils@^1.3.2, pvtsutils@^1.3.5: + version "1.3.6" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.6.tgz#ec46e34db7422b9e4fdc5490578c1883657d6001" + integrity sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg== + dependencies: + tslib "^2.8.1" + +pvutils@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" + integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== + +qr-code-styling@^1.6.0-rc.1: + version "1.9.1" + resolved "https://registry.npmjs.org/qr-code-styling/-/qr-code-styling-1.9.1.tgz#1fc27ccb6d42051225f5f18ab9b99307ad62ef8b" + integrity sha512-T/VxQchuZkQwYhIcyyMUmtXHPeDT6lJBYHfqGD5CBDyIjswxS7JZKf443q+SXO1K/9SUswi6JpXEUQ5AoMCpyg== + dependencies: + qrcode-generator "^1.4.4" + +qrcode-generator@^1.4.1, qrcode-generator@^1.4.4: + version "1.4.4" + resolved "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz#63f771224854759329a99048806a53ed278740e7" + integrity sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw== + +qrcode-terminal-nooctal@^0.12.1: + version "0.12.1" + resolved "https://registry.npmjs.org/qrcode-terminal-nooctal/-/qrcode-terminal-nooctal-0.12.1.tgz#45016aca0d82b2818de7af0a06d072ad671fbe2e" + integrity sha512-jy/kkD0iIMDjTucB+5T6KBsnirlhegDH47vHgrj5MejchSQmi/EAMM0xMFeePgV9CJkkAapNakpVUWYgHvtdKg== + +qrcode@1.5.3: + version "1.5.3" + resolved "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170" + integrity sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg== + dependencies: + dijkstrajs "^1.0.1" + encode-utf8 "^1.0.3" + pngjs "^5.0.0" + yargs "^15.3.1" + +qs@6.13.0: + version "6.13.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + +qs@~6.5.2: + version "6.5.3" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + +query-string@5.1.1, query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +query-string@7.1.3, query-string@^7.1.1: + version "7.1.3" + resolved "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== + dependencies: + decode-uri-component "^0.2.2" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +radix3@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz#fd27d2af3896c6bf4bcdfab6427c69c2afc69ec0" + integrity sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc-align@^4.0.0: + version "4.0.15" + resolved "https://registry.npmjs.org/rc-align/-/rc-align-4.0.15.tgz#2bbd665cf85dfd0b0244c5a752b07565e9098577" + integrity sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + dom-align "^1.7.0" + rc-util "^5.26.0" + resize-observer-polyfill "^1.5.1" + +rc-cascader@~3.30.0: + version "3.30.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.30.0.tgz#da3e35cadcc00c58c62a6757eca6c7147ff94ea8" + integrity sha512-rrzSbk1Bdqbu+pDwiLCLHu72+lwX9BZ28+JKzoi0DWZ4N29QYFeip8Gctl33QVd2Xg3Rf14D3yAOG76ElJw16w== + dependencies: + "@babel/runtime" "^7.25.7" + classnames "^2.3.1" + rc-select "~14.16.2" + rc-tree "~5.10.1" + rc-util "^5.43.0" + +rc-cascader@~3.7.0, rc-cascader@~3.7.3: + version "3.7.3" + resolved "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.7.3.tgz#1e2ad238b283f7226ce4c9f3a420a35cb63fcc82" + integrity sha512-KBpT+kzhxDW+hxPiNk4zaKa99+Lie2/8nnI11XF+FIOPl4Bj9VlFZi61GrnWzhLGA7VEN+dTxAkNOjkySDa0dA== + dependencies: + "@babel/runtime" "^7.12.5" + array-tree-filter "^2.1.0" + classnames "^2.3.1" + rc-select "~14.1.0" + rc-tree "~5.7.0" + rc-util "^5.6.1" + +rc-checkbox@~3.0.0, rc-checkbox@~3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.0.1.tgz#f978771329be339d479cd81465eb2e2f8c82bc87" + integrity sha512-k7nxDWxYF+jDI0ZcCvuvj71xONmWRVe5+1MKcERRR9MRyP3tZ69b+yUCSXXh+sik4/Hc9P5wHr2nnUoGS2zBjA== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.3.2" + rc-util "^5.25.2" + +rc-checkbox@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.3.0.tgz#0ffcb65ab78c7d2fcd1a0d6554af36786516bd02" + integrity sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.3.2" + rc-util "^5.25.2" + +rc-collapse@~3.4.2: + version "3.4.2" + resolved "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.4.2.tgz#1310be7ad4cd0dcfc622c45f6c3b5ffdee403ad7" + integrity sha512-jpTwLgJzkhAgp2Wpi3xmbTbbYExg6fkptL67Uu5LCRVEj6wqmy0DHTjjeynsjOLsppHGHu41t1ELntZ0lEvS/Q== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.3.4" + rc-util "^5.2.1" + shallowequal "^1.1.0" + +rc-collapse@~3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.9.0.tgz#972404ce7724e1c9d1d2476543e1175404a36806" + integrity sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.3.4" + rc-util "^5.27.0" + +rc-dialog@~9.0.0, rc-dialog@~9.0.2: + version "9.0.4" + resolved "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.0.4.tgz#16c5a47aff0ee4e009f966e79926bef0451b6cda" + integrity sha512-pmnPRZKd9CGzGgf4a1ysBvMhxm8Afx5fF6M7AzLtJ0qh8X1bshurDlqnK4MBNAB4hAeAMMbz6Ytb1rkGMvKFbQ== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/portal" "^1.0.0-8" + classnames "^2.2.6" + rc-motion "^2.3.0" + rc-util "^5.21.0" + +rc-dialog@~9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.6.0.tgz#dc7a255c6ad1cb56021c3a61c7de86ee88c7c371" + integrity sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/portal" "^1.0.0-8" + classnames "^2.2.6" + rc-motion "^2.3.0" + rc-util "^5.21.0" + +rc-drawer@~6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/rc-drawer/-/rc-drawer-6.3.0.tgz#f8af5fafbab19b83722360dcf93e966d8a2875ad" + integrity sha512-uBZVb3xTAR+dBV53d/bUhTctCw3pwcwJoM7g5aX+7vgwt2zzVzoJ6aqFjYJpBlZ9zp0dVYN8fV+hykFE7c4lig== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/portal" "^1.1.1" + classnames "^2.2.6" + rc-motion "^2.6.1" + rc-util "^5.21.2" + +rc-drawer@~7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.2.0.tgz#8d7de2f1fd52f3ac5a25f54afbb8ac14c62e5663" + integrity sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg== + dependencies: + "@babel/runtime" "^7.23.9" + "@rc-component/portal" "^1.1.1" + classnames "^2.2.6" + rc-motion "^2.6.1" + rc-util "^5.38.1" + +rc-dropdown@~4.0.0, rc-dropdown@~4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.0.1.tgz#f65d9d3d89750241057db59d5a75e43cd4576b68" + integrity sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g== + dependencies: + "@babel/runtime" "^7.18.3" + classnames "^2.2.6" + rc-trigger "^5.3.1" + rc-util "^5.17.0" + +rc-dropdown@~4.2.0, rc-dropdown@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.1.tgz#44729eb2a4272e0353d31ac060da21e606accb1c" + integrity sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA== + dependencies: + "@babel/runtime" "^7.18.3" + "@rc-component/trigger" "^2.0.0" + classnames "^2.2.6" + rc-util "^5.44.1" + +rc-field-form@~1.27.4: + version "1.27.4" + resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.27.4.tgz#53600714af5b28c226c70d34867a8c52ccd64d44" + integrity sha512-PQColQnZimGKArnOh8V2907+VzDCXcqtFvHgevDLtqWc/P7YASb/FqntSmdS8q3VND5SHX3Y1vgMIzY22/f/0Q== + dependencies: + "@babel/runtime" "^7.18.0" + async-validator "^4.1.0" + rc-util "^5.8.0" + +rc-field-form@~1.34.0: + version "1.34.2" + resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.34.2.tgz#8463b79a44842a341899195f364e952c401ab7f1" + integrity sha512-BdciU5C7dBO51/9ZKcMvK2f8zaaO12Lt1eBhlAo8nNv+6htlNcgY9DAkUlZ7gfyWjnCc1Oo4hHIXau1m6tLw1A== + dependencies: + "@babel/runtime" "^7.18.0" + async-validator "^4.1.0" + rc-util "^5.32.2" + +rc-field-form@~1.38.2: + version "1.38.2" + resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.38.2.tgz#1eafac98eb84d47dc3b55de98ed50751d9852dd2" + integrity sha512-O83Oi1qPyEv31Sg+Jwvsj6pXc8uQI2BtIAkURr5lvEYHVggXJhdU/nynK8wY1gbw0qR48k731sN5ON4egRCROA== + dependencies: + "@babel/runtime" "^7.18.0" + async-validator "^4.1.0" + rc-util "^5.32.2" + +rc-field-form@~2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.7.0.tgz#22413e793f35bfc1f35b0ec462774d7277f5a399" + integrity sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA== + dependencies: + "@babel/runtime" "^7.18.0" + "@rc-component/async-validator" "^5.0.3" + rc-util "^5.32.2" + +rc-image@~5.13.0: + version "5.13.0" + resolved "https://registry.npmjs.org/rc-image/-/rc-image-5.13.0.tgz#1ed9b852a40b5eff34786ba7d2f0e9d26eeab874" + integrity sha512-iZTOmw5eWo2+gcrJMMcnd7SsxVHl3w5xlyCgsULUdJhJbnuI8i/AL0tVOsE7aLn9VfOh1qgDT3mC2G75/c7mqg== + dependencies: + "@babel/runtime" "^7.11.2" + "@rc-component/portal" "^1.0.2" + classnames "^2.2.6" + rc-dialog "~9.0.0" + rc-motion "^2.6.2" + rc-util "^5.0.6" + +rc-image@~7.11.0: + version "7.11.1" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.1.tgz#3ab290708dc053d3681de94186522e4e594f6772" + integrity sha512-XuoWx4KUXg7hNy5mRTy1i8c8p3K8boWg6UajbHpDXS5AlRVucNfTi5YxTtPBTBzegxAZpvuLfh3emXFt6ybUdA== + dependencies: + "@babel/runtime" "^7.11.2" + "@rc-component/portal" "^1.0.2" + classnames "^2.2.6" + rc-dialog "~9.6.0" + rc-motion "^2.6.2" + rc-util "^5.34.1" + +rc-input-number@~7.3.11, rc-input-number@~7.3.9: + version "7.3.11" + resolved "https://registry.npmjs.org/rc-input-number/-/rc-input-number-7.3.11.tgz#c7089705a220e1a59ba974fabf89693e00dd2442" + integrity sha512-aMWPEjFeles6PQnMqP5eWpxzsvHm9rh1jQOWXExUEIxhX62Fyl/ptifLHOn17+waDG1T/YUb6flfJbvwRhHrbA== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.23.0" + +rc-input-number@~9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.3.0.tgz#3403c1071fcb9dbf91073faddd80ea83bd3974df" + integrity sha512-JQ363ywqRyxwgVxpg2z2kja3CehTpYdqR7emJ/6yJjRdbvo+RvfE83fcpBCIJRq3zLp8SakmEXq60qzWyZ7Usw== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/mini-decimal" "^1.0.1" + classnames "^2.2.5" + rc-input "~1.6.0" + rc-util "^5.40.1" + +rc-input@~0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/rc-input/-/rc-input-0.1.4.tgz#45cb4ba209ae6cc835a2acb8629d4f8f0cb347e0" + integrity sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.18.1" + +rc-input@~1.6.0, rc-input@~1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.6.4.tgz#08d91460f6b75b3fa5294154e89775784c233129" + integrity sha512-lBZhfRD4NSAUW0zOKLUeI6GJuXkxeZYi0hr8VcJgJpyTNOvHw1ysrKWAHcEOAAHj7guxgmWYSi6xWrEdfrSAsA== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.18.1" + +rc-mentions@~1.13.1: + version "1.13.1" + resolved "https://registry.npmjs.org/rc-mentions/-/rc-mentions-1.13.1.tgz#c884b70e1505a197f1b32a7c6b39090db6992a72" + integrity sha512-FCkaWw6JQygtOz0+Vxz/M/NWqrWHB9LwqlY2RtcuFqWJNFK9njijOOzTSsBGANliGufVUzx/xuPHmZPBV0+Hgw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.6" + rc-menu "~9.8.0" + rc-textarea "^0.4.0" + rc-trigger "^5.0.4" + rc-util "^5.22.5" + +rc-mentions@~2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.17.0.tgz#d16dd5c8e4db87862c1007f7195b0aea9247cdcd" + integrity sha512-sfHy+qLvc+p8jx8GUsujZWXDOIlIimp6YQz7N5ONQ6bHsa2kyG+BLa5k2wuxgebBbH97is33wxiyq5UkiXRpHA== + dependencies: + "@babel/runtime" "^7.22.5" + "@rc-component/trigger" "^2.0.0" + classnames "^2.2.6" + rc-input "~1.6.0" + rc-menu "~9.16.0" + rc-textarea "~1.8.0" + rc-util "^5.34.1" + +rc-menu@~9.16.0: + version "9.16.1" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.16.1.tgz#9df1168e41d87dc7164c582173e1a1d32011899f" + integrity sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^2.0.0" + classnames "2.x" + rc-motion "^2.4.3" + rc-overflow "^1.3.1" + rc-util "^5.27.0" + +rc-menu@~9.8.0, rc-menu@~9.8.4: + version "9.8.4" + resolved "https://registry.npmjs.org/rc-menu/-/rc-menu-9.8.4.tgz#58bf19d471e3c74ff4bcfdb0f02a3826ebe2553b" + integrity sha512-lmw2j8I2fhdIzHmC9ajfImfckt0WDb2KVJJBBRIsxPEw2kGkEfjLMUoB1NgiNT/Q5cC8PdjGOGQjHJIJMwyNMw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.4.3" + rc-overflow "^1.2.8" + rc-trigger "^5.1.2" + rc-util "^5.27.0" + +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0: + version "2.9.2" + resolved "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.2.tgz#f7c6d480250df8a512d0cfdce07ff3da906958cf" + integrity sha512-fUAhHKLDdkAXIDLH0GYwof3raS58dtNUmzLF2MeiR8o6n4thNpSDQhOqQzWE4WfFZDCi9VEN8n7tiB7czREcyw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.43.0" + +rc-motion@^2.9.5: + version "2.9.5" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" + integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.44.0" + +rc-notification@^4.0.0, rc-notification@~4.6.0, rc-notification@~4.6.1: + version "4.6.1" + resolved "https://registry.npmjs.org/rc-notification/-/rc-notification-4.6.1.tgz#068e8674f4bd7926a447eca512915d4b41b15c91" + integrity sha512-NSmFYwrrdY3+un1GvDAJQw62Xi9LNMSsoQyo95tuaYrcad5Bn9gJUL8AREufRxSQAQnr64u3LtP3EUyLYT6bhw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.2.0" + rc-util "^5.20.1" + +rc-notification@~5.6.2: + version "5.6.4" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.4.tgz#ea89c39c13cd517fdfd97fe63f03376fabb78544" + integrity sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.9.0" + rc-util "^5.20.1" + +rc-overflow@^1.0.0, rc-overflow@^1.2.8: + version "1.3.2" + resolved "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" + integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-resize-observer "^1.0.0" + rc-util "^5.37.0" + +rc-overflow@^1.3.1, rc-overflow@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.4.1.tgz#e1bcf0375979c24cffa2d87bf83a19ded5fcdf45" + integrity sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-resize-observer "^1.0.0" + rc-util "^5.37.0" + +rc-pagination@~3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/rc-pagination/-/rc-pagination-3.2.0.tgz#4f2fdba9fdac0f48e5c9fb1141973818138af7e1" + integrity sha512-5tIXjB670WwwcAJzAqp2J+cOBS9W3cH/WU1EiYwXljuZ4vtZXKlY2Idq8FZrnYBz8KhN3vwPo9CoV/SJS6SL1w== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.1" + +rc-pagination@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-5.0.0.tgz#7633e1f0ff372ad78c03e86bcef78b660374d196" + integrity sha512-QjrPvbAQwps93iluvFM62AEYglGYhWW2q/nliQqmvkTi4PXP4HHoh00iC1Sa5LLVmtWQHmG73fBi2x6H6vFHRg== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.3.2" + rc-util "^5.38.0" + +rc-picker@~2.7.0, rc-picker@~2.7.6: + version "2.7.6" + resolved "https://registry.npmjs.org/rc-picker/-/rc-picker-2.7.6.tgz#03d855888d1878d8946bab77a3d24477fd3a0792" + integrity sha512-H9if/BUJUZBOhPfWcPeT15JUI3/ntrG9muzERrXDkSoWmDj4yzmBvumozpxYrHwjcKnjyDGAke68d+whWwvhHA== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.1" + date-fns "2.x" + dayjs "1.x" + moment "^2.24.0" + rc-trigger "^5.0.4" + rc-util "^5.37.0" + shallowequal "^1.1.0" + +rc-picker@~4.8.3: + version "4.8.3" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.8.3.tgz#06cffd5a2201fc8d274e12f7ee32ea8ba6f3f60f" + integrity sha512-hJ45qoEs4mfxXPAJdp1n3sKwADul874Cd0/HwnsEOE60H+tgiJUGgbOD62As3EG/rFVNS5AWRfBCDJJfmRqOVQ== + dependencies: + "@babel/runtime" "^7.24.7" + "@rc-component/trigger" "^2.0.0" + classnames "^2.2.1" + rc-overflow "^1.3.2" + rc-resize-observer "^1.4.0" + rc-util "^5.43.0" + +rc-progress@~3.4.1, rc-progress@~3.4.2: + version "3.4.2" + resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-3.4.2.tgz#f8df9ee95e790490171ab6b31bf07303cdc79980" + integrity sha512-iAGhwWU+tsayP+Jkl9T4+6rHeQTG9kDz8JAHZk4XtQOcYN5fj9H34NXNEdRdZx94VUDHMqCb1yOIvi8eJRh67w== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.6" + rc-util "^5.16.1" + +rc-progress@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" + integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.6" + rc-util "^5.16.1" + +rc-rate@~2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.1.tgz#29af7a3d4768362e9d4388f955a8b6389526b7fd" + integrity sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.0.1" + +rc-rate@~2.9.0, rc-rate@~2.9.3: + version "2.9.3" + resolved "https://registry.npmjs.org/rc-rate/-/rc-rate-2.9.3.tgz#b30a8043ffcb327bab053cd78508e07015d8a483" + integrity sha512-2THssUSnRhtqIouQIIXqsZGzRczvp4WsH4WvGuhiwm+LG2fVpDUJliP9O1zeDOZvYfBE/Bup4SgHun/eCkbjgQ== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.0.1" + +rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0, rc-resize-observer@^1.3.1: + version "1.4.0" + resolved "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" + integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== + dependencies: + "@babel/runtime" "^7.20.7" + classnames "^2.2.1" + rc-util "^5.38.0" + resize-observer-polyfill "^1.5.1" + +rc-resize-observer@^1.4.0, rc-resize-observer@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" + integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== + dependencies: + "@babel/runtime" "^7.20.7" + classnames "^2.2.1" + rc-util "^5.44.1" + resize-observer-polyfill "^1.5.1" + +rc-segmented@~2.1.0: + version "2.1.2" + resolved "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.1.2.tgz#14c9077a1dae9c2ccb2ef5fbc5662c1c48c7ce8e" + integrity sha512-qGo1bCr83ESXpXVOCXjFe1QJlCAQXyi9KCiy8eX3rIMYlTeJr/ftySIaTnYsitL18SvWf5ZEHsfqIWoX0EMfFQ== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-motion "^2.4.4" + rc-util "^5.17.0" + +rc-segmented@~2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.3.0.tgz#b3fe080fb434a266c02e30bb62a47d2c6e094341" + integrity sha512-I3FtM5Smua/ESXutFfb8gJ8ZPcvFR+qUgeeGFQHBOvRiRKyAk4aBE5nfqrxXx+h8/vn60DQjOt6i4RNtrbOobg== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-motion "^2.4.4" + rc-util "^5.17.0" + +rc-segmented@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.5.0.tgz#3b5423adf57459345c77c39c7581fde786a16c11" + integrity sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-motion "^2.4.4" + rc-util "^5.17.0" + +rc-select@~14.1.0, rc-select@~14.1.17, rc-select@~14.1.18: + version "14.1.18" + resolved "https://registry.npmjs.org/rc-select/-/rc-select-14.1.18.tgz#f1d95233132cda9c1485963254255b83e97a37a9" + integrity sha512-4JgY3oG2Yz68ECMUSCON7mtxuJvCSj+LJpHEg/AONaaVBxIIrmI/ZTuMJkyojall/X50YdBe5oMKqHHPNiPzEg== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-overflow "^1.0.0" + rc-trigger "^5.0.4" + rc-util "^5.16.1" + rc-virtual-list "^3.2.0" + +rc-select@~14.16.2, rc-select@~14.16.4: + version "14.16.8" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.8.tgz#78e6782f1ccc1f03d9003bc3effa4ed609d29a97" + integrity sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^2.1.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-overflow "^1.3.1" + rc-util "^5.16.1" + rc-virtual-list "^3.5.2" + +rc-slider@~10.0.0, rc-slider@~10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/rc-slider/-/rc-slider-10.0.1.tgz#7058c68ff1e1aa4e7c3536e5e10128bdbccb87f9" + integrity sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.18.1" + shallowequal "^1.1.0" + +rc-slider@~11.1.7: + version "11.1.8" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.8.tgz#cf3b30dacac8f98d44f7685f733f6f7da146fc06" + integrity sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.36.0" + +rc-steps@~5.0.0, rc-steps@~5.0.0-alpha.2: + version "5.0.0" + resolved "https://registry.npmjs.org/rc-steps/-/rc-steps-5.0.0.tgz#2e2403f2dd69eb3966d65f461f7e3a8ee1ef69fe" + integrity sha512-9TgRvnVYirdhbV0C3syJFj9EhCRqoJAsxt4i1rED5o8/ZcSv5TLIYyo4H8MCjLPvbe2R+oBAm/IYBEtC+OS1Rw== + dependencies: + "@babel/runtime" "^7.16.7" + classnames "^2.2.3" + rc-util "^5.16.1" + +rc-steps@~6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" + integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== + dependencies: + "@babel/runtime" "^7.16.7" + classnames "^2.2.3" + rc-util "^5.16.1" + +rc-switch@~3.2.0, rc-switch@~3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/rc-switch/-/rc-switch-3.2.2.tgz#d001f77f12664d52595b4f6fb425dd9e66fba8e8" + integrity sha512-+gUJClsZZzvAHGy1vZfnwySxj+MjLlGRyXKXScrtCTcmiYNPzxDFOxdQ/3pK1Kt/0POvwJ/6ALOR8gwdXGhs+A== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.1" + rc-util "^5.0.1" + +rc-switch@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" + integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== + dependencies: + "@babel/runtime" "^7.21.0" + classnames "^2.2.1" + rc-util "^5.30.0" + +rc-table@~7.26.0: + version "7.26.0" + resolved "https://registry.npmjs.org/rc-table/-/rc-table-7.26.0.tgz#9d517e7fa512e7571fdcc453eb1bf19edfac6fbc" + integrity sha512-0cD8e6S+DTGAt5nBZQIPFYEaIukn17sfa5uFL98faHlH/whZzD8ii3dbFL4wmUDEL4BLybhYop+QUfZJ4CPvNQ== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-resize-observer "^1.1.0" + rc-util "^5.22.5" + shallowequal "^1.1.0" + +rc-table@~7.49.0: + version "7.49.0" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.49.0.tgz#f5a4880d9527d2c9e42f5f721b5423e7a1ca475b" + integrity sha512-/FoPLX94muAQOxVpi1jhnpKjOIqUbT81eELQPAzSXOke4ky4oCWYUXOcVpL31ZCO90xScwVSXRd7coqtgtB1Ng== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/context" "^1.4.0" + classnames "^2.2.5" + rc-resize-observer "^1.1.0" + rc-util "^5.41.0" + rc-virtual-list "^3.14.2" + +rc-tabs@~12.5.10, rc-tabs@~12.5.6: + version "12.5.10" + resolved "https://registry.npmjs.org/rc-tabs/-/rc-tabs-12.5.10.tgz#0e41c723fac66c4f0bcad3271429fff6653b0721" + integrity sha512-Ay0l0jtd4eXepFH9vWBvinBjqOpqzcsJTerBGwJy435P2S90Uu38q8U/mvc1sxUEVOXX5ZCFbxcWPnfG3dH+tQ== + dependencies: + "@babel/runtime" "^7.11.2" + classnames "2.x" + rc-dropdown "~4.0.0" + rc-menu "~9.8.0" + rc-motion "^2.6.2" + rc-resize-observer "^1.0.0" + rc-util "^5.16.0" + +rc-tabs@~15.4.0: + version "15.4.0" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.4.0.tgz#a829cabcb33f93525b548010f5bbf91dee7ac1d6" + integrity sha512-llKuyiAVqmXm2z7OrmhX5cNb2ueZaL8ZyA2P4R+6/72NYYcbEgOXibwHiQCFY2RiN3swXl53SIABi2CumUS02g== + dependencies: + "@babel/runtime" "^7.11.2" + classnames "2.x" + rc-dropdown "~4.2.0" + rc-menu "~9.16.0" + rc-motion "^2.6.2" + rc-resize-observer "^1.0.0" + rc-util "^5.34.1" + +rc-textarea@^0.4.0, rc-textarea@~0.4.5, rc-textarea@~0.4.7: + version "0.4.7" + resolved "https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.4.7.tgz#627f662d46f99e0059d1c1ebc8db40c65339fe90" + integrity sha512-IQPd1CDI3mnMlkFyzt2O4gQ2lxUsnBAeJEoZGJnkkXgORNqyM9qovdrCj9NzcRfpHgLdzaEbU3AmobNFGUznwQ== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.1" + rc-resize-observer "^1.0.0" + rc-util "^5.24.4" + shallowequal "^1.1.0" + +rc-textarea@~1.8.0, rc-textarea@~1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.8.2.tgz#57a6847304551c1883fc3fb0c5076d587f70bf7f" + integrity sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.1" + rc-input "~1.6.0" + rc-resize-observer "^1.0.0" + rc-util "^5.27.0" + +rc-tooltip@~5.2.0, rc-tooltip@~5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-5.2.2.tgz#e5cafa8ecebf78108936a0bcb93c150fa81ac93b" + integrity sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg== + dependencies: + "@babel/runtime" "^7.11.2" + classnames "^2.3.1" + rc-trigger "^5.0.0" + +rc-tooltip@~6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.1.tgz#9a8f0335c86443a0c20c2557933205f645a381b7" + integrity sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg== + dependencies: + "@babel/runtime" "^7.11.2" + "@rc-component/trigger" "^2.0.0" + classnames "^2.3.1" + +rc-tree-select@~5.24.5: + version "5.24.5" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.24.5.tgz#a1bf85c7d5e4979880cfb0748bb6bab937ed3483" + integrity sha512-PnyR8LZJWaiEFw0SHRqo4MNQWyyZsyMs8eNmo68uXZWjxc7QqeWcjPPoONN0rc90c3HZqGF9z+Roz+GLzY5GXA== + dependencies: + "@babel/runtime" "^7.25.7" + classnames "2.x" + rc-select "~14.16.2" + rc-tree "~5.10.1" + rc-util "^5.43.0" + +rc-tree-select@~5.5.0, rc-tree-select@~5.5.5: + version "5.5.5" + resolved "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.5.5.tgz#d28b3b45da1e820cd21762ba0ee93c19429bb369" + integrity sha512-k2av7jF6tW9bIO4mQhaVdV4kJ1c54oxV3/hHVU+oD251Gb5JN+m1RbJFTMf1o0rAFqkvto33rxMdpafaGKQRJw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-select "~14.1.0" + rc-tree "~5.7.0" + rc-util "^5.16.1" + +rc-tree@~5.10.1: + version "5.10.1" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.10.1.tgz#8807614c54aaa39edc05392f0f5982b609d95255" + integrity sha512-FPXb3tT/u39mgjr6JNlHaUTYfHkVGW56XaGDahDpEFLGsnPxGcVLNTjcqoQb/GNbSCycl7tD7EvIymwOTP0+Yw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-util "^5.16.1" + rc-virtual-list "^3.5.1" + +rc-tree@~5.7.0, rc-tree@~5.7.12: + version "5.7.12" + resolved "https://registry.npmjs.org/rc-tree/-/rc-tree-5.7.12.tgz#6910e551390963708936c2cbf925f9deff4a6d76" + integrity sha512-LXA5nY2hG5koIAlHW5sgXgLpOMz+bFRbnZZ+cCg0tQs4Wv1AmY7EDi1SK7iFXhslYockbqUerQan82jljoaItg== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-util "^5.16.1" + rc-virtual-list "^3.5.1" + +rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10, rc-trigger@^5.3.1, rc-trigger@^5.3.4: + version "5.3.4" + resolved "https://registry.npmjs.org/rc-trigger/-/rc-trigger-5.3.4.tgz#6b4b26e32825677c837d1eb4d7085035eecf9a61" + integrity sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw== + dependencies: + "@babel/runtime" "^7.18.3" + classnames "^2.2.6" + rc-align "^4.0.0" + rc-motion "^2.0.0" + rc-util "^5.19.2" + +rc-upload@~4.3.0, rc-upload@~4.3.6: + version "4.3.6" + resolved "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.6.tgz#6a87397315cee065a04bee4103d6de9dbe2e377a" + integrity sha512-Bt7ESeG5tT3IY82fZcP+s0tQU2xmo1W6P3S8NboUUliquJLQYLkUcsaExi3IlBVr43GQMCjo30RA2o0i70+NjA== + dependencies: + "@babel/runtime" "^7.18.3" + classnames "^2.2.5" + rc-util "^5.2.0" + +rc-upload@~4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.8.1.tgz#ac55f2bc101b95b52a6e47f3c18f0f55b54e16d2" + integrity sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw== + dependencies: + "@babel/runtime" "^7.18.3" + classnames "^2.2.5" + rc-util "^5.2.0" + +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.35.1, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.43.0, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4: + version "5.43.0" + resolved "https://registry.npmjs.org/rc-util/-/rc-util-5.43.0.tgz#bba91fbef2c3e30ea2c236893746f3e9b05ecc4c" + integrity sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + +rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.41.0, rc-util@^5.44.0, rc-util@^5.44.1, rc-util@^5.44.3: + version "5.44.4" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.4.tgz#89ee9037683cca01cd60f1a6bbda761457dd6ba5" + integrity sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + +rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.2: + version "3.19.1" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.19.1.tgz#785b5f409b0bbbfa1eaadcc811971359c83dd9fb" + integrity sha512-DCapO2oyPqmooGhxBuXHM4lFuX+sshQwWqqkuyFA+4rShLe//+GEPVwiDgO+jKtKHtbeYwZoNvetwfHdOf+iUQ== + dependencies: + "@babel/runtime" "^7.20.0" + classnames "^2.2.6" + rc-resize-observer "^1.0.0" + rc-util "^5.36.0" + +rc-virtual-list@^3.2.0, rc-virtual-list@^3.5.1: + version "3.14.5" + resolved "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.14.5.tgz#593cd13fe05eabf4ad098329704a30c77701869e" + integrity sha512-ZMOnkCLv2wUN8Jz7yI4XiSLa9THlYvf00LuMhb1JlsQCewuU7ydPuHw1rGVPhe9VZYl/5UqODtNd7QKJ2DMGfg== + dependencies: + "@babel/runtime" "^7.20.0" + classnames "^2.2.6" + rc-resize-observer "^1.0.0" + rc-util "^5.36.0" + +react-dom@18.2.0, react-dom@^18.3.1: + version "18.2.0" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-error-boundary@^3.1.0: + version "3.1.4" + resolved "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0" + integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA== + dependencies: + "@babel/runtime" "^7.12.5" + +react-fast-compare@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== + +react-ga@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/react-ga/-/react-ga-3.3.1.tgz#d8e1f4e05ec55ed6ff944dcb14b99011dfaf9504" + integrity sha512-4Vc0W5EvXAXUN/wWyxvsAKDLLgtJ3oLmhYYssx+YzphJpejtOst6cbIHCIyF50Fdxuf5DDKqRYny24yJ2y7GFQ== + +react-i18next@^11.18.0, react-i18next@^11.18.6: + version "11.18.6" + resolved "https://registry.npmjs.org/react-i18next/-/react-i18next-11.18.6.tgz#e159c2960c718c1314f1e8fcaa282d1c8b167887" + integrity sha512-yHb2F9BiT0lqoQDt8loZ5gWP331GwctHz9tYQ8A2EIEUu+CcEdjBLQWli1USG3RdWQt3W+jqQLg/d4rrQR96LA== + dependencies: + "@babel/runtime" "^7.14.5" + html-parse-stringify "^3.0.1" + +react-infinite-scroll-component@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz#7e511e7aa0f728ac3e51f64a38a6079ac522407f" + integrity sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ== + dependencies: + throttle-debounce "^2.1.0" + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0, react-is@^18.2.0: + version "18.3.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +react-native-securerandom@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz#f130623a412c338b0afadedbc204c5cbb8bf2070" + integrity sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw== + dependencies: + base64-js "*" + +react-native-webview@^11.26.0: + version "11.26.1" + resolved "https://registry.npmjs.org/react-native-webview/-/react-native-webview-11.26.1.tgz#658c09ed5162dc170b361e48c2dd26c9712879da" + integrity sha512-hC7BkxOpf+z0UKhxFSFTPAM4shQzYmZHoELa6/8a/MspcjEP7ukYKpuSUTLDywQditT8yI9idfcKvfZDKQExGw== + dependencies: + escape-string-regexp "2.0.0" + invariant "2.2.4" + +react-qrcode-logo@2.9.0: + version "2.9.0" + resolved "https://registry.npmjs.org/react-qrcode-logo/-/react-qrcode-logo-2.9.0.tgz#34270d563b4a15396b5e7e7656ea2d079673adad" + integrity sha512-x9uctB/8kuQ+x7p4V/BRv6qbQV4xYopLcitBuQT3925U/zVFb3bbDUsUZsi7hEKuAX/wRsfhs331FhlPSYCO9Q== + dependencies: + lodash.isequal "^4.5.0" + qrcode-generator "^1.4.1" + +react-refresh@^0.14.2: + version "0.14.2" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" + integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== + +react-universal-interface@^0.6.2: + version "0.6.2" + resolved "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" + integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== + +react-use@^17.4.0: + version "17.5.1" + resolved "https://registry.npmjs.org/react-use/-/react-use-17.5.1.tgz#19fc2ae079775d8450339e9fa8dbe25b17f2263c" + integrity sha512-LG/uPEVRflLWMwi3j/sZqR00nF6JGqTTDblkXK2nzXsIvij06hXl1V/MZIlwj1OKIQUtlh1l9jK8gLsRyCQxMg== + dependencies: + "@types/js-cookie" "^2.2.6" + "@xobotyi/scrollbar-width" "^1.9.5" + copy-to-clipboard "^3.3.1" + fast-deep-equal "^3.1.3" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.6.2" + react-universal-interface "^0.6.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.1.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^3.0.1" + ts-easing "^0.2.0" + tslib "^2.1.0" + +react-use@^17.6.0: + version "17.6.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.6.0.tgz#2101a3a79dc965a25866b21f5d6de4b128488a14" + integrity sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g== + dependencies: + "@types/js-cookie" "^2.2.6" + "@xobotyi/scrollbar-width" "^1.9.5" + copy-to-clipboard "^3.3.1" + fast-deep-equal "^3.1.3" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.6.2" + react-universal-interface "^0.6.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.1.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^3.0.1" + ts-easing "^0.2.0" + tslib "^2.1.0" + +react@18.2.0, react@^18.3.1: + version "18.2.0" + resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.2: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^2.3.3, readable-stream@^2.3.8: + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +"readable-stream@^3.6.2 || ^4.4.2", readable-stream@^4.3.0: + version "4.5.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readable-stream@^4.4.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" + integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +real-require@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" + integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +redux-thunk@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" + integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== + +redux@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + +reflect.getprototypeof@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" + integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.1" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +rehackt@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" + integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== + +request@^2.79.0: + version "2.88.2" + resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +reselect@^5.1.0: + version "5.1.1" + resolved "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" + integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== + +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +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#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-global@1.0.0, resolve-global@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz#a2a79df4af2ca3f49bf77ef9ddacd322dad19255" + integrity sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw== + dependencies: + global-dirs "^0.1.1" + +resolve@1.22.8, resolve@^1.10.0, resolve@^1.22.0, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.5: + version "2.0.0-next.5" + resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +response-iterator@^0.2.6: + version "0.2.6" + resolved "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" + integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +rollup-plugin-visualizer@^5.9.2: + version "5.14.0" + resolved "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.14.0.tgz#be82d43fb3c644e396e2d50ac8a53d354022d57c" + integrity sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA== + dependencies: + open "^8.4.0" + picomatch "^4.0.2" + source-map "^0.7.4" + yargs "^17.5.1" + +rollup@2.78.0: + version "2.78.0" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.78.0.tgz#00995deae70c0f712ea79ad904d5f6b033209d9e" + integrity sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg== + optionalDependencies: + fsevents "~2.3.2" + +rollup@^4.30.1: + version "4.34.7" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.34.7.tgz#e00d8550688a616a3481c6446bb688d4c753ba8f" + integrity sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.34.7" + "@rollup/rollup-android-arm64" "4.34.7" + "@rollup/rollup-darwin-arm64" "4.34.7" + "@rollup/rollup-darwin-x64" "4.34.7" + "@rollup/rollup-freebsd-arm64" "4.34.7" + "@rollup/rollup-freebsd-x64" "4.34.7" + "@rollup/rollup-linux-arm-gnueabihf" "4.34.7" + "@rollup/rollup-linux-arm-musleabihf" "4.34.7" + "@rollup/rollup-linux-arm64-gnu" "4.34.7" + "@rollup/rollup-linux-arm64-musl" "4.34.7" + "@rollup/rollup-linux-loongarch64-gnu" "4.34.7" + "@rollup/rollup-linux-powerpc64le-gnu" "4.34.7" + "@rollup/rollup-linux-riscv64-gnu" "4.34.7" + "@rollup/rollup-linux-s390x-gnu" "4.34.7" + "@rollup/rollup-linux-x64-gnu" "4.34.7" + "@rollup/rollup-linux-x64-musl" "4.34.7" + "@rollup/rollup-win32-arm64-msvc" "4.34.7" + "@rollup/rollup-win32-ia32-msvc" "4.34.7" + "@rollup/rollup-win32-x64-msvc" "4.34.7" + fsevents "~2.3.2" + +rtl-css-js@^1.16.1: + version "1.16.1" + resolved "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz#4b48b4354b0ff917a30488d95100fbf7219a3e80" + integrity sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg== + dependencies: + "@babel/runtime" "^7.1.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +runes2@^1.1.2: + version "1.1.4" + resolved "https://registry.npmjs.org/runes2/-/runes2-1.1.4.tgz#aa38d3d7946e147ac4718ed0fb19b22340ae5c66" + integrity sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g== + +rxjs@6, rxjs@^6.6.7: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +safe-stable-stringify@^2.1.0: + version "2.5.0" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.4.1" + resolved "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + +scheduler@^0.23.0: + version "0.23.2" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== + dependencies: + loose-envify "^1.1.0" + +schema-utils@^3.0.0: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +screenfull@^5.0.0, screenfull@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + +scroll-into-view-if-needed@^2.2.25: + version "2.2.31" + resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz#d3c482959dc483e37962d1521254e3295d0d1587" + integrity sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA== + dependencies: + compute-scroll-into-view "^1.0.20" + +scroll-into-view-if-needed@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" + integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== + dependencies: + compute-scroll-into-view "^3.0.2" + +scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +scryptsy@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" + integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== + +secp256k1@^3.0.1: + version "3.8.0" + resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz#28f59f4b01dbee9575f56a47034b7d2e3b3b352d" + integrity sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.5.2" + nan "^2.14.0" + safe-buffer "^5.1.2" + +secp256k1@^4.0.1: + version "4.0.4" + resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz#58f0bfe1830fe777d9ca1ffc7574962a8189f8ab" + integrity sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw== + dependencies: + elliptic "^6.5.7" + node-addon-api "^5.0.0" + node-gyp-build "^4.2.0" + +secp256k1@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== + dependencies: + elliptic "^6.5.7" + node-addon-api "^5.0.0" + node-gyp-build "^4.2.0" + +"semver@2 || 3 || 4 || 5", semver@^5.6.0: + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@7.5.4: + version "7.5.4" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +semver@^7.5.3: + version "7.7.1" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +send@0.19.0: + version "0.19.0" + resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1, set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + 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#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.2" + resolved "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz#5708fb0919d440657326cd5fe7d2599d07705019" + integrity sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +sirv@^1.0.7: + version "1.0.19" + resolved "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^1.0.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +socket.io-client@^2.3.0: + version "2.5.0" + resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.5.0.tgz#34f486f3640dde9c2211fce885ac2746f9baf5cb" + integrity sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw== + dependencies: + backo2 "1.0.2" + component-bind "1.0.0" + component-emitter "~1.3.0" + debug "~3.1.0" + engine.io-client "~3.5.0" + has-binary2 "~1.0.2" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-client@^4.5.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.8.1.tgz#1941eca135a5490b94281d0323fe2a35f6f291cb" + integrity sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.6.1" + socket.io-parser "~4.2.4" + +socket.io-client@^4.7.5: + version "4.7.5" + resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7" + integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.5.2" + socket.io-parser "~4.2.4" + +socket.io-parser@~3.3.0: + version "3.3.4" + resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.4.tgz#ab84236b6d06eaf1fb68b179b3a7501195886cc3" + integrity sha512-z/pFQB3x+EZldRRzORYW1vwVO8m/3ILkswtnpoeU6Ve3cbMWkmHEWDAVJn4QJtchiiFTo5j7UG2QvwxvaA9vow== + dependencies: + component-emitter "~1.3.0" + debug "~3.1.0" + isarray "2.0.1" + +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +sonic-boom@^2.2.1: + version "2.8.0" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" + integrity sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg== + dependencies: + atomic-sleep "^1.0.0" + +source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== + +source-map@^0.6.1, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + 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#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +spark-md5@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" + integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.20" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" + integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +split2@^3.0.0, split2@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + +sshpk@^1.7.0: + version "1.18.0" + resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-generator@^2.0.5: + version "2.0.10" + resolved "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" + integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== + dependencies: + stackframe "^1.3.4" + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +stacktrace-gps@^3.0.4: + version "3.1.2" + resolved "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0" + integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== + dependencies: + source-map "0.5.6" + stackframe "^1.3.4" + +stacktrace-js@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + +staged-components@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/staged-components/-/staged-components-1.1.3.tgz#bb5a396df2d9b48fbc31841a59f53437ed8b8ac6" + integrity sha512-9EIswzDqjwlEu+ymkV09TTlJfzSbKgEnNteUnZSTxkpMgr5Wx2CzzA9WcMFWBNCldqVPsHVnRGGrApduq2Se5A== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +std-env@^3.7.0: + version "3.7.0" + resolved "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== + +std-env@^3.8.0: + version "3.8.0" + resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" + integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +str2buf@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/str2buf/-/str2buf-1.3.0.tgz#a4172afff4310e67235178e738a2dbb573abead0" + integrity sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA== + +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +stream-shift@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" + integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== + +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== + +string-argv@0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +string-convert@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" + integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== + +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.includes@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz#8986d57aee66d5460c144620a6d873778ad7289f" + integrity sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.matchall@^4.0.11: + version "4.0.11" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" + +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1, string_decoder@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== + dependencies: + is-hex-prefixed "1.0.0" + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg== + +styled-jsx@5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.2.tgz#ff230fd593b737e9e68b630a694d460425478729" + integrity sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ== + +stylelint-config-prettier@^9.0.3: + version "9.0.5" + resolved "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.5.tgz#9f78bbf31c7307ca2df2dd60f42c7014ee9da56e" + integrity sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA== + +stylelint-config-recommended@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-8.0.0.tgz#7736be9984246177f017c39ec7b1cd0f19ae9117" + integrity sha512-IK6dWvE000+xBv9jbnHOnBq01gt6HGVB2ZTsot+QsMpe82doDQ9hvplxfv4YnpEuUwVGGd9y6nbaAnhrjcxhZQ== + +stylelint-config-standard@^26.0.0: + version "26.0.0" + resolved "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-26.0.0.tgz#4701b8d582d34120eec7d260ba779e4c2d953635" + integrity sha512-hUuB7LaaqM8abvkOO84wh5oYSkpXgTzHu2Zza6e7mY+aOmpNTjoFBRxSLlzY0uAOMWEFx0OMKzr+reG1BUtcqQ== + dependencies: + stylelint-config-recommended "^8.0.0" + +stylelint@^14.9.1: + version "14.16.1" + resolved "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz#b911063530619a1bbe44c2b875fd8181ebdc742d" + integrity sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A== + dependencies: + "@csstools/selector-specificity" "^2.0.2" + balanced-match "^2.0.0" + colord "^2.9.3" + cosmiconfig "^7.1.0" + css-functions-list "^3.1.0" + debug "^4.3.4" + fast-glob "^3.2.12" + fastest-levenshtein "^1.0.16" + file-entry-cache "^6.0.1" + global-modules "^2.0.0" + globby "^11.1.0" + globjoin "^0.1.4" + html-tags "^3.2.0" + ignore "^5.2.1" + import-lazy "^4.0.0" + imurmurhash "^0.1.4" + is-plain-object "^5.0.0" + known-css-properties "^0.26.0" + mathml-tag-names "^2.1.3" + meow "^9.0.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.19" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^6.0.0" + postcss-selector-parser "^6.0.11" + postcss-value-parser "^4.2.0" + resolve-from "^5.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + style-search "^0.1.0" + supports-hyperlinks "^2.3.0" + svg-tags "^1.0.0" + table "^6.8.1" + v8-compile-cache "^2.3.0" + write-file-atomic "^4.0.2" + +stylis@^4.3.0: + version "4.3.4" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" + integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== + +stylis@^4.3.4: + version "4.3.6" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" + integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== + +subscriptions-transport-ws@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz#baf88f050cba51d52afe781de5e81b3c31f89883" + integrity sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ== + dependencies: + backo2 "^1.0.2" + eventemitter3 "^3.1.0" + iterall "^1.2.1" + symbol-observable "^1.0.4" + ws "^5.2.0 || ^6.0.0 || ^7.0.0" + +superstruct@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca" + integrity sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.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#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== + +swarm-js@^0.1.40: + version "0.1.42" + resolved "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979" + integrity sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^11.8.5" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +symbol-observable@^1.0.4: + version "1.2.0" + resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +symbol-observable@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" + integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== + +symbol.inspect@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol.inspect/-/symbol.inspect-1.0.1.tgz#e13125b8038c4996eb0dfa1567332ad4dcd0763f" + integrity sha512-YQSL4duoHmLhsTD1Pw8RW6TZ5MaTX5rXJnqacJottr2P2LZBF/Yvrc3ku4NUpMOm8aM0KOCqM+UAkMA5HWQCzQ== + +system-architecture@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d" + integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA== + +table@^6.8.1: + version "6.8.2" + resolved "https://registry.npmjs.org/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar@^4.0.2: + version "4.4.19" + resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + +test-exclude@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" + integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^10.4.1" + minimatch "^9.0.4" + +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thread-stream@^0.15.1: + version "0.15.2" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz#fb95ad87d2f1e28f07116eb23d85aba3bc0425f4" + integrity sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA== + dependencies: + real-require "^0.1.0" + +throttle-debounce@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz#fd31865e66502071e411817e241465b3e9c372e2" + integrity sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ== + +throttle-debounce@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" + integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== + +throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" + integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== + +through2@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== + +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinypool@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== + +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== + +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +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#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tonweb@^0.0.66: + version "0.0.66" + resolved "https://registry.yarnpkg.com/tonweb/-/tonweb-0.0.66.tgz#e9aa1f81ac2c2a9b0ff692294d58b1dfea516fb4" + integrity sha512-ntDJU1b/kgMFvOYN09FvhdnAfwnZh8W5sONkUP+P0FQkYRa/027otRxApp5gfxZ9DR+3WTnCQgrsG/EXv8RhEA== + dependencies: + "@ledgerhq/hw-transport-web-ble" "5.48.0" + "@ledgerhq/hw-transport-webhid" "5.48.0" + "@ledgerhq/hw-transport-webusb" "5.48.0" + bn.js "5.1.1" + ethjs-unit "0.1.6" + isomorphic-webcrypto "2.3.8" + node-fetch "2.6.7" + tweetnacl "1.0.3" + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +"tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0": + version "4.1.4" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + +ts-invariant@^0.10.3: + version "0.10.3" + resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" + integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== + dependencies: + tslib "^2.1.0" + +ts-node@^10.8.1: + version "10.9.2" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tsconfck@^3.0.3: + version "3.1.5" + resolved "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.5.tgz#2f07f9be6576825e7a77470a5304ce06c7746e61" + integrity sha512-CLDfGgUp7XPswWnezWwsCRxNmgQjhYq3VXHM0/XIRxhVrKw0M1if9agzryh1QS3nxjCROvV+xWxoJO1YctzzWg== + +tsconfig-paths@^3.14.1, tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@1.14.1, tslib@^1.8.1, tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0, tslib@^2.6.2: + version "2.7.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + +tslib@^2.7.0, tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@1.0.3, tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^2.7.2: + version "2.7.3" + resolved "https://registry.npmjs.org/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + +"typescript@^4.6.4 || ^5.2.2", typescript@^5.0.4: + version "5.6.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" + integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== + +ua-parser-js@^1.0.35: + version "1.0.39" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018" + integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw== + +ufo@^1.4.0, ufo@^1.5.3, ufo@^1.5.4: + version "1.5.4" + resolved "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754" + integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== + +uint8arrays@3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.0.tgz#8186b8eafce68f28bd29bd29d683a311778901e2" + integrity sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog== + dependencies: + multiformats "^9.4.2" + +uint8arrays@^3.0.0: + version "3.1.1" + resolved "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" + integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== + dependencies: + multiformats "^9.4.2" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +uncrypto@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz#e1288d609226f2d02d8d69ee861fa20d8348ef2b" + integrity sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q== + +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +unenv@^1.9.0: + version "1.10.0" + resolved "https://registry.npmjs.org/unenv/-/unenv-1.10.0.tgz#c3394a6c6e4cfe68d699f87af456fe3f0db39571" + integrity sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ== + dependencies: + consola "^3.2.3" + defu "^6.1.4" + mime "^3.0.0" + node-fetch-native "^1.6.4" + pathe "^1.1.2" + +unfetch@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" + integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +unstorage@^1.9.0: + version "1.12.0" + resolved "https://registry.npmjs.org/unstorage/-/unstorage-1.12.0.tgz#a215895dfdef01ffb8ff529bb3869dbf947d8498" + integrity sha512-ARZYTXiC+e8z3lRM7/qY9oyaOkaozCeNd2xoz7sYK9fv7OLGhVsf+BZbmASqiK/HTZ7T6eAlnVq9JynZppyk3w== + dependencies: + anymatch "^3.1.3" + chokidar "^3.6.0" + destr "^2.0.3" + h3 "^1.12.0" + listhen "^1.7.2" + lru-cache "^10.4.3" + mri "^1.2.0" + node-fetch-native "^1.6.4" + ofetch "^1.3.4" + ufo "^1.5.4" + +untun@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/untun/-/untun-0.1.3.tgz#5d10dee37a3a5737ff03d158be877dae0a0e58a6" + integrity sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ== + dependencies: + citty "^0.1.5" + consola "^3.2.3" + pathe "^1.1.1" + +update-browserslist-db@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" + integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +uqr@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz#5c6cd5dcff9581f9bb35b982cb89e2c483a41d7d" + integrity sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== + +use-sync-external-store@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82" + integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ== + +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +use-sync-external-store@^1.2.0: + version "1.2.2" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + +utf-8-validate@^5.0.2: + version "5.0.10" + resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" + integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== + dependencies: + node-gyp-build "^4.3.0" + +utf8@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.12.4, util@^0.12.5: + version "0.12.5" + resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + +uuid@^3.3.2, uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +uuid@^9.0.0, uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0: + version "2.4.0" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz#cdada8bec61e15865f05d097c5f4fd30e94dc128" + integrity sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +valtio@1.11.2: + version "1.11.2" + resolved "https://registry.npmjs.org/valtio/-/valtio-1.11.2.tgz#b8049c02dfe65620635d23ebae9121a741bb6530" + integrity sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw== + dependencies: + proxy-compare "2.5.1" + use-sync-external-store "1.2.0" + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vconsole@^3.14.6, vconsole@^3.15.1: + version "3.15.1" + resolved "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz#569a8ab15f353259527bbcf004f02946b4482cff" + integrity sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g== + dependencies: + "@babel/runtime" "^7.17.2" + copy-text-to-clipboard "^3.0.1" + core-js "^3.11.0" + mutation-observer "^1.0.3" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +viem@2.23.2, viem@2.29.x, viem@^2.1.1: + version "2.29.3" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.29.3.tgz#8cc703758de81b351da8cb84570809103da171cf" + integrity sha512-9D/nO+4S3Kk0P8vh6yXUA8OJ0mli9nzoY22qyh8iYHf1Q0GIejUnyq0QGjoDbkTcVzCVD5iA9KBkSxw9Tp3vUg== + dependencies: + "@noble/curves" "1.8.2" + "@noble/hashes" "1.7.2" + "@scure/bip32" "1.6.2" + "@scure/bip39" "1.5.4" + abitype "1.0.8" + isows "1.0.6" + ox "0.6.9" + ws "8.18.1" + +vite-node@3.0.5: + version "3.0.5" + resolved "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz#6a0d06f7a4bdaae6ddcdedc12d910d886cf7d62f" + integrity sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A== + dependencies: + cac "^6.7.14" + debug "^4.4.0" + es-module-lexer "^1.6.0" + pathe "^2.0.2" + vite "^5.0.0 || ^6.0.0" + +vite-tsconfig-paths@^5.1.4: + version "5.1.4" + resolved "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz#d9a71106a7ff2c1c840c6f1708042f76a9212ed4" + integrity sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w== + dependencies: + debug "^4.1.1" + globrex "^0.1.2" + tsconfck "^3.0.3" + +"vite@^5.0.0 || ^6.0.0": + version "6.1.0" + resolved "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz#00a4e99a23751af98a2e4701c65ba89ce23858a6" + integrity sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ== + dependencies: + esbuild "^0.24.2" + postcss "^8.5.1" + rollup "^4.30.1" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^3.0.5: + version "3.0.5" + resolved "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz#a9a3fa1203d85869c9ba66f3ea990b72d00ddeb0" + integrity sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q== + dependencies: + "@vitest/expect" "3.0.5" + "@vitest/mocker" "3.0.5" + "@vitest/pretty-format" "^3.0.5" + "@vitest/runner" "3.0.5" + "@vitest/snapshot" "3.0.5" + "@vitest/spy" "3.0.5" + "@vitest/utils" "3.0.5" + chai "^5.1.2" + debug "^4.4.0" + expect-type "^1.1.0" + magic-string "^0.30.17" + pathe "^2.0.2" + std-env "^3.8.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinypool "^1.0.2" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0" + vite-node "3.0.5" + why-is-node-running "^2.3.0" + +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + +wagmi@2.11.2: + version "2.11.2" + resolved "https://registry.npmjs.org/wagmi/-/wagmi-2.11.2.tgz#8d508c2f7ed5f5470754a21cd4deebfa338ac5ac" + integrity sha512-yHbeI2HNo7pPGToo4ib3lKSQDfprp+flV/V8T66nxbTne0fHcNtbCiny1xe9kAE44VNFdnABrUk8d83CMC7+QA== + dependencies: + "@wagmi/connectors" "5.0.26" + "@wagmi/core" "2.12.2" + use-sync-external-store "1.2.0" + +web3-bzz@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.10.4.tgz#dcc787970767d9004c73d11d0eeef774ce16b880" + integrity sha512-ZZ/X4sJ0Uh2teU9lAGNS8EjveEppoHNQiKlOXAjedsrdWuaMErBPdLQjXfcrYvN6WM6Su9PMsAxf3FXXZ+HwQw== + dependencies: + "@types/node" "^12.12.6" + got "12.1.0" + swarm-js "^0.1.40" + +web3-core-helpers@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.10.4.tgz#bd2b4140df2016d5dd3bb2b925fc29ad8678677c" + integrity sha512-r+L5ylA17JlD1vwS8rjhWr0qg7zVoVMDvWhajWA5r5+USdh91jRUYosp19Kd1m2vE034v7Dfqe1xYRoH2zvG0g== + dependencies: + web3-eth-iban "1.10.4" + web3-utils "1.10.4" + +web3-core-method@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.10.4.tgz#566b52f006d3cbb13b21b72b8d2108999bf5d6bf" + integrity sha512-uZTb7flr+Xl6LaDsyTeE2L1TylokCJwTDrIVfIfnrGmnwLc6bmTWCCrm71sSrQ0hqs6vp/MKbQYIYqUN0J8WyA== + dependencies: + "@ethersproject/transactions" "^5.6.2" + web3-core-helpers "1.10.4" + web3-core-promievent "1.10.4" + web3-core-subscriptions "1.10.4" + web3-utils "1.10.4" + +web3-core-promievent@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.10.4.tgz#629b970b7934430b03c5033c79f3bb3893027e22" + integrity sha512-2de5WnJQ72YcIhYwV/jHLc4/cWJnznuoGTJGD29ncFQHAfwW/MItHFSVKPPA5v8AhJe+r6y4Y12EKvZKjQVBvQ== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.10.4.tgz#eb1f147e6b9df84e3a37e602162f8925bdb4bb9a" + integrity sha512-vqP6pKH8RrhT/2MoaU+DY/OsYK9h7HmEBNCdoMj+4ZwujQtw/Mq2JifjwsJ7gits7Q+HWJwx8q6WmQoVZAWugg== + dependencies: + util "^0.12.5" + web3-core-helpers "1.10.4" + web3-providers-http "1.10.4" + web3-providers-ipc "1.10.4" + web3-providers-ws "1.10.4" + +web3-core-subscriptions@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.10.4.tgz#2f4dcb404237e92802a563265d11a33934dc38e6" + integrity sha512-o0lSQo/N/f7/L76C0HV63+S54loXiE9fUPfHFcTtpJRQNDBVsSDdWRdePbWwR206XlsBqD5VHApck1//jEafTw== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.10.4" + +web3-core@1.10.4, web3-core@^1.7.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-core/-/web3-core-1.10.4.tgz#639de68b8b9871d2dc8892e0dd4e380cb1361a98" + integrity sha512-B6elffYm81MYZDTrat7aEhnhdtVE3lDBUZft16Z8awYMZYJDbnykEbJVS+l3mnA7AQTnSDr/1MjWofGDLBJPww== + dependencies: + "@types/bn.js" "^5.1.1" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-core-requestmanager "1.10.4" + web3-utils "1.10.4" + +web3-eth-abi@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz#16c19d0bde0aaf8c1a56cb7743a83156d148d798" + integrity sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ== + dependencies: + "@ethersproject/abi" "^5.6.3" + web3-utils "1.10.4" + +web3-eth-accounts@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.10.4.tgz#df30e85a7cd70e475f8cf52361befba408829e34" + integrity sha512-ysy5sVTg9snYS7tJjxVoQAH6DTOTkRGR8emEVCWNGLGiB9txj+qDvSeT0izjurS/g7D5xlMAgrEHLK1Vi6I3yg== + dependencies: + "@ethereumjs/common" "2.6.5" + "@ethereumjs/tx" "3.5.2" + "@ethereumjs/util" "^8.1.0" + eth-lib "0.2.8" + scrypt-js "^3.0.1" + uuid "^9.0.0" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-utils "1.10.4" + +web3-eth-contract@1.10.4, web3-eth-contract@^1.8.0: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.10.4.tgz#22d39f04e11d9ff4e726e8025a56d78e843a2c3d" + integrity sha512-Q8PfolOJ4eV9TvnTj1TGdZ4RarpSLmHnUnzVxZ/6/NiTfe4maJz99R0ISgwZkntLhLRtw0C7LRJuklzGYCNN3A== + dependencies: + "@types/bn.js" "^5.1.1" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-core-promievent "1.10.4" + web3-core-subscriptions "1.10.4" + web3-eth-abi "1.10.4" + web3-utils "1.10.4" + +web3-eth-ens@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.10.4.tgz#3d991adac52bc8e598f1f1b8528337fa6291004c" + integrity sha512-LLrvxuFeVooRVZ9e5T6OWKVflHPFgrVjJ/jtisRWcmI7KN/b64+D/wJzXqgmp6CNsMQcE7rpmf4CQmJCrTdsgg== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-promievent "1.10.4" + web3-eth-abi "1.10.4" + web3-eth-contract "1.10.4" + web3-utils "1.10.4" + +web3-eth-iban@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.10.4.tgz#bc61b4a1930d19b1df8762c606d669902558e54d" + integrity sha512-0gE5iNmOkmtBmbKH2aTodeompnNE8jEyvwFJ6s/AF6jkw9ky9Op9cqfzS56AYAbrqEFuClsqB/AoRves7LDELw== + dependencies: + bn.js "^5.2.1" + web3-utils "1.10.4" + +web3-eth-personal@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.10.4.tgz#e2ee920f47e84848288e03442659cdbb2c4deea2" + integrity sha512-BRa/hs6jU1hKHz+AC/YkM71RP3f0Yci1dPk4paOic53R4ZZG4MgwKRkJhgt3/GPuPliwS46f/i5A7fEGBT4F9w== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-net "1.10.4" + web3-utils "1.10.4" + +web3-eth@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-eth/-/web3-eth-1.10.4.tgz#3a908c635cb5d935bd30473e452f3bd7f2ee66a5" + integrity sha512-Sql2kYKmgt+T/cgvg7b9ce24uLS7xbFrxE4kuuor1zSCGrjhTJ5rRNG8gTJUkAJGKJc7KgnWmgW+cOfMBPUDSA== + dependencies: + web3-core "1.10.4" + web3-core-helpers "1.10.4" + web3-core-method "1.10.4" + web3-core-subscriptions "1.10.4" + web3-eth-abi "1.10.4" + web3-eth-accounts "1.10.4" + web3-eth-contract "1.10.4" + web3-eth-ens "1.10.4" + web3-eth-iban "1.10.4" + web3-eth-personal "1.10.4" + web3-net "1.10.4" + web3-utils "1.10.4" + +web3-net@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-net/-/web3-net-1.10.4.tgz#20e12c60e4477d4298979d8d5d66b9abf8e66a09" + integrity sha512-mKINnhOOnZ4koA+yV2OT5s5ztVjIx7IY9a03w6s+yao/BUn+Luuty0/keNemZxTr1E8Ehvtn28vbOtW7Ids+Ow== + dependencies: + web3-core "1.10.4" + web3-core-method "1.10.4" + web3-utils "1.10.4" + +web3-providers-http@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.10.4.tgz#ca7aa58aeaf8123500c24ffe0595896319f830e8" + integrity sha512-m2P5Idc8hdiO0l60O6DSCPw0kw64Zgi0pMjbEFRmxKIck2Py57RQMu4bxvkxJwkF06SlGaEQF8rFZBmuX7aagQ== + dependencies: + abortcontroller-polyfill "^1.7.5" + cross-fetch "^4.0.0" + es6-promise "^4.2.8" + web3-core-helpers "1.10.4" + +web3-providers-ipc@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.10.4.tgz#2e03437909e4e7771d646ff05518efae44b783c3" + integrity sha512-YRF/bpQk9z3WwjT+A6FI/GmWRCASgd+gC0si7f9zbBWLXjwzYAKG73bQBaFRAHex1hl4CVcM5WUMaQXf3Opeuw== + dependencies: + oboe "2.1.5" + web3-core-helpers "1.10.4" + +web3-providers-ws@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.10.4.tgz#55d0c3ba36c6a79d105f02e20a707eb3978e7f82" + integrity sha512-j3FBMifyuFFmUIPVQR4pj+t5ILhAexAui0opgcpu9R5LxQrLRUZxHSnU+YO25UycSOa/NAX8A+qkqZNpcFAlxA== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.10.4" + websocket "^1.0.32" + +web3-shh@1.10.4: + version "1.10.4" + resolved "https://registry.npmjs.org/web3-shh/-/web3-shh-1.10.4.tgz#9852d6f3d05678e31e49235a60fea10ca7a9e21d" + integrity sha512-cOH6iFFM71lCNwSQrC3niqDXagMqrdfFW85hC9PFUrAr3PUrIem8TNstTc3xna2bwZeWG6OBy99xSIhBvyIACw== + dependencies: + web3-core "1.10.4" + web3-core-method "1.10.4" + web3-core-subscriptions "1.10.4" + web3-net "1.10.4" + +web3-utils@1.10.4, web3-utils@^1.8.0: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" + integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== + dependencies: + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereum-cryptography "^2.1.2" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3@^1.7.4, web3@^1.8.0: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.4.tgz#5d5e59b976eaf758b060fe1a296da5fe87bdc79c" + integrity sha512-kgJvQZjkmjOEKimx/tJQsqWfRDPTTcBfYPa9XletxuHLpHcXdx67w8EFn5AW3eVxCutE9dTVHgGa9VYe8vgsEA== + dependencies: + web3-bzz "1.10.4" + web3-core "1.10.4" + web3-eth "1.10.4" + web3-eth-personal "1.10.4" + web3-net "1.10.4" + web3-shh "1.10.4" + web3-utils "1.10.4" + +webcrypto-core@^1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.8.1.tgz#09d5bd8a9c48e9fbcaf412e06b1ff1a57514ce86" + integrity sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A== + dependencies: + "@peculiar/asn1-schema" "^2.3.13" + "@peculiar/json-schema" "^1.1.12" + asn1js "^3.0.5" + pvtsutils "^1.3.5" + tslib "^2.7.0" + +webcrypto-shim@^0.1.4: + version "0.1.7" + resolved "https://registry.yarnpkg.com/webcrypto-shim/-/webcrypto-shim-0.1.7.tgz#da8be23061a0451cf23b424d4a9b61c10f091c12" + integrity sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg== + +"webextension-polyfill@>=0.10.0 <1.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.12.0.tgz#f62c57d2cd42524e9fbdcee494c034cae34a3d69" + integrity sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q== + +webextension-polyfill@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz#ccb28101c910ba8cf955f7e6a263e662d744dbb8" + integrity sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +webpack-bundle-analyzer@4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b" + integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^6.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +"webpack-sources@^2.0.0 || ^3.0.0": + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +websocket@^1.0.32: + version "1.0.35" + resolved "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz#374197207d7d4cc4c36cbf8a1bb886ee52a07885" + integrity sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.63" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-fetch@^3.4.1: + version "3.6.20" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-builtin-type@^1.1.3: + version "1.1.4" + resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz#592796260602fc3514a1b5ee7fa29319b72380c3" + integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w== + dependencies: + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.2" + which-typed-array "^1.1.15" + +which-collection@^1.0.1, which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@8.18.1: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.3.1, ws@^7.4.5, ws@^7.5.1, ws@~7.5.10: + version "7.5.10" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + +ws@~8.17.1: + version "8.17.1" + resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.2.1.tgz#4e73adc4f9cfec9cbd2157f73efdce3a5f108a93" + integrity sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw== + +xhr@^2.0.4, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest-ssl@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6" + integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q== + +xmlhttprequest-ssl@~2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" + integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== + +xmlhttprequest-ssl@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz#e9e8023b3f29ef34b97a859f584c5e6c61418e23" + integrity sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ== + +xmlhttprequest@^1.8.0: + version "1.8.0" + resolved "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA== + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" + integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yargs@^17.0.0, yargs@^17.5.1: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zen-observable-ts@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" + integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== + dependencies: + zen-observable "0.8.15" + +zen-observable@0.8.15: + version "0.8.15" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== + +zustand@4.4.1: + version "4.4.1" + resolved "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz#0cd3a3e4756f21811bd956418fdc686877e8b3b0" + integrity sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw== + dependencies: + use-sync-external-store "1.2.0"