diff --git a/.env-example b/.env-example index 804f52c1..eb230d48 100644 --- a/.env-example +++ b/.env-example @@ -1,13 +1,28 @@ +# - Core bundler environment variables - + BUNDLER_CONFIG_PASSPHRASE= -BUNDLER_NODE_PATH_INDEX=0 -BUNDLER_CHAIN_ID=80001 -BUNDLER_MIN_RELAYER_COUNT=5 -BUNDLER_MAX_RELAYER_COUNT=10 -BUNDLER_FUNDING_BALANCE_THRESHOLD=0.1 -BUNDLER_FUNDING_RELAYER_AMOUNT=0.2 -BUNDLER_SLACK_JSON='{"token": "","channel": ""}' -BUNDLER_PROVIDER_JSON='{"137": "", "42161": "", "56": "", "10": "", "43114": "", "8453": "", "80001": ""}' -BUNDLER_DATASOURCES_JSON= '{"mongoUrl": "", "redisUrl": ""}' -BUNDLER_QUEUE_URL="" -BUNDLER_IS_TRUSTWALLET_SETUP=true -BUNDLER_FALLBACK_PROVIDER_JSON='{"137": ["", ""], "42161": ["", ""], "56": ["", ""], "10": ["", ""], "43114": ["", ""], "8453": ["", ""], "80001": ["", ""]}' \ No newline at end of file +NODE_PATH_INDEX=0 +# πŸ”₯ make sure this is never development when deploying to production πŸ”₯ +NODE_ENV=development + +# - Smoke tests environment variables - + +# You can set the folloowing to a production deployment if you want to run smoke tests against the production environment +SMOKE_TEST_BUNDLER_HOSTNAME=https://bundler.biconomy.io + +# - E2E tests environment variables - + +# Following are the environment variables required for e2e tests, see the docstrings inside src/test/e2e/nativeTransfer.test.ts for more information +# Set this to the private key of the EOA account that will be used to derive the smart account +PRIVATE_KEY= + +# Set this to https://bundler.biconomy.io if you want to test against the production environment +BUNDLER_HOSTNAME=http://localhost:3000 + +# You don't have to set all of the paymasters, just the ones you want to test e2e +BASE_MAINNET_PAYMASTER_URL= +AVALANCHE_MAINNET_PAYMASTER_URL= +OPTIMISM_MAINNET_PAYMASTER_URL= +POLYGON_MAINNET_PAYMASTER_URL= +BSC_MAINNET_PAYMASTER_URL= +ARBITRUM_MAINNET_PAYMASTER_URL= diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index b512c09d..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -node_modules \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index ef1aa027..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "extends": [ - "airbnb-base", - "airbnb-typescript/base", - "prettier" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project" : "./tsconfig.json" - }, - "plugins": [ - "@typescript-eslint", - "prettier" - ], - "rules": { - "import/prefer-default-export": "off", - "semi": ["error", "always"], - "import/extensions": [ - "error", - "ignorePackages", - { - "js": "never", - "jsx": "never", - "ts": "never", - "tsx": "never" - } - ], - "import/no-dynamic-require": 0, - "no-underscore-dangle": "off", - "no-restricted-syntax": "off", - "comma-dangle": "off", - "@typescript-eslint/comma-dangle": "off", - "import/no-extraneous-dependencies": ["error", {"devDependencies": false, "optionalDependencies": false, "peerDependencies": false}] - }, - "ignorePatterns": ["**/*.hbs"] -} diff --git a/.github/workflows/container_img_build_push_gar.yaml b/.github/workflows/container_img_build_push_gar.yaml new file mode 100644 index 00000000..fecc036b --- /dev/null +++ b/.github/workflows/container_img_build_push_gar.yaml @@ -0,0 +1,111 @@ +# This is a reusable workflow +# https://docs.github.com/en/actions/using-workflows/reusing-workflows +# +--- +# yamllint disable rule:line-length +name: Container_Img_Build_Push_GAR +# yamllint disable-line rule:truthy +on: + workflow_call: + inputs: + + image: + required: true + type: string + + dockerfile: + required: true + type: string + push: + required: true + type: boolean + + docker_context: + required: false + type: string + + gcp_project_number: + required: true + type: string + + gcp_pool_id: + required: true + type: string + + gcp_provider_id: + required: true + type: string + + gcp_service_account: + required: true + type: string + + gcp_registry: + required: true + type: string + +jobs: + # To avoid packing and passing artifacts from one job to another, we will + # build and test in the same github job. + container_image_build_push: + runs-on: ubuntu-latest + # Allow the job to fetch a GitHub ID token + # !!! IMPORTANT set "permissions:" section in the calling workflow job + # same like lines below + # permissions: + # id-token: write + # contents: read + # https://github.com/marketplace/actions/build-and-push-docker-images + # Checks out the repository source code with ref/SHA commit that trigger the workflow as HEAD. + steps: + - name: Checkout + uses: actions/checkout@v4 + + # https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines#authenticate + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v2' + with: + token_format: access_token + workload_identity_provider: "projects/${{inputs.gcp_project_number}}/locations/global/workloadIdentityPools/${{inputs.gcp_pool_id}}/providers/${{inputs.gcp_provider_id}}" + service_account: ${{ inputs.gcp_service_account }} + + - name: 'Login to Google Artifact Registry' + uses: docker/login-action@v3 + with: + # registry: -docker.pkg.dev + registry: ${{ inputs.gcp_registry }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + # Allow to extract the metadata information of the events and reference. + # In this step we configure the tags and labels for Docker image build and push + - name: 'Docker metadata' + id: 'metadata' + uses: docker/metadata-action@v5.0.0 + with: + images: ${{ inputs.image }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern=v{{version}} + type=raw,value=latest,enable=${{ github.ref_type != 'tag' }} + type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }} + # type=raw,value=foo + + - name: Build and push Docker images + uses: docker/build-push-action@v5.0.0 + with: + # context: . + context: ${{ inputs.docker_context == '' && '.' || inputs.docker_context }} + # push: ${{ github.event.base_ref == 'refs/heads/main' && github.ref_type == 'tag' && !startsWith(github.ref, 'refs/tags/v0.')}} + file: ${{ inputs.dockerfile }} + push: ${{ inputs.push }} + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} + + all: + # This job ensures that all jobs above are successful. + needs: [container_image_build_push] + runs-on: ubuntu-latest + steps: + - run: echo Success diff --git a/.github/workflows/deploy_to_gke.yaml b/.github/workflows/deploy_to_gke.yaml new file mode 100644 index 00000000..2162f819 --- /dev/null +++ b/.github/workflows/deploy_to_gke.yaml @@ -0,0 +1,114 @@ +--- +# yamllint disable rule:line-length +name: Setup_Connection_GKE_Cluster +# yamllint disable-line rule:truthy +on: + workflow_call: + inputs: + environment: + required: false + type: string + gcp_project_number: + required: true + type: string + gcp_project_id: + required: true + type: string + gcp_bastion: + required: false + type: string + gcp_bastion_zone: + required: true + type: string + gcp_pool_id: + required: true + type: string + gcp_provider_id: + required: true + type: string + gcp_service_account: + required: true + type: string + gcp_cluster_name: + required: true + type: string + gcp_cluster_location: + required: true + type: string + use_internal_ip: + required: true + type: boolean + deploy_command: + required: true + type: string + +jobs: + # To avoid packing and passing artifacts from one job to another, we will + # build and test in the same github job. + auth_gke: + environment: ${{ inputs.environment }} + runs-on: ubuntu-latest + # Allow the job to fetch a GitHub ID token + # !!! IMPORTANT set "permissions:" section in the calling workflow job + # same like lines below + # permissions: + # id-token: write + # contents: read + + # https://github.com/marketplace/actions/build-and-push-docker-images + # Checks out the repository source code with ref/SHA commit that trigger the workflow as HEAD. + steps: + - name: Checkout + uses: actions/checkout@v4 + + # https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines#authenticate + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v2' + with: + token_format: access_token + workload_identity_provider: "projects/${{inputs.gcp_project_number}}/locations/global/workloadIdentityPools/${{inputs.gcp_pool_id}}/providers/${{inputs.gcp_provider_id}}" + service_account: ${{ inputs.gcp_service_account }} + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v2' + with: + version: '>= 363.0.0' + + - name: 'Use gcloud CLI to open ssh tunnel' + run: | + gcloud info + # TODO: look into https://github.com/google-github-actions/ssh-compute + if [[ -n "${{ inputs.gcp_bastion }}" ]] ; then + gcloud compute ssh ${{ inputs.gcp_bastion }} \ + --project ${{ inputs.gcp_project_id }} \ + --zone ${{ inputs.gcp_bastion_zone }}\ + -- -L8888:127.0.0.1:8888 -f tail -f /dev/null + else + echo "INFO: no need for a bastion tunnel" + fi + + - id: 'get-gke-cluster-credentilas' + uses: 'google-github-actions/get-gke-credentials@v2' + with: + cluster_name: ${{ inputs.gcp_cluster_name }} + location: ${{ inputs.gcp_cluster_location }} + project_id: ${{ inputs.gcp_project_id }} + use_internal_ip: ${{ inputs.use_internal_ip }} + # The KUBECONFIG env var is automatically exported and picked up by kubectl. + - id: 'set-proxy' + run: | + if [[ -n "${{ inputs.gcp_bastion }}" ]] ; then + echo kubectl config set "clusters.${{inputs.gcp_cluster_name}}.proxy-url" "http://localhost:8888" + kubectl config set "clusters.${{inputs.gcp_cluster_name}}.proxy-url" "http://localhost:8888" + kubectl get nodes + else + echo "INFO: No proxy-url is needed" + kubectl get nodes + fi + - id: 'setup-helm' + uses: azure/setup-helm@v4 + - name: deploy with helm + run: | + echo ${{ inputs.deploy_command }} + eval '${{ inputs.deploy_command }}' diff --git a/.github/workflows/merge_on_master_tw.yaml b/.github/workflows/merge_on_master_tw.yaml index 4c2ea8c8..3766323a 100644 --- a/.github/workflows/merge_on_master_tw.yaml +++ b/.github/workflows/merge_on_master_tw.yaml @@ -49,7 +49,7 @@ jobs: - us-docker.pkg.dev/prj-biconomy-prod-001/bundler/bundler # LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE # {owner}/{repo}/.github/workflows/{filename}@{ref} - uses: bcnmy/devops/.github/workflows/container_img_build_push_gar.yaml@master + uses: bcnmy/bundler/.github/workflows/container_img_build_push_gar.yaml@master with: image: ${{ matrix.image }} dockerfile: Dockerfile.helm diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 0b42d307..880aa721 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -44,21 +44,18 @@ jobs: strategy: matrix: image: - - us-docker.pkg.dev/biconomy-prod/bundler/trustwallet - - us-docker.pkg.dev/prj-biconomy-prod-001/bundler/bundler + - us-west1-docker.pkg.dev/artifact-registry-1191/bundler/bundler # LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE # {owner}/{repo}/.github/workflows/{filename}@{ref} - uses: bcnmy/devops/.github/workflows/container_img_build_push_gar.yaml@master + uses: bcnmy/bundler/.github/workflows/container_img_build_push_gar.yaml@master with: image: ${{ matrix.image }} - dockerfile: Dockerfile.helm + dockerfile: Dockerfile + push: false # GCP project where the identity provider is - # gcloud projects describe prj-workload-identity-001 - gcp_project_number: '766873424314' - gcp_pool_id: 'pool-id-github-actions' - # gcp_provider_id: 'ga-GITHUB_REPO_NAME' - gcp_provider_id: 'ga-bundler' + gcp_project_number: '334188102066' + gcp_pool_id: 'github-pool' + gcp_provider_id: 'github-actions-wif-provider' # LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE - gcp_registry: 'us-docker.pkg.dev/biconomy-prod/bundler/trustwallet' - gcp_service_account: 'sa-bundler@prj-workload-identity-001.iam.gserviceaccount.com' - # + gcp_registry: 'us-west1-docker.pkg.dev/artifact-registry-1191/bundler/bundler' + gcp_service_account: 'sa-bundler@workload-identity-e697.iam.gserviceaccount.com' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a5e22216..504fb87f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,7 +7,7 @@ name: Version_Release on: push: tags: - - v0.** + - v1.** jobs: js_build_test: runs-on: ubuntu-latest @@ -47,79 +47,68 @@ jobs: strategy: matrix: image: - - us-docker.pkg.dev/biconomy-prod/bundler/trustwallet - - us-docker.pkg.dev/prj-biconomy-prod-001/bundler/bundler + - us-west1-docker.pkg.dev/artifact-registry-1191/bundler/bundler # LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE # {owner}/{repo}/.github/workflows/{filename}@{ref} - uses: bcnmy/devops/.github/workflows/container_img_build_push_gar.yaml@master + uses: bcnmy/bundler/.github/workflows/container_img_build_push_gar.yaml@master with: image: ${{ matrix.image }} - dockerfile: Dockerfile.helm + dockerfile: Dockerfile + push: true # GCP project where the identity provider is - # gcloud projects describe prj-workload-identity-001 - gcp_project_number: '766873424314' - gcp_pool_id: 'pool-id-github-actions' - # gcp_provider_id: 'ga-GITHUB_REPO_NAME' - gcp_provider_id: 'ga-bundler' + gcp_project_number: '334188102066' + gcp_pool_id: 'github-pool' + gcp_provider_id: 'github-actions-wif-provider' # LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE - gcp_registry: 'us-docker.pkg.dev/biconomy-prod/bundler/trustwallet' - gcp_service_account: 'sa-bundler@prj-workload-identity-001.iam.gserviceaccount.com' + gcp_registry: 'us-west1-docker.pkg.dev/artifact-registry-1191/bundler/bundler' + gcp_service_account: 'sa-bundler@workload-identity-e697.iam.gserviceaccount.com' - deploy_tw_staging: + # deploy_staging: + # needs: [container_img_build_push_gar] + # # Allow the job to fetch a GitHub ID token + # # runs-on: ubuntu-latest + # permissions: + # id-token: write + # contents: read + # uses: bcnmy/bundler/.github/workflows/deploy_to_gke.yaml@master + # with: + # # GCP project where the identity provider is + # # gcloud projects describe prj-workload-identity-001 + # gcp_project_number: '334188102066' + # gcp_project_id: 'biconomy-staging' + # gcp_bastion: '' + # gcp_bastion_zone: '' + # gcp_pool_id: 'github-pool' + # gcp_provider_id: 'github-actions-wif-provider' + # # SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT}@${PROJECT}.iam.gserviceaccount.com" + # gcp_service_account: 'sa-bundler@workload-identity-e697.iam.gserviceaccount.com' + # gcp_cluster_name: 'biconomy-staging' + # gcp_cluster_location: 'us-east1-b' + # use_internal_ip: false + # # deploy_command: 'echo IMG_VERSION is ${GITHUB_REF:10}' # for example extracts v0.0.5 from "refs/tags/v0.0.5" + # deploy_command: './k8s/deploy.sh 1 staging staging-sdk-relayer sdk-staging ${GITHUB_REF:10}' + + + deploy_prod: needs: [container_img_build_push_gar] # Allow the job to fetch a GitHub ID token # runs-on: ubuntu-latest permissions: id-token: write contents: read - uses: bcnmy/devops/.github/workflows/deploy_to_gke.yaml@master + uses: bcnmy/bundler/.github/workflows/deploy_to_gke.yaml@master with: + environment: 'prod' # GCP project where the identity provider is - # gcloud projects describe prj-workload-identity-001 - gcp_project_number: '766873424314' + gcp_project_number: '334188102066' gcp_project_id: 'biconomy-prod' - gcp_bastion: 'bastion02' - gcp_bastion_zone: 'us-east1-b' - gcp_pool_id: 'pool-id-github-actions' - # created by devops/gcp/github-actions/configure_workload_identity_federation_with_github_actions_pipelines.sh - # gcp_provider_id: 'ga-GITHUB_REPO_NAME' - gcp_provider_id: 'ga-bundler' + gcp_bastion: '' + gcp_bastion_zone: '' + gcp_pool_id: 'github-pool' + gcp_provider_id: 'github-actions-wif-provider' # SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT}@${PROJECT}.iam.gserviceaccount.com" - gcp_service_account: 'sa-bundler@prj-workload-identity-001.iam.gserviceaccount.com' - gcp_cluster_name: 'dedicated-bundler' + gcp_service_account: 'sa-bundler@workload-identity-e697.iam.gserviceaccount.com' + gcp_cluster_name: 'biconomy-prod' gcp_cluster_location: 'us-east1' - use_internal_ip: true - # https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables - # deploy_command: 'helm ls --all-namespaces' - # deploy_command: 'echo IMG_VERSION is ${GITHUB_REF:10}' # for example extracts v0.0.5 from "refs/tags/v0.0.5" - deploy_command: './install-bundler/bundler-update-release.sh bundler-tw-staging.cfg ${GITHUB_REF:10}' - - deploy_tw_prod: - needs: [deploy_tw_staging] - # environment: tw-prod - # Allow the job to fetch a GitHub ID token - # runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - uses: bcnmy/devops/.github/workflows/deploy_to_gke.yaml@master - with: - environment: 'tw-prod' - # GCP project where the identity provider is - # gcloud projects describe prj-workload-identity-001 - gcp_project_number: '766873424314' - # GCP project ID where the workload will be deployed - gcp_project_id: 'prj-biconomy-prod-001' - gcp_bastion: 'bastion02' - gcp_bastion_zone: 'us-central1-a' - gcp_pool_id: 'pool-id-github-actions' - # created by devops/gcp/github-actions/configure_workload_identity_federation_with_github_actions_pipelines.sh - # gcp_provider_id: 'ga-GITHUB_REPO_NAME' - gcp_provider_id: 'ga-bundler' - # SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT}@${PROJECT}.iam.gserviceaccount.com" - gcp_service_account: 'sa-bundler@prj-workload-identity-001.iam.gserviceaccount.com' - gcp_cluster_name: 'trustwallet' - gcp_cluster_location: 'us-central1' - use_internal_ip: true - deploy_command: './install-bundler/bundler-update-release.sh bundler-tw-production.cfg ${GITHUB_REF:10}' - # deploy_command: 'helm ls --all-namespaces' + use_internal_ip: false + deploy_command: './k8s/deploy.sh 2 prod prod-sdk-relayer sdk-prod ${GITHUB_REF:10}' diff --git a/.gitignore b/.gitignore index 626f99c5..23340766 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,9 @@ dist/ *.code-workspace config/development.json config/production.json + +# Random scripts that we shouldn't commit +dont-commit/ + +# Cursor rules +.cursorrules diff --git a/.husky/pre-commit b/.husky/pre-commit index a212e935..15259826 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,2 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" - -yarn run format diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ba27f535 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,49 @@ +#### 2024-10-25 + +##### Build System / Dependencies + +* **package.json:** add commitizen and conventional changelogs (950a0582) + +##### Chores + +* **EPv0.7.0:** :rocket: Allow Base Mainnet transactions (6d224305) +* **admin:** :wastebasket: cleanup (bead9291) +* remove unused weight (bac25c32) +* add empty values.yaml to run "helm lint" (4cfb467a) +* set CPU request to 2CPU, remove CPU limit (df39dbda) + +##### New Features + +* **admin:** :sparkles: Cancel relayer transaction admin API (8297ad2f) +* **integrations:** + * :sparkles: Lisk Mainnet integration (5cabc50a) + * :sparkles: Metal L2 Mainnet & Lisk Testnet (09254289) + * :sparkles: Metal L2 Testnet integration (e11b5637) + * :sparkles: Kakarot Sepolia Testnet integration (0d9042be) + * :sparkles: Boba Mainnet integration (eac054d4) + * :sparkles: Boba Sepolia Testnet integration (2b1c3dc8) + * :sparkles: 5irechain mainnet integration (e691f387) +* **config:** :poop: allow disabling fee validation for specific chains requested by TW (79fe70f1) +* add antiAffinity to deploy sts in separate ndoes (4e0f0622) +* enable-tolerations-affinity (cb472aef) + +##### Bug Fixes + +* **transaction-service:** :bug: Fix transactions stuck in Linea mempool (ec400d4d) +* make logging async (6ac59160) +* remove podAffinityTerm (d17a8d0b) +* wrap topologyKey value (c5f69cc3) +* comment out weight in requiredDuringSchedulingIgnoredDuringExecution (f68fc086) +* handle nil case for CPU limit (22c245bd) +* **integrations:** + * :bug: Increase Sei Mainnet funding amount (e4f8fa18) + * :bug: Fix 5irechain integration parameters (1c859618) +* **admin:** :bug: Make healthchecks less sensitive to balance changes (27e8559a) +* **tw:** :bug: Fix opBNB baseFeePerGas returning 0 (151f27ca) + +##### Other Changes + +* instead of @ for image tag (e4bfa49e) +* bcnmy/bundler into fix/cancel-stuck-transactions (be3ac232) +* EntryPoint v0.7 support" (4c3010f7) +* EntryPoint v0.7 support (c05eb936) diff --git a/Dockerfile b/Dockerfile index 5d6820a0..0e25a49f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:21.6-bookworm as builder +FROM --platform=linux/amd64 node:21.6-bookworm as builder # Install dependencies @@ -21,7 +21,7 @@ COPY . /bundler RUN yarn run build # Second stage -FROM node:21.6-bookworm +FROM --platform=linux/amd64 node:21.6-bookworm # Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. # NOTE: See https://github.com/hexops/dockerfile#is-tini-still-required-in-2020-i-thought-docker-added-it-natively diff --git a/Dockerfile.dev b/Dockerfile.dev index 13c07968..614c625e 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:21.6-bookworm as builder +FROM --platform=linux/amd64 node:21.6-bookworm AS builder # Install dependencies @@ -15,17 +15,17 @@ COPY package.json yarn.lock ./ RUN yarn install # Copy the rest of the files -COPY . . +# COPY . . # Second stage -FROM node:21.6-bookworm +FROM --platform=linux/amd64 node:21.6-bookworm # Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. # NOTE: See https://github.com/hexops/dockerfile#is-tini-still-required-in-2020-i-thought-docker-added-it-natively RUN apt-get update && apt-get install -y \ - tini \ - && rm -rf /var/lib/apt/lists/* + tini \ + && rm -rf /var/lib/apt/lists/* # Expose the port diff --git a/Dockerfile.helm b/Dockerfile.helm index bab11c59..f5692ee2 100644 --- a/Dockerfile.helm +++ b/Dockerfile.helm @@ -1,4 +1,4 @@ -FROM node:21.6-bookworm as builder +FROM --platform=linux/amd64 node:21.6-bookworm as builder # Install dependencies @@ -21,7 +21,7 @@ COPY . /bundler RUN yarn run build # Second stage -FROM node:21.6-bookworm +FROM --platform=linux/amd64 node:21.6-bookworm # Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. # NOTE: See https://github.com/hexops/dockerfile#is-tini-still-required-in-2020-i-thought-docker-added-it-natively diff --git a/README.md b/README.md index 4e7a2df3..f934140b 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,18 @@ # Bundler +[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) Biconomy ERC-4337 bundler-as-a-service implementation. ## Workflow best practices -This project has two _important_ branches: - - `master`: Production branch. This is code that's currently in production. -- `staging`: Staging branch. This represents what will be deployed to staging and included in the next release after it's been tested. - -To write quality commit messages we (try to) follow the [Conventional Commits Standard](https://www.conventionalcommits.org/en/v1.0.0/). -## Local Development Environment +To write quality commit messages we follow the [Conventional Commits Standard](https://www.conventionalcommits.org/en/v1.0.0/). -There are 2 ways to run the service and it's dependencies locally: +To commit use `yarn commit`. -1. **Manually**: follow the instructions in the [Bundler Local Setup](https://www.notion.so/biconomy/Local-setup-858695240f3a4c19b6c96cbb3f235b0a?pvs=4) Notion page. -2. **Docker (recommended)**: follow the instructions below. +## Local Development Environment -## Using the Docker development environment 1. Install Docker, Docker compose and `ts-node` on your local machine 2. Follow the [First setup instructions](src/config/CONFIG.md#πŸ‘ΆπŸ»-first-setup-instructions) to configure the Bundler before running it. 3. Run `docker compose up` and the server and all of it's dependencies should run in the current terminal session without throwing any errors. @@ -31,9 +25,8 @@ Other useful commands: - πŸ’‘ `docker compose build server`: run this whenever you add a new package to `package.json` or it won't be reflected in the container. - `docker compose build --no-cache `: build without cache if you suspect caching problems. +## Troubleshooting -### NOTES for DEVOPS -When adding a new chain integration make sure to add the new charin id -to all relevant config deployment files, in the `supportedNetworks` -e.g. `config/staging.json` or the bunlder will not start that network -even thouh is supported. +### ESLint is not working in VS Code +- Make sure your Microsoft ESLint extension is the latest version +- In your VS Code settings make sure `ESLint: Use Flat Config` is enabled diff --git a/babel.config.js b/babel.config.js index 1f3f8801..72bada46 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1 +1,2 @@ -module.exports = { presets: ['@babel/preset-env'] }; +// eslint-disable-next-line no-undef +module.exports = { presets: ["@babel/preset-env"] }; diff --git a/centrifugo_config.json b/centrifugo_config.json deleted file mode 100644 index 37982cc7..00000000 --- a/centrifugo_config.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "token_hmac_secret_key": "9edb7c38-0f55-4627-9bda-4cc050b5f6cb", - "admin_password": "admin_password", - "admin_secret": "admin_secret", - "api_key": "a4c3c3df-4294-4719-a6a6-0c3416d68466", - "allowed_origins": ["*"], - "debug": true, - "admin": true, - "log_level": "debug", - "client_anonymous": true, - "client_channel_limit": 512, - "namespaces": [ - { - "name": "relayer", - "publish": true, - "history_size": 10, - "history_ttl": "300s", - "recover": true, - "anonymous": false - }, - { - "name": "transaction", - "publish": true, - "history_size": 10, - "history_ttl": "300s", - "recover": true, - "anonymous": true - } - ] -} \ No newline at end of file diff --git a/config/default.json b/config/default.json index b88aa6c5..b4f436ec 100644 --- a/config/default.json +++ b/config/default.json @@ -1,21 +1,42 @@ { + "boot": { + "fundRelayers": true + }, + "health": { + "checkRelayers": false + }, + "config": { + "validate": true + }, "supportedNetworks": [ - 1, 137, 97, 56, 1101, 42161, 42170, 10, 43113, 43114, 8453, 59140, 59144, - 5000, 204, 5611, 88888, 592, 88882, 81, 1115, 1116, 169, 3441005, 91715, - 9980, 421614, 11155111, 84532, 168587773, 80085, 81457, 534351, 534352, - 56400, 7000, 11155420, 80002, 27827, 4653, 8101902, 666666666, 2442, 7001, - 5003, 100, 10200, 195, 196, 2810, 997, 713715, 3799 + 1, 137, 97, 56, 1101, 42161, 42170, 10, 43113, 43114, 8453, 59144, 5000, + 204, 5611, 88888, 592, 88882, 81, 1115, 1116, 169, 1715, 9980, 421614, + 11155111, 84532, 168587773, 81457, 534351, 534352, 56400, 7000, 11155420, + 80002, 27827, 4653, 8101902, 666666666, 2442, 7001, 5003, 100, 10200, 195, + 196, 2810, 997, 3799, 167009, 80084, 5845, 167000, 1328, 1329, 995, 28882, + 288, 920637907288165, 1740, 1750, 4202, 1135, 2818, 146, 57054, 10143 + ], + "supportedNetworksV07": [ + 1, 11155420, 84532, 8453, 10, 56, 42161, 137, 100, 80084, 168587773, 81457, + 11155111, 146, 57054, 10143, 80002, 421614, 97, 10200 ], "EIP1559SupportedNetworks": [ - 1, 137, 42161, 10, 43114, 43113, 8453, 59140, 59144, 204, 5611, 421614, - 11155111, 84532, 168587773, 80085, 81457, 42170, 169, 56400, 11155420, - 80002, 27827, 4653, 100, 10200, 997, 713715, 3799 + 1, 137, 42161, 10, 43114, 43113, 8453, 59144, 204, 5611, 421614, 11155111, + 84532, 168587773, 81457, 42170, 169, 56400, 11155420, 80002, 27827, 4653, + 100, 10200, 997, 3799, 167009, 80084, 5845, 167000, 1328, 1329, 1715, 995, + 28882, 288, 920637907288165, 1740, 1750, 4202, 1135, 2818, 146, 57054, 10143 ], "testnetNetworks": [ - 97, 43113, 59140, 5611, 81, 88882, 59144, 421614, 11155111, 1115, 3441005, - 84532, 168587773, 80085, 534351, 56400, 11155420, 80002, 7001, 2442, 5003, - 10200, 195, 2810, 997, 713715, 3799 + 97, 43113, 5611, 81, 88882, 59144, 421614, 11155111, 1115, 84532, 168587773, + 534351, 56400, 11155420, 80002, 7001, 2442, 5003, 10200, 195, 2810, 997, + 713715, 3799, 167009, 80084, 5845, 1328, 28882, 920637907288165, 1740, 4202, + 57054, 10143 ], + "blocknative": { + "supportedNetworks": [], + "confidenceLevel": 99 + }, + "supportsDebugTraceCall": [], "supportedTransactionType": { "1": ["BUNDLER"], "137": ["BUNDLER"], @@ -28,8 +49,8 @@ "43113": ["BUNDLER"], "43114": ["BUNDLER"], "8453": ["BUNDLER"], - "59140": ["BUNDLER"], "59144": ["BUNDLER"], + "59140": ["BUNDLER"], "1287": ["BUNDLER"], "1284": ["BUNDLER"], "5000": ["BUNDLER"], @@ -42,15 +63,13 @@ "1115": ["BUNDLER"], "1116": ["BUNDLER"], "169": ["BUNDLER"], - "3441005": ["BUNDLER"], - "91715": ["BUNDLER"], + "1715": ["BUNDLER"], "9980": ["BUNDLER"], "7116": ["BUNDLER"], "421614": ["BUNDLER"], "11155111": ["BUNDLER"], "168587773": ["BUNDLER"], "84532": ["BUNDLER"], - "80085": ["BUNDLER"], "81457": ["BUNDLER"], "534351": ["BUNDLER"], "534352": ["BUNDLER"], @@ -72,7 +91,25 @@ "2810": ["BUNDLER"], "997": ["BUNDLER"], "713715": ["BUNDLER"], - "3799": ["BUNDLER"] + "3799": ["BUNDLER"], + "167009": ["BUNDLER"], + "80084": ["BUNDLER"], + "5845": ["BUNDLER"], + "167000": ["BUNDLER"], + "1328": ["BUNDLER"], + "1329": ["BUNDLER"], + "995": ["BUNDLER"], + "28882": ["BUNDLER"], + "288": ["BUNDLER"], + "920637907288165": ["BUNDLER"], + "1740": ["BUNDLER"], + "1750": ["BUNDLER"], + "4202": ["BUNDLER"], + "1135": ["BUNDLER"], + "2818": ["BUNDLER"], + "146": ["BUNDLER"], + "57054": ["BUNDLER"], + "10143": ["BUNDLER"] }, "chains": { "providers": { @@ -111,8 +148,40 @@ "url": "https://rpc.ankr.com/blast_testnet_sepolia/0f2ac70eb2e86d935951e9d3874deeb44525123a1ce2a7cf609cf1f0a098d2d6", "type": "private" } + ], + "84532": [ + { + "url": "https://sepolia.base.org", + "type": "public" + } + ], + "920637907288165": [ + { + "url": "https://sepolia-rpc.kakarot.org", + "type": "public" + } + ], + "11155420": [ + { + "url": "https://sepolia.optimism.io", + "type": "public" + } + ], + "2818": [ + { + "url": "https://rpc.morphl2.io", + "type": "public" + } ] }, + "checkForReceiptInterval": { + "1": 1000, + "11155111": 1000 + }, + "checkReceiptTimeout": { + "1": 60000, + "11155111": 60000 + }, "currency": { "137": "MATIC", "97": "BNB", @@ -125,7 +194,6 @@ "43113": "AVAX", "43114": "AVAX", "8453": "ETH", - "59140": "ETH", "59144": "ETH", "5000": "MNT", "204": "BNB", @@ -139,13 +207,11 @@ "1115": "CORE", "1116": "CORE", "169": "ETH", - "3441005": "ETH", - "91715": "tcBNB", + "1715": "tcBNB", "9980": "BNB", "7116": "ETH", "421614": "ETH", "11155111": "ETH", - "80085": "BERA", "84532": "ETH", "168587773": "ETH", "81457": "ETH", @@ -167,9 +233,27 @@ "195": "OKB", "196": "OKB", "2810": "ETH", - "997": "5ire", + "997": "T5IRE", "713715": "SEI", - "3799": "tTNT" + "3799": "tTNT", + "167009": "ETH", + "80084": "ETH", + "5845": "TNT", + "167000": "ETH", + "1328": "SEI", + "1329": "SEI", + "995": "5IRE", + "28882": "ETH", + "288": "ETH", + "920637907288165": "ETH", + "1740": "ETH", + "1750": "ETH", + "4202": "ETH", + "1135": "ETH", + "2818": "ETH", + "146": "S", + "57054": "S", + "10143": "MON" }, "decimal": { "137": 18, @@ -183,7 +267,6 @@ "43113": 18, "43114": 18, "8453": 18, - "59140": 18, "59144": 18, "5000": 18, "204": 18, @@ -197,13 +280,11 @@ "1115": 18, "1116": 18, "169": 18, - "3441005": 18, - "91715": 18, + "1715": 18, "9980": 18, "7116": 18, "421614": 18, "11155111": 18, - "80085": 18, "84532": 18, "168587773": 18, "81457": 18, @@ -227,11 +308,29 @@ "2810": 18, "997": 18, "713715": 18, - "3799": 18 + "3799": 18, + "167009": 18, + "80084": 18, + "5845": 18, + "167000": 18, + "1328": 18, + "1329": 18, + "995": 18, + "28882": 18, + "288": 18, + "920637907288165": 18, + "1740": 18, + "1750": 18, + "4202": 18, + "1135": 18, + "2818": 18, + "146": 18, + "57054": 18, + "10143": 18 }, "retryTransactionInterval": { "137": 30000, - "1": 30000, + "1": 375000, "97": 30000, "56": 30000, "1101": 30000, @@ -241,7 +340,6 @@ "43113": 30000, "43114": 30000, "8453": 30000, - "59140": 30000, "59144": 30000, "1287": 30000, "1284": 30000, @@ -255,13 +353,11 @@ "1115": 30000, "1116": 30000, "169": 30000, - "3441005": 30000, - "91715": 30000, + "1715": 30000, "9980": 30000, "7116": 30000, "421614": 30000, "11155111": 30000, - "80085": 30000, "84532": 30000, "168587773": 30000, "81457": 30000, @@ -285,11 +381,29 @@ "2810": 30000, "997": 30000, "713715": 30000, - "3799": 30000 + "3799": 30000, + "167009": 30000, + "80084": 30000, + "5845": 30000, + "167000": 30000, + "1328": 30000, + "1329": 30000, + "995": 30000, + "28882": 30000, + "288": 30000, + "920637907288165": 30000, + "1740": 30000, + "1750": 30000, + "4202": 30000, + "1135": 30000, + "2818": 30000, + "146": 30000, + "57054": 30000, + "10143": 30000 }, "updateFrequencyInSeconds": { - "137": 10, - "1": 60, + "137": 3, + "1": 30, "97": 60, "56": 60, "1101": 60, @@ -298,8 +412,7 @@ "10": 60, "43113": 60, "43114": 60, - "8453": 60, - "59140": 60, + "8453": 10, "59144": 60, "5000": 86400, "204": 60, @@ -307,19 +420,17 @@ "1287": 60, "1284": 60, "592": 60, - "88888": 60, + "88888": 30, "81": 60, "88882": 60, "1115": 60, "1116": 60, "169": 60, - "3441005": 60, - "91715": 60, + "1715": 60, "9980": 60, "7116": 60, "421614": 60, - "11155111": 60, - "80085": 60, + "11155111": 30, "84532": 60, "168587773": 3600, "81457": 60, @@ -343,11 +454,29 @@ "2810": 60, "997": 60, "713715": 60, - "3799": 60 + "3799": 60, + "167009": 60, + "80084": 60, + "5845": 60, + "167000": 60, + "1328": 60, + "1329": 60, + "995": 60, + "28882": 60, + "288": 60, + "920637907288165": 60, + "1740": 60, + "1750": 60, + "4202": 60, + "1135": 60, + "2818": 60, + "146": 10, + "57054": 10, + "10143": 30 } }, "maxPriorityFeePerGasThresholdPercentage": 0.5, - "maxFeePerGasThresholdPercentage": 0.5, + "maxFeePerGasThresholdPercentage": 0.45, "preVerificationGasThresholdPercentage": 0.5, "relayerManagers": [ { @@ -370,7 +499,6 @@ "43113": 2, "43114": 100, "8453": 300, - "59140": 2, "59144": 20, "1287": 2, "1284": 2, @@ -378,19 +506,17 @@ "204": 2, "5611": 2, "592": 2, - "88888": 2, + "88888": 100, "81": 1, "88882": 1, "1115": 1, "1116": 15, "169": 1, - "3441005": 1, - "91715": 1, + "1715": 1, "9980": 1, "7116": 40, "421614": 5, - "11155111": 10, - "80085": 2, + "11155111": 2, "168587773": 20, "84532": 2, "81457": 20, @@ -409,16 +535,34 @@ "666666666": 4, "100": 50, "10200": 10, - "195": 2, - "196": 10, + "195": 20, + "196": 300, "2810": 2, "997": 2, "713715": 2, - "3799": 2 + "3799": 2, + "167009": 2, + "80084": 2, + "5845": 2, + "167000": 2, + "1328": 2, + "1329": 2, + "995": 2, + "28882": 2, + "288": 2, + "920637907288165": 2, + "1740": 2, + "1750": 2, + "4202": 2, + "1135": 2, + "2818": 2, + "146": 2, + "57054": 2, + "10143": 2 }, "maxRelayerCount": { "137": 400, - "1": 2, + "1": 5, "97": 7, "56": 30, "1101": 10, @@ -428,7 +572,6 @@ "43113": 10, "43114": 80, "8453": 350, - "59140": 10, "59144": 40, "1287": 10, "1284": 10, @@ -436,19 +579,17 @@ "204": 10, "5611": 10, "592": 10, - "88888": 10, + "88888": 200, "81": 5, "88882": 5, "1115": 5, "1116": 20, "169": 5, - "3441005": 5, - "91715": 5, + "1715": 5, "9980": 5, "7116": 100, "421614": 20, - "11155111": 20, - "80085": 7, + "11155111": 5, "84532": 7, "168587773": 30, "81457": 30, @@ -467,17 +608,35 @@ "666666666": 10, "100": 60, "10200": 20, - "195": 10, - "196": 20, + "195": 30, + "196": 400, "2810": 5, "997": 5, "713715": 5, - "3799": 5 + "3799": 5, + "167009": 5, + "80084": 5, + "5845": 5, + "167000": 5, + "1328": 5, + "1329": 5, + "995": 5, + "28882": 5, + "288": 5, + "920637907288165": 5, + "1740": 5, + "1750": 5, + "4202": 5, + "1135": 5, + "2818": 5, + "146": 5, + "57054": 5, + "10143": 5 }, "inactiveRelayerCountThreshold": { "137": 250, "97": 5, - "1": 2, + "1": 3, "56": 2, "1101": 5, "42161": 40, @@ -486,7 +645,6 @@ "43113": 5, "43114": 8, "8453": 280, - "59140": 5, "59144": 18, "1287": 5, "1284": 5, @@ -494,19 +652,17 @@ "204": 5, "5611": 5, "592": 5, - "88888": 5, + "88888": 40, "81": 5, "88882": 5, "1115": 5, "1116": 5, "169": 5, - "3441005": 5, - "91715": 5, + "1715": 5, "9980": 5, "7116": 60, "421614": 15, "11155111": 15, - "80085": 5, "168587773": 15, "84532": 5, "81457": 15, @@ -525,12 +681,30 @@ "666666666": 5, "100": 45, "10200": 8, - "195": 8, - "196": 8, + "195": 15, + "196": 180, "2810": 3, "997": 3, "713715": 3, - "3799": 3 + "3799": 3, + "167009": 3, + "80084": 3, + "5845": 3, + "167000": 3, + "1328": 3, + "1329": 3, + "995": 3, + "28882": 3, + "288": 3, + "920637907288165": 3, + "1740": 3, + "1750": 3, + "4202": 3, + "1135": 3, + "2818": 3, + "146": 2, + "57054": 2, + "10143": 2 }, "pendingTransactionCountThreshold": { "137": 15, @@ -544,7 +718,6 @@ "43113": 15, "43114": 15, "8453": 15, - "59140": 15, "59144": 15, "1287": 15, "1284": 15, @@ -558,13 +731,11 @@ "1115": 15, "1116": 15, "169": 15, - "3441005": 15, - "91715": 15, + "1715": 15, "9980": 15, "7116": 15, "421614": 15, "11155111": 15, - "80085": 15, "168587773": 15, "84532": 15, "81457": 15, @@ -588,12 +759,30 @@ "2810": 15, "997": 15, "713715": 15, - "3799": 15 + "3799": 15, + "167009": 15, + "80084": 15, + "5845": 15, + "167000": 15, + "1328": 15, + "1329": 15, + "995": 15, + "28882": 15, + "288": 15, + "920637907288165": 15, + "1740": 15, + "1750": 15, + "4202": 15, + "1135": 15, + "2818": 15, + "146": 15, + "57054": 15, + "10143": 15 }, "fundingRelayerAmount": { "137": 5, "97": 0.1, - "1": 1, + "1": 0.2, "56": 0.15, "1101": 0.2, "42161": 0.01, @@ -602,7 +791,6 @@ "43113": 1, "43114": 0.5, "8453": 0.01, - "59140": 0.1, "59144": 0.004, "1287": 1, "1284": 1, @@ -616,13 +804,11 @@ "1115": 0.4, "1116": 0.2, "169": 0.002, - "3441005": 0.002, - "91715": 0.05, + "1715": 0.05, "9980": 0.05, "7116": 0.01, "421614": 0.01, "11155111": 0.1, - "80085": 0.01, "168587773": 0.5, "84532": 0.015, "81457": 0.02, @@ -641,17 +827,35 @@ "666666666": 0.01, "100": 1, "10200": 0.05, - "195": 0.03, + "195": 0.1, "196": 0.05, "2810": 0.01, - "997": 0.01, - "713715": 0.1, - "3799": 0.1 + "997": 0.1, + "713715": 0.01, + "3799": 0.1, + "167009": 0.01, + "80084": 0.05, + "5845": 0.1, + "167000": 0.01, + "1328": 0.01, + "1329": 1, + "995": 0.1, + "28882": 0.01, + "288": 0.01, + "920637907288165": 0.005, + "1740": 0.01, + "1750": 0.01, + "4202": 0.01, + "1135": 0.01, + "2818": 0.01, + "146": 0.5, + "57054": 0.1, + "10143": 1 }, "fundingBalanceThreshold": { "137": 3, "97": 0.05, - "1": 0.5, + "1": 0.1, "56": 0.003, "1101": 0.01, "42161": 0.002, @@ -660,7 +864,6 @@ "43113": 0.1, "43114": 0.2, "8453": 0.008, - "59140": 0.01, "59144": 0.002, "1287": 0.1, "1284": 0.1, @@ -674,13 +877,11 @@ "1115": 0.1, "1116": 6, "169": 0.001, - "3441005": 0.001, - "91715": 0.02, + "1715": 0.02, "9980": 0.02, "7116": 0.005, "421614": 0.005, "11155111": 0.05, - "80085": 0.005, "168587773": 0.2, "84532": 0.0075, "81457": 0.01, @@ -699,12 +900,30 @@ "666666666": 0.005, "100": 0.8, "10200": 0.01, - "195": 0.01, + "195": 0.05, "196": 0.02, "2810": 0.005, - "997": 0.005, - "713715": 0.05, - "3799": 0.05 + "997": 0.05, + "713715": 0.005, + "3799": 0.05, + "167009": 0.005, + "80084": 0.025, + "5845": 0.05, + "167000": 0.005, + "1328": 0.005, + "1329": 0.5, + "995": 0.05, + "28882": 0.005, + "288": 0.005, + "920637907288165": 0.0025, + "1740": 0.005, + "1750": 0.005, + "4202": 0.005, + "1135": 0.005, + "2818": 0.005, + "146": 0.25, + "57054": 0.05, + "10143": 0.5 }, "newRelayerInstanceCount": { "137": 10, @@ -718,7 +937,6 @@ "43113": 2, "43114": 2, "8453": 2, - "59140": 2, "59144": 2, "1287": 2, "1284": 2, @@ -732,13 +950,11 @@ "1115": 2, "1116": 2, "169": 2, - "3441005": 2, - "91715": 2, + "1715": 2, "9980": 2, "7116": 2, "421614": 2, "11155111": 2, - "80085": 2, "168587773": 2, "84532": 2, "81457": 2, @@ -758,11 +974,29 @@ "100": 2, "10200": 2, "195": 2, - "196": 2, + "196": 10, "2810": 2, "997": 2, "713715": 2, - "3799": 2 + "3799": 2, + "167009": 2, + "80084": 2, + "5845": 2, + "167000": 2, + "1328": 2, + "1329": 2, + "995": 2, + "28882": 2, + "288": 2, + "920637907288165": 2, + "1740": 2, + "1750": 2, + "4202": 2, + "1135": 2, + "2818": 2, + "146": 2, + "57054": 2, + "10143": 2 } } ], @@ -782,7 +1016,8 @@ "insufficient funds for gas", "transaction would cause overdraft", "insufficient funds for transfer", - "insufficient funds for gas * price + value" + "insufficient funds for gas * price + value", + "transaction exceeds the balance of the account" ], "NONCE_TOO_LOW": [ "nonce too low", @@ -815,38 +1050,52 @@ "entryPointData": { "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789": { "supportedChainIds": [ - 1, 137, 97, 56, 1101, 42161, 10, 43114, 8453, 59140, 59144, 592, 81, - 204, 5611, 5000, 169, 3441005, 1115, 1116, 91715, 9980, 421614, - 11155111, 84532, 168587773, 43113, 80085, 81457, 42170, 534351, 534352, - 56400, 7000, 11155420, 80002, 27827, 7001, 2442, 4653, 5003, 8101902, - 666666666, 100, 10200, 195, 196, 2810, 997, 713715, 3799 + 1, 137, 97, 56, 1101, 42161, 10, 43114, 8453, 59144, 592, 81, 204, 5611, + 5000, 169, 1115, 1116, 1715, 9980, 421614, 11155111, 84532, 168587773, + 43113, 81457, 42170, 534351, 534352, 56400, 7000, 11155420, 80002, + 27827, 7001, 2442, 4653, 5003, 8101902, 666666666, 100, 10200, 195, 196, + 2810, 997, 713715, 3799, 167009, 80084, 5845, 167000, 1328, 1329, 995, + 28882, 288, 920637907288165, 1740, 1750, 4202, 1135, 2818 ] }, "0x00000061fefce24a79343c27127435286bb7a4e1": { "supportedChainIds": [88888, 88882] } }, + "entryPointV07Data": { + "0x0000000071727De22E5E9d8BAf0edAc6f37da032": { + "supportedChainIds": [ + 1, 11155420, 84532, 8453, 10, 56, 42161, 137, 100, 80084, 168587773, + 81457, 11155111, 146, 57054, 10143, 80002, 421614, 97, 10200 + ] + } + }, "zeroAddress": "0x0000000000000000000000000000000000000000", "l2Networks": [42161, 42170], "polygonZKEvmNetworks": [1101, 2442, 195, 196], "optimismNetworks": [ - 10, 8453, 204, 5611, 9980, 91715, 84532, 168587773, 11155420, 4653 + 10, 8453, 204, 5611, 9980, 1715, 84532, 168587773, 11155420, 4653, 28882, + 288, 1740, 1750, 4202, 1135 ], - "morphNetworks": [2810], - "lineaNetworks": [59140, 59144], + "morphNetworks": [2810, 2818], + "seiNetworks": [713715, 1328, 1329], + "lineaNetworks": [59144], "arbitrumNetworks": [42170, 42161, 421614, 8101902, 666666666], "astarNetworks": [81, 592], "mantleNetworks": [5000, 5003], "blastPvgValue": 700000000, "scrollNetworks": [534351, 534352], + "kakarotNetworks": [920637907288165], "networksNotSupportingEthCallStateOverrides": [ - 1101, 81, 592, 169, 3441005, 91715, 7116, 9980, 88882, 88888, 81457, - 666666666, 2442, 8101902, 7001, 196, 195 + 1101, 81, 592, 169, 1715, 7116, 9980, 88882, 88888, 81457, 666666666, 2442, + 8101902, 7001, 196, 195, 167009, 713715, 167000, 1328, 1329, 997, 995, + 28882, 288, 920637907288165 ], "trustWalletSupportedNetworks": [137, 56, 204, 42161, 10, 8453], "networksNotSupportingEthCallBytecodeStateOverrides": [ - 1, 59140, 59144, 84532, 421614, 168587773, 43113, 11155111, 1101, 88882, - 88888, 81457 + 1, 59144, 84532, 421614, 168587773, 43113, 11155111, 1101, 88882, 88888, + 81457, 167009, 713715, 167000, 1328, 1329, 997, 995, 28882, 288, + 920637907288165, 4202, 1135 ], "pvgMarkUp": { "1": 0.1, @@ -860,7 +1109,6 @@ "43114": 0.05, "43113": 0.05, "8453": 0.15, - "59140": 0.1, "59144": 0.1, "5000": 0.15, "204": 0.15, @@ -872,8 +1120,7 @@ "1115": 0.1, "1116": 0.1, "169": 0.1, - "3441005": 0.1, - "91715": 0.15, + "1715": 0.15, "9980": 0.15, "7116": 0.1, "421614": 0.1, @@ -894,11 +1141,20 @@ "100": 0.1, "10200": 0.1, "195": 0.1, - "196": 0.1 + "196": 0.1, + "995": 0.1, + "2818": 0.1 }, "aaDashboardBackend": { "url": "https://paymaster-dashboard-backend-v2.staging.biconomy.io/api/v2/bundlers/auth" }, + "flashbots": { + "rpcUrl": "https://rpc.flashbots.net/fast", + "statusUrl": "https://protect.flashbots.net/tx", + "getStatusIntervalDelay": 5000, + "maxBlockWait": 26, + "supportedNetworks": [] + }, "isTWSetup": false, "redisCluster": { "port": 6379, @@ -907,5 +1163,41 @@ "maxRedirections": 16, "retryDelayOnFailover": 100, "scaleReads": "master" + }, + "gasOverrides": { + "204": { + "baseFeePerGas": 1, + "preVerificationGas": 2172916 + } + }, + "disableFeeValidation": [], + "adminAddresses": [], + "callGasLimitMarkup": { + "100": 20000, + "88888": 20000, + "81457": 50000, + "146": 500000, + "57054": 500000, + "137": 30000 + }, + "hardcodedGasLimits": { + "84532": { + "verificationGasLimit": 2000000, + "callGasLimit": 2000000, + "preVerificationGas": 45000000 + } + }, + "networkService": { + "viemFallbackTransport": { + "rank": { + "interval": 60000, + "sampleCount": 5, + "timeout": 500, + "weights": { + "latency": 0.3, + "stability": 0.7 + } + } + } } } diff --git a/config/test.json b/config/test.json index 7343aa8f..ff7c7c3a 100644 --- a/config/test.json +++ b/config/test.json @@ -1,36 +1,12 @@ { - "supportedNetworks": [137], + "config": { + "validate": false + }, "relayer": { "nodePathIndex": 0 }, - "dataSources": { - "redisUrl": "test" - }, - "chains": { - "providers": { - "1": [ - { - "url": "https://thislinkdoesnotexist.rs", - "type": "private" - } - ], - "10": [ - { - "url": "https://thislinkdoesnotexist.rs", - "type": "private" - } - ], - "137": [ - { - "url": "https://thislinkdoesnotexist.rs", - "type": "private" - } - ] - } - }, - "EVMNetworkService": { - "waitForTransaction": { - "timeoutMs": 2000 - } + "clearStaleMessages": { + "supportedNetworks": [1], + "ttlSeconds": 1 } } diff --git a/docker-compose.yaml b/docker-compose.yaml index 0ad085bc..aeaa1a85 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,5 +1,3 @@ -version: "3.1" - volumes: redis-data: mongo-data: @@ -7,8 +5,8 @@ volumes: rabbitmq_data: services: - server: - container_name: api-service + bundler: + container_name: bundler env_file: .env ports: - "0.0.0.0:3000:3000" @@ -21,18 +19,19 @@ services: rabbitmq: condition: service_healthy restart: true - # centrifugo: - # condition: service_healthy - # restart: true build: context: . dockerfile: Dockerfile.dev restart: always volumes: - - .:/home/nonroot/bundler + - ./src:/home/nonroot/bundler/src + - ./config:/home/nonroot/bundler/config + - ./nodemon.json:/home/nonroot/bundler/nodemon.json + - ./config.json.enc:/home/nonroot/bundler/config.json.enc + - ./.env:/home/nonroot/bundler/.env redis: - image: redis:6.0 + image: redis:latest container_name: redis command: ["redis-server", "--appendonly", "yes"] ports: @@ -46,27 +45,6 @@ services: timeout: 20s retries: 5 - centrifugo: - container_name: centrifugo - image: centrifugo/centrifugo:v3.2.2 - volumes: - - ./centrifugo_config.json:/centrifugo/config.json - command: centrifugo -c config.json - - healthcheck: - test: ["CMD", "sh", "-c", "wget -nv -O - http://localhost:8000"] - interval: 3s - timeout: 3s - retries: 3 - - ulimits: - nofile: - soft: 65535 - hard: 65535 - - ports: - - "0.0.0.0:8000:8000" - mongo: image: mongo:latest container_name: mongo @@ -77,7 +55,7 @@ services: - mongo-data:/data/db rabbitmq: - image: heidiks/rabbitmq-delayed-message-exchange:3.8.3-management + image: heidiks/rabbitmq-delayed-message-exchange:3.13.0-management container_name: "rabbitmq" healthcheck: test: rabbitmq-diagnostics -q ping diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..ba8f277d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,11 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default [ + { files: ["**/*.{js,mjs,cjs,ts}"] }, + { languageOptions: { globals: globals.browser } }, + { ignores: ["dist/"] }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, +]; diff --git a/install-bundler/bundler/templates/statefulset.yaml b/install-bundler/bundler/templates/statefulset.yaml index f10eb891..72e01c5d 100644 --- a/install-bundler/bundler/templates/statefulset.yaml +++ b/install-bundler/bundler/templates/statefulset.yaml @@ -129,9 +129,10 @@ spec: startupProbe: httpGet: - path: /health + path: /admin/startup port: {{ .Values.port }} - failureThreshold: 40 + failureThreshold: 120 + successThreshold: 1 periodSeconds: 10 livenessProbe: diff --git a/install-bundler/configs/chains/prod-trust-wallet-chains.sh b/install-bundler/configs/chains/prod-trust-wallet-chains.sh index 9f2c7c77..bc079ef2 100644 --- a/install-bundler/configs/chains/prod-trust-wallet-chains.sh +++ b/install-bundler/configs/chains/prod-trust-wallet-chains.sh @@ -17,13 +17,13 @@ # [maxReplica]=6 # ) -declare -A chain_polygon=( - [name]='chain-137' - [chainId]="137" - [autoScalingThreshholdHTTPRequestsPerMingute]=1000 - [autoScalingThreshholdCPU]=800m - [minReplica]=6 - [maxReplica]=6) +# declare -A chain_polygon=( +# [name]='chain-137' +# [chainId]="137" +# [autoScalingThreshholdHTTPRequestsPerMingute]=1000 +# [autoScalingThreshholdCPU]=800m +# [minReplica]=6 +# [maxReplica]=6) # declare -A chain_arbitrum=( @@ -45,14 +45,14 @@ declare -A chain_polygon=( # [maxReplica]=3 # ) -# declare -A chain_opBNB=( -# [name]='chain-204' -# [chainId]="204" -# [autoScalingThreshholdHTTPRequestsPerMinute]=1000 -# [autoScalingThreshholdCPU]=900m -# [minReplica]=3 -# [maxReplica]=3 -# ) +declare -A chain_opBNB=( + [name]='chain-204' + [chainId]="204" + [autoScalingThreshholdHTTPRequestsPerMinute]=1000 + [autoScalingThreshholdCPU]=900m + [minReplica]=3 + [maxReplica]=3 +) # declare -A chain_base=( # [name]='chain-8453' diff --git a/install-bundler/configs/chains/staging-trust-wallet-chains.sh b/install-bundler/configs/chains/staging-trust-wallet-chains.sh index a7087db4..b161cde6 100644 --- a/install-bundler/configs/chains/staging-trust-wallet-chains.sh +++ b/install-bundler/configs/chains/staging-trust-wallet-chains.sh @@ -1,9 +1,8 @@ -# declare -A chain_mumbai=( -# [name]='chain-80001' -# [chainId]="80001" -# [autoScalingThreshholdHTTPRequestsPerMinute]=10000 -# [autoScalingThreshholdCPU]=2500m -# [minReplica]=2 -# [maxReplica]=2 -# ) \ No newline at end of file +declare -A chain_polygon=( + [name]='chain-137' + [chainId]="137" + [autoScalingThreshholdHTTPRequestsPerMinute]=1000 + [autoScalingThreshholdCPU]=800m + [minReplica]=1 + [maxReplica]=1) \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 9476c5f0..ef0eba62 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-undef module.exports = { preset: "ts-jest", testTimeout: 600000, diff --git a/k8s/common/templates/secret.yaml b/k8s/common/templates/secret.yaml deleted file mode 100644 index 3391194f..00000000 --- a/k8s/common/templates/secret.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: kubernetes-client.io/v1 -kind: ExternalSecret -metadata: - name: {{ .Values.relayer.name }}-passphrase - namespace: {{ .Values.namespace }} -spec: - backendType: gcpSecretsManager - projectId: {{ .Values.secret.projectID }} - data: - - key: {{ .Values.secret.passphrase.key }} - name: {{ .Values.secret.passphrase.name }} - version: {{ .Values.secret.passphrase.version }} - property: {{ .Values.secret.passphrase.name }} ---- -apiVersion: kubernetes-client.io/v1 -kind: ExternalSecret -metadata: - name: {{ .Values.relayer.name }}-plain - namespace: {{ .Values.namespace }} - labels: - {{- include "relayers-service.labels" $ | nindent 4 }} - -spec: - backendType: gcpSecretsManager - projectId: {{ .Values.secret_plain.projectID }} - data: - - key: {{ .Values.secret_plain.key }} - name: {{ .Values.secret_plain.config.name }} ---- \ No newline at end of file diff --git a/k8s/common/templates/secrets.yaml b/k8s/common/templates/secrets.yaml new file mode 100644 index 00000000..061b2a9b --- /dev/null +++ b/k8s/common/templates/secrets.yaml @@ -0,0 +1,55 @@ +apiVersion: external-secrets.io/v1 +kind: SecretStore +metadata: + name: {{ .Values.name }}-gsm-store + namespace: {{ .Values.namespace }} + labels: + {{- include "relayers-service.labels" . | nindent 4 }} +spec: + provider: + gcpsm: + projectID: {{ .Values.secret.projectID }} + auth: + workloadIdentity: + serviceAccountRef: + name: sa-{{ .Values.name }} +--- + +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: {{ .Values.name }}-plain-secrets + namespace: {{ .Values.namespace }} +spec: + refreshInterval: 24h + secretStoreRef: + name: {{ .Values.name }}-gsm-store + kind: SecretStore + target: + name: {{ .Values.name }}-plain-secrets # k8s secret + creationPolicy: Owner + data: + - secretKey: {{ .Values.secret_plain.config.name }} + remoteRef: + key: {{ .Values.secret_plain.key }} + version: latest + +--- + +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: {{ .Values.name }}-passphrase-secrets + namespace: {{ .Values.namespace }} +spec: + refreshInterval: 24h + secretStoreRef: + name: {{ .Values.name }}-gsm-store + kind: SecretStore + target: + name: {{ .Values.name }}-passphrase-secrets # k8s secret + creationPolicy: Owner + dataFrom: + - extract: + key: {{ .Values.secret.passphrase.key }} + version: latest \ No newline at end of file diff --git a/k8s/common/values.test.yaml b/k8s/common/values.test.yaml index 99a8761a..5e1cd3f8 100644 --- a/k8s/common/values.test.yaml +++ b/k8s/common/values.test.yaml @@ -1,25 +1,39 @@ -name: "relayer-server" +name: "bundler" relayer: - name: "relayer-server" + name: "bundler" port: "3000" -namespace: "sdk-test" +namespace: "sdk-staging" secret: projectID: biconomy-staging - passphrase: - key: sdk-test-relayer-node-service-passphrase - name: CONFIG_PASSPHRASE + key: bundler-passphrase + name: BUNDLER_CONFIG_PASSPHRASE version: latest +secret_plain: + projectID: biconomy-staging + key: bundler-plain + config: + name: production.json + ingress: - host: sdk-relayer.test.biconomy.io + host: bundler.test.biconomy.io -# datadog configs -affinity_tolerations: - enable: false datadog: - enable: false + enable: true + env: "production" + + service: "sdk-relayer-service" + version: "v3.15.0" + lib_version: "v3.16.0" + + configs: + DD_TRACE_DEBUG: "false" + DD_TRACE_STARTUP_LOGS: "false" + DD_PROFILING_ENABLED: "true" + DD_LOGS_INJECTION: "false" + DD_RUNTIME_METRICS_ENABLED: "true" diff --git a/k8s/deploy.sh b/k8s/deploy.sh index dda2b259..5db0f69c 100755 --- a/k8s/deploy.sh +++ b/k8s/deploy.sh @@ -3,12 +3,12 @@ set -e echo "The number of arguments is: $#" -if [ $# -ne 4 ]; then - echo "Invalid number of args provided. Required: 4. Provided: $#" - echo "Usage: ./deploy.sh "; +if [ $# -ne 5 ]; then + echo "Invalid number of args provided. Required: 5. Provided: $#" + echo "Usage: ./deploy.sh "; printf "Supported vales for env: test, staging, and production\n\n"; - echo "Example: ./deploy.sh 3 prod prod-sdk-relayer sdk-prod"; + echo "Example: ./deploy.sh 3 prod prod-sdk-relayer sdk-prod v1.0.0"; exit 1; fi @@ -16,6 +16,7 @@ REPLICAS=$1 ENV=$2 NAME=$3 NAMESPACE=$4 +IMAGE_TAG=$5 PODS_NAME="relayer-server" echo "Number of replicas: $REPLICAS" @@ -70,6 +71,7 @@ helm upgrade "${HELM_NAME}" ./k8s/common/ \ --timeout 720s \ --values ./k8s/common/values."${ENV}".yaml \ --set-string namespace="${NAMESPACE}" \ + --set image_tag="${IMAGE_TAG}" \ --namespace "${NAMESPACE}" echo "Deployed $HELM_NAME to $NAMESPACE" @@ -86,6 +88,7 @@ while [ "${x}" -lt "${REPLICAS}" ]; do --values ./k8s/relayer/values."${ENV}".yaml \ --set-string namespace="${NAMESPACE}" \ --set index="${x}" \ + --set image_tag="${IMAGE_TAG}" \ --namespace "${NAMESPACE}" echo "Deployed ${HELM_NAME}to ${NAMESPACE}"; diff --git a/k8s/relayer/templates/secret.yaml b/k8s/relayer/templates/secret.yaml deleted file mode 100644 index 870aa41d..00000000 --- a/k8s/relayer/templates/secret.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: kubernetes-client.io/v1 -kind: ExternalSecret -metadata: - name: {{ .Values.relayer.name }}{{ .Values.index }} - namespace: {{ .Values.namespace }} - labels: - {{- include "relayers-service.labels" $ | nindent 4 }} - -spec: - backendType: gcpSecretsManager - projectId: {{ .Values.secret_encrypted.projectID }} - data: - - key: {{ .Values.secret_encrypted.key }}-{{ .Values.index }} - name: {{ .Values.secret_encrypted.config.name }} ---- \ No newline at end of file diff --git a/k8s/relayer/templates/secrets.yaml b/k8s/relayer/templates/secrets.yaml new file mode 100644 index 00000000..025c06c6 --- /dev/null +++ b/k8s/relayer/templates/secrets.yaml @@ -0,0 +1,18 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: {{ .Values.name }}-encrypted-secrets{{ .Values.index }} + namespace: {{ .Values.namespace }} +spec: + refreshInterval: 24h + secretStoreRef: + name: {{ .Values.name }}-gsm-store + kind: SecretStore + target: + name: {{ .Values.name }}-encrypted-secrets{{ .Values.index }} # k8s secret + creationPolicy: Owner + data: + - secretKey: {{ .Values.secret_encrypted.config.name }} + remoteRef: + key: {{ .Values.secret_encrypted.key }}-{{ .Values.index }} + version: latest diff --git a/k8s/relayer/templates/statefulset.yaml b/k8s/relayer/templates/statefulset.yaml index a0371f6b..93593a0d 100644 --- a/k8s/relayer/templates/statefulset.yaml +++ b/k8s/relayer/templates/statefulset.yaml @@ -30,8 +30,19 @@ spec: {{- end }} releaseTime: {{ dateInZone "2006-01-02 15:04:05Z" (now) "UTC"| quote }} spec: - {{- if .Values.affinity_tolerations.enable }} affinity: + {{- if .Values.affinity.antiAffinity }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - {{ .Values.name }} + topologyKey: {{ .Values.affinity.antiAffinity.topologyKey }} + {{- end }} + {{- if .Values.affinity_tolerations.enable }} nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: @@ -65,7 +76,7 @@ spec: name: {{ .Values.name }}-dd-configmap {{- end }} - secretRef: - name: {{ .Values.relayer.name }}-passphrase + name: {{ .Values.relayer.name }}-passphrase-secrets volumeMounts: - mountPath: "/home/nonroot/bundler/config.json.enc" name: secret-encrypted @@ -83,7 +94,9 @@ spec: cpu: {{ .Values.relayer.resource.requests.cpu }} limits: memory: {{ .Values.relayer.resource.limits.memory }} + {{- if .Values.relayer.resource.limits.cpu }} cpu: {{ .Values.relayer.resource.limits.cpu }} + {{- end }} # allow in to total failureThreshold(30) * periodSeconds(10) = 300 seconds startupProbe: httpGet: @@ -120,10 +133,10 @@ spec: volumes: - name: secret-encrypted secret: - secretName: {{ .Values.name }}{{ .Values.index }} + secretName: {{ .Values.name }}-encrypted-secrets{{ .Values.index }} - name: secret-plain secret: - secretName: {{ .Values.name }}-plain + secretName: {{ .Values.name }}-plain-secrets {{- if .Values.datadog.enable }} - hostPath: path: /var/run/datadog/ diff --git a/k8s/relayer/values.prod.yaml b/k8s/relayer/values.prod.yaml index 6755f390..475766b0 100644 --- a/k8s/relayer/values.prod.yaml +++ b/k8s/relayer/values.prod.yaml @@ -6,16 +6,16 @@ index: 0 relayer: environment: "production" name: "relayer-server" - image: "gcr.io/biconomy-prod/sdk/relayer-node-service:latest" + image: "us-west1-docker.pkg.dev/artifact-registry-1191/bundler/bundler:latest" port: "3000" - replicaCount: 1 + replicaCount: 1 resource: requests: - memory: "6000Mi" - cpu: "4000m" + memory: "7000Mi" + cpu: "2000m" limits: - memory: "6000Mi" - cpu: "4000m" + memory: "7000Mi" + # cpu: "4000m" # don't need to set limit Ref: https://home.robusta.dev/blog/stop-using-cpu-limits secret_encrypted: projectID: biconomy-prod @@ -33,12 +33,17 @@ secret_plain: ingress: host: sdk-relayer.prod.biconomy.io -# datadog configs +# deploy to dedicated k8s node with taint affinity_tolerations: enable: true key: "app" - values: "monitoring" + values: "relayer-server" +# make sure pods are deployed in different nodes +affinity: + antiAffinity: + topologyKey: "kubernetes.io/hostname" + # wait for 120 * 10s to fail startupProbe: failureThreshold: 120 diff --git a/k8s/relayer/values.staging.yaml b/k8s/relayer/values.staging.yaml index f16a9255..50ea8248 100644 --- a/k8s/relayer/values.staging.yaml +++ b/k8s/relayer/values.staging.yaml @@ -4,7 +4,7 @@ index: 0 relayer: environment: "staging" name: "relayer-server" - image: "gcr.io/biconomy-staging/sdk/relayer-node-service:latest" + image: "us-west1-docker.pkg.dev/artifact-registry-1191/bundler/bundler:latest" port: "3000" replicaCount: 1 # Increase also the value for NODE_OPTIONS --max_old_space_size @@ -25,6 +25,11 @@ affinity_tolerations: key: "app" values: "relayer-server" +# make sure pods are deployed in different nodes +affinity: + antiAffinity: + topologyKey: "kubernetes.io/hostname" + secret_encrypted: projectID: biconomy-staging key: sdk-staging-relayer-node-service diff --git a/k8s/relayer/values.test.yaml b/k8s/relayer/values.test.yaml index abc87cac..4e3d888c 100644 --- a/k8s/relayer/values.test.yaml +++ b/k8s/relayer/values.test.yaml @@ -1,33 +1,64 @@ -name: "relayer-server" -namespace: "sdk-test" - +name: "bundler" +namespace: "sdk-staging" index: 0 + relayer: - name: "relayer-server" - image: "gcr.io/biconomy-staging/sdk-test/relayer-node-service:latest" + environment: "production" + name: "bundler" + image: "us-west1-docker.pkg.dev/artifact-registry-1191/bundler/bundler:latest" port: "3000" replicaCount: 1 resource: requests: - memory: "100Mi" - cpu: "100m" + memory: "6000Mi" + cpu: "1000m" limits: - memory: "500Mi" - cpu: "500m" + memory: "6000Mi" + # cpu: "4000m" # don't need to set limit Ref: https://home.robusta.dev/blog/stop-using-cpu-limits -secret: +secret_encrypted: projectID: biconomy-staging - key: sdk-test-relayer-node-service + key: bundler-master-account-enc config: name: config.json.enc +secret_plain: + projectID: biconomy-staging + key: bundler-plain + config: + name: production.json + + ingress: - host: sdk-relayer.test.biconomy.io + host: bundler.test.biconomy.io -# datadog configs +# deploy to dedicated k8s node with taint affinity_tolerations: - enable: false + enable: true + key: "app" + values: "relayer-server" + +# make sure pods are deployed in different nodes +affinity: + antiAffinity: + topologyKey: "kubernetes.io/hostname" + +# wait for 120 * 10s to fail +startupProbe: + failureThreshold: 120 datadog: - enable: false \ No newline at end of file + enable: true + env: "production" + + service: "sdk-relayer-service" + version: "v3.15.0" + lib_version: "v3.16.0" + + configs: + DD_TRACE_DEBUG: "false" + DD_TRACE_STARTUP_LOGS: "false" + DD_PROFILING_ENABLED: "true" + DD_LOGS_INJECTION: "false" + DD_RUNTIME_METRICS_ENABLED: "true" diff --git a/k8s/relayer/values.yaml b/k8s/relayer/values.yaml new file mode 100644 index 00000000..e69de29b diff --git a/nodemon.json b/nodemon.json index d5b2f55c..152d542b 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,6 +1,6 @@ { - "watch": ["src"], + "watch": ["src", "config"], "ext": "ts,json", - "ignore": ["src/**/*.spec.ts"], - "exec": "ts-node ./src/server/index.ts" + "ignore": ["src/**/*.spec.ts", "src/**/*.test.ts"], + "exec": "tsx ./src/server/index.ts" } diff --git a/package.json b/package.json index b780e15a..e6bb7a0a 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,28 @@ { "scripts": { - "lint": "eslint . --ext .ts", - "format": "eslint 'src' --ext .ts --fix", + "commit": "cz", + "changelog": "npx generate-changelog", "prepare": "husky install", "dev": "nodemon", "build": "tsc -p .", "start": "node --max_old_space_size=6000 dist/server/index.js", "start:production": "NODE_ENV=production node --max_old_space_size=6000 dist/server/index.js", - "test": "jest --setupFiles dotenv/config" + "test": "jest --testPathIgnorePatterns='/smoke|e2e/' --setupFiles dotenv/config --verbose", + "test:smoke": "jest -t smoke --setupFiles dotenv/config --verbose", + "test:e2e": "jest --testPathIgnorePatterns='smoke' -t e2e --setupFiles dotenv/config --verbose" }, "dependencies": { - "@arbitrum/sdk": "^3.1.3", + "@biconomy/gas-estimations": "^0.2.75", "@slack/web-api": "^6.8.0", "@types/config": "^3.3.3", "@types/crypto-js": "^4.1.1", - "@types/express-prometheus-middleware": "^1.2.1", - "@types/pino": "^7.0.5", - "amqplib": "^0.10.3", + "@types/node": "^22.10.1", + "amqplib": "^0.10.5", "async-mutex": "^0.4.0", "axios": "^1.1.2", - "big.js": "^6.2.1", - "cent.js": "^2.0.5", + "axios-retry": "^4.5.0", "cls-rtracer": "^2.6.2", + "commander": "^12.1.0", "config": "^3.3.11", "consolidate": "^0.16.0", "cors": "^2.8.5", @@ -29,37 +30,30 @@ "crypto-js": "^4.1.1", "dd-trace": "^4.17.0", "dotenv": "^16.0.3", - "entry-point-gas-estimations": "0.0.22", "ethereumjs-util": "^7.1.5", "express": "^4.18.2", - "express-handlebars": "^6.0.6", - "express-prometheus-middleware": "^1.2.0", - "handlebars": "^4.7.7", "hdkey": "^2.0.1", "husky": "^8.0.2", "ioredis": "^5.4.1", "joi": "^17.6.3", "lodash": "^4.17.21", - "mongodb": "^6.3.0", - "mongoose": "^6.6.5", + "mongoose": "^8.9.5", "node-cache": "^5.1.2", "node-mocks-http": "^1.14.1", - "numeral": "^2.0.6", - "perf_hooks": "^0.0.1", "pino": "^8.15.6", "pino-http": "^8.5.0", "pino-pretty": "^10.2.3", - "prom-client": "^15.1.0", - "redis": "^4.3.1", "redlock": "^5.0.0-beta.2", - "rest-api-errors": "^1.2.5", "serialize-error": "8.1.0", "typescript": "^5.3.3", - "viem": "^2.7.8" + "viem": "^2.21.49" }, "devDependencies": { - "@types/amqplib": "^0.8.2", - "@types/big.js": "^6.1.5", + "@biconomy/account": "^4.5.7", + "@biconomy/sdk": "^0.0.29", + "@eslint/js": "^9.13.0", + "@rhinestone/module-sdk": "^0.1.32", + "@types/amqplib": "^0.10.6", "@types/consolidate": "^0.14.1", "@types/cors": "^2.8.12", "@types/express": "^4.17.14", @@ -68,19 +62,33 @@ "@types/lodash": "^4.14.186", "@types/morgan": "^1.9.3", "@types/node-cron": "^3.0.4", - "@types/numeral": "^2.0.2", - "@typescript-eslint/eslint-plugin": "^5.40.0", - "@typescript-eslint/parser": "^5.40.0", + "@typescript-eslint/eslint-plugin": "^8.11.0", + "@typescript-eslint/parser": "^8.11.0", "babel-jest": "^29.1.2", - "eslint": "^8.25.0", - "eslint-config-airbnb-typescript": "^17.0.0", + "commitizen": "^4.3.1", + "cz-conventional-changelog": "^3.3.0", + "eslint": "^9.13.0", + "eslint-config-eslint": "^11.0.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-prettier": "^5.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-prettier": "^5.2.1", + "globals": "^15.11.0", "jest": "^29.1.2", "nodemon": "^3.0.1", - "prettier": "^3.1.1", + "prettier": "^3.3.3", "ts-jest": "^29.0.3", - "ts-node": "^10.9.1" + "ts-node": "^10.9.1", + "tsx": "^4.19.2", + "typescript-eslint": "^8.11.0" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "husky": { + "hooks": { + "prepare-commit-msg": "exec < /dev/tty && npx cz --hook || true" + } } } diff --git a/src/admin-cli/cancel.ts b/src/admin-cli/cancel.ts new file mode 100644 index 00000000..d713bbd3 --- /dev/null +++ b/src/admin-cli/cancel.ts @@ -0,0 +1,97 @@ +import { Command } from "commander"; +import { config } from "dotenv"; +import { privateKeyToAccount } from "viem/accounts"; + +// Load environment variables from .env file +config(); + +/** + * Usage: + * yarn admin-cli cancel \ + * --bundler-url https://bundler.biconomy.io + * --private-key \ + * --chain-id \ + * --relayer-address \ + * --nonce + */ +async function main() { + const program = new Command(); + + program + .requiredOption( + "-b, --bundler-url ", + "Bundler URL", + process.env.BUNDLER_URL, + ) + .requiredOption( + "-k, --private-key ", + "Private key", + process.env.PRIVATE_KEY, + ) + .requiredOption("-c, --chain-id ", "Chain ID", parseInt) + .requiredOption("-r, --relayer-address
", "Relayer address") + .requiredOption("-n, --nonce ", "Nonce", parseInt) + .parse(process.argv); + + const options = program.opts(); + + console.log(`Bundler URL: ${options.bundlerUrl}`); + console.log(`Private key: ${options.privateKey.substring(0, 6)}...`); // Only show the first 6 characters for security + + // now create a viem account from the private key + const account = privateKeyToAccount( + options.privateKey.startsWith("0x") + ? options.privateKey + : `0x${options.privateKey}`, + ); + console.log(`Admin address: ${account.address}`); + + const requestBody = { + adminAddress: account.address, + chainId: options.chainId, + relayerAddress: options.relayerAddress, + nonce: options.nonce, + }; + + const message = JSON.stringify(requestBody); + console.log(`Message: ${message}`); + + const signature = await account.signMessage({ message }); + console.log(`Signature: ${signature}`); + + // join the base bundler URL with the path /admin/cancel + const url = new URL(options.bundlerUrl); + url.pathname = "/admin/cancel"; + console.log(`Making a request to URL: ${url.toString()}`); + + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: signature, + }, + body: JSON.stringify(requestBody), + }); + + console.log(`Response status: ${response.status}`); + console.log(`Response status text: ${response.statusText}`); + + // Read and log the response body + const responseBody = await response.text(); + console.log(`Response body: ${responseBody}`); + + // If the response is JSON, we can parse and log it in a more readable format + try { + const jsonBody = JSON.parse(responseBody); + console.log("Parsed JSON response:"); + console.log(JSON.stringify(jsonBody, null, 2)); + } catch { + // If parsing fails, it's not JSON, so we've already logged it as text + console.log("Response is not JSON"); + } +} + +main().catch((error) => { + console.error("Error:", error.message); + process.exit(1); +}); diff --git a/src/admin-scripts/getRequiredPrefund.ts b/src/admin-scripts/getRequiredPrefund.ts new file mode 100644 index 00000000..34f7d3a6 --- /dev/null +++ b/src/admin-scripts/getRequiredPrefund.ts @@ -0,0 +1,30 @@ +import { formatEther } from "viem"; + +export function getRequiredPrefund(userOp: { + paymasterAndData: string; + callGasLimit: bigint; + verificationGasLimit: bigint; + preVerificationGas: bigint; + maxFeePerGas: bigint; +}) { + const multiplier = userOp.paymasterAndData !== "0x" ? 3n : 1n; + + const requiredGas = + userOp.callGasLimit + + userOp.verificationGasLimit * multiplier + + userOp.preVerificationGas; + + return requiredGas * userOp.maxFeePerGas; +} + +const prefund = getRequiredPrefund({ + paymasterAndData: + "0x00000f79b7faf42eebadba19acc07cd08af447890000000000000000000000005430eac2228c12577ed5179a4dee3aaa56a238a600000000000000000000000000000000000000000000000000000000676469c800000000000000000000000000000000000000000000000000000000676462c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000413824432901286f0f7794774baa5b55728fdccee0a7884049992c7f82471299477f978db2c8ce464433153e134f39af1a265406475b104bef19aa9cd2758e90da1c00000000000000000000000000000000000000000000000000000000000000", + callGasLimit: 24276n, + verificationGasLimit: 297166n, + preVerificationGas: 493353887n, + maxFeePerGas: 16261619n, +}); + +console.log(`Required prefund: ${formatEther(prefund)} ETH`); +// old pvg: 523923235734 diff --git a/src/common/cache/redis/RedisCacheService.ts b/src/common/cache/redis/RedisCacheService.ts index b31f4540..08f676f6 100644 --- a/src/common/cache/redis/RedisCacheService.ts +++ b/src/common/cache/redis/RedisCacheService.ts @@ -1,11 +1,9 @@ -/* eslint-disable import/no-import-module-exports */ import Redlock, { Lock } from "redlock"; import Redis from "ioredis"; import nodeconfig from "config"; import { config } from "../../../config"; import { logger } from "../../logger"; import { ICacheService } from "../interface"; -import { parseError } from "../../utils"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -80,10 +78,16 @@ export class RedisCacheService implements ICacheService { if (redisLock && this.redLock) { await this.redLock.release(redisLock); } else { - log.error("Redlock not initialized"); + log.error( + { redisLock: redisLock.toString() }, + "Redlock not initialized", + ); } - } catch (error) { - log.error(`Error in unlocking redis lock ${parseError(error)}`); + } catch (err) { + log.error( + { err, redisLock: redisLock.toString() }, + `Error during redLock.release`, + ); } } @@ -102,29 +106,25 @@ export class RedisCacheService implements ICacheService { * Method creates connection to redis client instance */ async connect(): Promise { - log.info( - `Initiating Redis connection with current status as: ${this.redisClient.status}`, - ); + const _log = log.child({ + redisUrl: config.dataSources.redisUrl, + status: this.redisClient.status, + }); try { + _log.info(`Connecting to Redis`); await this.redisClient.connect(); - log.info( - `Main Redis connected successfully with status as: ${this.redisClient.status}`, - ); - } catch (error) { - log.info(`Error in connecting to redis client ${error}`); - throw error; + _log.info(`Connected to Redis`); + } catch (err) { + log.info({ err }, `Error in RedisCacheService.connect()`); + throw err; } this.redLock = this.connectRedLock(); this.redLock.on("clientError", (err: object) => { - try { - log.info(`Failed to get redis lock ${err.toString()}`); - } catch (error) { - log.error(error); - } + _log.info({ err }, `redlock.clientError`); }); - this.redisClient.on("error", (err: any) => { - log.error(`Redis redisClient Error ${err}`); + this.redisClient.on("error", (err: unknown) => { + log.error({ err }, `redisClient.clientError`); }); } @@ -142,21 +142,20 @@ export class RedisCacheService implements ICacheService { * @returns true or false basis on success or failure */ async decrement(key: string, decrementBy: number = 1): Promise { - log.info(`Checking if the key: ${key} exists`); + const _log = log.child({ key, decrementBy }); + // could use get service also here const val = await this.redisClient.get(key); if (val == null || val === "undefined") { - log.info("Key does not exist. Nothing to decrement"); + _log.info("Key does not exist. Nothing to decrement"); return false; } try { - log.info( - `Key exists. Decrementing cache value by ${decrementBy} => Key: ${key}`, - ); + _log.info(`Key exists. Decrementing cache value`); await this.redisClient.decrby(key, decrementBy); return true; - } catch (error) { - log.error(`Error in decrement value ${parseError(error)}`); + } catch (err) { + _log.error({ err }, `Error in RedisCacheService.decrement()`); } return false; } @@ -167,13 +166,14 @@ export class RedisCacheService implements ICacheService { * @returns true or false basis on success or failure */ async delete(key: string): Promise { - log.info(`Deleting cache value => Key: ${key}`); + const _log = log.child({ key }); try { + _log.debug(`RedisCacheService.delete()`); const result = await this.redisClient.del(key); if (result) return true; return false; - } catch (error) { - log.error(`Error in deleting key ${key} - ${parseError(error)}`); + } catch (err) { + log.error({ err }, `Error in RedisCacheService.delete()`); return false; } } @@ -185,13 +185,14 @@ export class RedisCacheService implements ICacheService { * @returns true or false basis on success or failure */ async expire(key: string, expiryTime: number): Promise { + const _log = log.child({ key, expiryTime }); try { - log.info(`Setting expirtyTime: ${expiryTime} for key: ${key}`); + _log.info(`RedisCacheService.expire`); const result = await this.redisClient.expire(key, expiryTime); if (result) return true; return false; - } catch (error) { - log.error(parseError(error)); + } catch (err) { + log.error({ err }, "Error in RedisCacheService.expire"); return false; } } @@ -202,12 +203,13 @@ export class RedisCacheService implements ICacheService { * @returns string value that is set in cache */ async get(key: string): Promise { + const _log = log.child({ key }); try { - log.info(`Getting key: ${key} from cache`); + _log.debug(`RedisCacheService.get()`); const result = (await this.redisClient.get(key)) || ""; return result; - } catch (error) { - log.error(`Error getting value for key ${key} - ${parseError(error)}`); + } catch (err) { + _log.error({ err }, `Error in RedisCacheService.get`); } return ""; } @@ -219,23 +221,23 @@ export class RedisCacheService implements ICacheService { * @returns true or false basis on success or failure */ async increment(key: string, incrementBy: number = 1): Promise { - log.info(`Checking if the key: ${key} exists`); + const _log = log.child({ key, incrementBy }); try { const val = await this.redisClient.get(key); if (!val) { - log.info("Key does not exist. Nothing to increment"); + _log.info("Key does not exist. Nothing to increment"); return false; } - log.info(`Inrementing cache value by ${incrementBy} => Key: ${key}`); + _log.info(`Inrementing cache value`); const result = await this.redisClient.incrby(key, incrementBy); if (result) { - log.info(`Incremented cache value by ${incrementBy} => Key: ${key}`); + _log.info(`Incremented cache value`); return true; } return false; - } catch (error) { - log.error(`Error in increment value - ${parseError(error)}`); + } catch (err) { + _log.error({ err }, `Error in RedisCacheService.increment`); return false; } } @@ -252,172 +254,17 @@ export class RedisCacheService implements ICacheService { value: string, hideValueInLogs: boolean = false, ): Promise { - if (!hideValueInLogs) { - log.info(`Setting cache value => Key: ${key} Value: ${value}`); - } else { - log.info(`Setting value in cache with key: ${key}`); - } + const _log = hideValueInLogs + ? log.child({ key }) + : log.child({ key, value }); + + _log.debug(`RedisCacheService.set`); try { await this.redisClient.set(key, value); - log.info(`Cache value set in logs for key: ${key}`); return true; - } catch (error) { - log.error( - `Error setting value ${value} for key ${key} - ${parseError(error)}`, - ); + } catch (err) { + _log.error({ err }, `Error in RedisCacheService.set()`); return false; } } } - -// export class RedisCacheService implements ICacheService { -// private static instance: ICacheService; - -// private redisPool: Pool; - -// private redLock: Redlock | undefined; - -// private constructor() { -// this.redisPool = createPool({ -// create: async () => new Redis(config.dataSources.redisUrl), -// destroy: (client: Redis) => client.quit().then(() => { }), -// }, { -// max: 200, // maximum size of the pool -// min: 100, // minimum size of the pool -// // additional pool configuration options -// }); -// } - -// getRedLock(): Redlock | undefined { -// if (this.redLock) return this.redLock; -// return undefined; -// } - -// async unlockRedLock(redisLock: Lock) { -// try { -// if (redisLock && this.redLock) { -// await this.redLock.release(redisLock); -// } else { -// log.error('Redlock not initialized'); -// } -// } catch (error) { -// log.error(`Error in unlocking redis lock ${JSON.stringify(error)}`); -// } -// } - -// public static getInstance(): ICacheService { -// if (!RedisCacheService.instance) { -// RedisCacheService.instance = new RedisCacheService(); -// } -// return RedisCacheService.instance; -// } - -// async connect(): Promise { -// try { -// const client = await this.redisPool.acquire(); -// log.info(`Redis connection status: ${client.status}`); -// await this.redisPool.release(client); -// } catch (error) { -// log.error(`Error in connecting to redis client ${error}`); -// throw error; -// } -// } - -// async close(): Promise { -// await this.redisPool.drain(); -// await this.redisPool.clear(); -// } - -// async get(key: string): Promise { -// const client = await this.redisPool.acquire(); -// try { -// return await client.get(key) || ''; -// } catch (error) { -// log.error(`Error getting value for key ${key} - ${JSON.stringify(error)}`); -// return ''; -// } finally { -// await this.redisPool.release(client); -// } -// } - -// async set(key: string, value: string, hideValueInLogs: boolean = false): Promise { -// const client = await this.redisPool.acquire(); -// try { -// if (!hideValueInLogs) { -// log.info(`Setting cache value => Key: ${key} Value: ${value}`); -// } else { -// log.info(`Setting value in cache with key: ${key}`); -// } -// await client.set(key, value); -// log.info(`Cache value set for key: ${key}`); -// return true; -// } catch (error) { -// log.error(`Error setting value for key ${key} - ${JSON.stringify(error)}`); -// return false; -// } finally { -// await this.redisPool.release(client); -// } -// } - -// async increment(key: string, incrementBy: number = 1): Promise { -// const client = await this.redisPool.acquire(); -// try { -// const val = await client.get(key); -// if (!val) { -// log.info('Key does not exist. Nothing to increment'); -// return false; -// } -// await client.incrby(key, incrementBy); -// return true; -// } catch (error) { -// log.error(`Error in increment value - ${JSON.stringify(error)}`); -// return false; -// } finally { -// await this.redisPool.release(client); -// } -// } - -// async decrement(key: string, decrementBy: number = 1): Promise { -// const client = await this.redisPool.acquire(); -// try { -// const val = await client.get(key); -// if (val == null || val === 'undefined') { -// log.info('Key does not exist. Nothing to decrement'); -// return false; -// } -// await client.decrby(key, decrementBy); -// return true; -// } catch (error) { -// log.error(`Error in decrement value ${JSON.stringify(error)}`); -// return false; -// } finally { -// await this.redisPool.release(client); -// } -// } - -// async delete(key: string): Promise { -// const client = await this.redisPool.acquire(); -// try { -// const result = await client.del(key); -// return result === 1; -// } catch (error) { -// log.error(`Error in deleting key ${key} - ${JSON.stringify(error)}`); -// return false; -// } finally { -// await this.redisPool.release(client); -// } -// } - -// async expire(key: string, expiryTime: number): Promise { -// const client = await this.redisPool.acquire(); -// try { -// const result = await client.expire(key, expiryTime); -// return result === 1; -// } catch (error) { -// log.error(`Error setting expiry for key ${key} - ${JSON.stringify(error)}`); -// return false; -// } finally { -// await this.redisPool.release(client); -// } -// } -// } diff --git a/src/common/constants/index.ts b/src/common/constants/index.ts index 5e2ce17b..31e10d0a 100644 --- a/src/common/constants/index.ts +++ b/src/common/constants/index.ts @@ -23,7 +23,7 @@ export enum BLOCKCHAINS { CORE_TESTNET = 1115, MANTA_TESTNET = 3441005, MANTA_MAINNET = 169, - COMBO_TESTNET = 91715, + COMBO_TESTNET = 1715, COMBO_MAINNET = 9980, ARBITRUM_SEPOLIA_TESTNET = 421614, SEPOLIA_TESTNET = 11155111, @@ -47,8 +47,24 @@ export enum BLOCKCHAINS { GNOSIS_TESTNET = 10200, X_LAYER_MAINNET = 196, X_LAYER_TESTNET = 195, + MORPH_MAINNET = 2818, MORPH_HOLESKY_TESTNET = 2810, FIRECHAIN_TESTNET = 997, - SEI_TESTNET = 713715, + FIRECHAIN_MAINNET = 995, + SEI_DEVNET_ARCTIC_1 = 713715, + SEI_TESTNET_ATLANTIC_2 = 1328, + SEI_MAINNET = 1329, TANGLE_TESTNET = 3799, + TANGLE_MAINNET = 5845, + TAIKO_HEKLA_TESTNET = 167009, + TAIKO_MAINNET = 167000, + BERACHAIN_BARTIO_TESTNET = 80084, + KAKAROT_TESTNET = 920637907288165, + METAL_L2_TESTNET = 1740, + METAL_L2_MAINNET = 1750, + LISK_TESTNET = 4202, + LISK_MAINNET = 1135, + BOBA_SEPOLIA_TESTNET = 28882, + BOBA_MAINNET = 288, + MOONBEAM_MAINNET = 1284, } diff --git a/src/common/db/dao/TransactionDAO.ts b/src/common/db/dao/TransactionDAO.ts index 7c36dff2..f34b7cdf 100644 --- a/src/common/db/dao/TransactionDAO.ts +++ b/src/common/db/dao/TransactionDAO.ts @@ -79,6 +79,7 @@ export class TransactionDAO implements ITransactionDAO { async updateMetaDataAndRelayerDestinationContractDataByTransactionId( chainId: number, id: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any metaData: any, relayerDestinationContractAddress: string, relayerDestinationContractName: string, diff --git a/src/common/db/dao/UserOperationDAO.ts b/src/common/db/dao/UserOperationDAO.ts index 1c485147..f222c84c 100644 --- a/src/common/db/dao/UserOperationDAO.ts +++ b/src/common/db/dao/UserOperationDAO.ts @@ -90,7 +90,7 @@ export class UserOperationDAO implements IUserOperationDAO { startTime: number, endTime: number, ): Promise { - const data = await this._db.getUserOperation(chainId).count({ + const data = await this._db.getUserOperation(chainId).countDocuments({ "metaData.dappAPIKey": bundlerApiKey, creationTime: { $gte: startTime, $lte: endTime }, }); diff --git a/src/common/db/dao/UserOperationV07DAO.ts b/src/common/db/dao/UserOperationV07DAO.ts new file mode 100644 index 00000000..362a3e86 --- /dev/null +++ b/src/common/db/dao/UserOperationV07DAO.ts @@ -0,0 +1,99 @@ +import { + FinalUserOperationDataType, + IUserOperationV07DAO, + InitialUserOperationV07DataType, +} from "../interface"; +import { IUserOperationV07, Mongo } from "../mongo"; + +export class UserOperationV07DAO implements IUserOperationV07DAO { + private _db: Mongo; + + constructor() { + this._db = Mongo.getInstance(); + } + + async save( + chainId: number, + userOperationData: InitialUserOperationV07DataType, + ): Promise { + await this._db.getUserOperationV07(chainId).insertMany([userOperationData]); + } + + async getUserOperationsDataByApiKey( + chainId: number, + apiKey: string, + startTime: number, + endTime: number, + limit: number, + offSet: number, + ): Promise> { + const data = await this._db + .getUserOperationV07(chainId) + .aggregate([ + { + $match: { + $and: [ + { "metaData.apiKey": apiKey }, + { creationTime: { $gte: startTime } }, + { creationTime: { $lte: endTime } }, + ], + }, + }, + ]) + .skip(offSet) + .limit(limit); + return data; + } + + async getUserOperationDataByUserOpHash( + chainId: number, + userOpHash: string, + ): Promise { + const data = await this._db + .getUserOperationV07(chainId) + .findOne({ userOpHash }); + return data; + } + + async updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + chainId: number, + transactionId: string, + userOpHash: string, + userOperationData: FinalUserOperationDataType, + ): Promise { + await this._db.getUserOperationV07(chainId).updateOne( + { + transactionId, + userOpHash, + }, + userOperationData, + ); + } + + async getUserOpsByTransactionId( + chainId: number, + transactionId: string, + ): Promise { + const data = await this._db + .getUserOperationV07(chainId) + .find({ + transactionId, + }) + .sort({ _id: -1 }) + .lean(); + return data; + } + + async getUserOperationsCountByApiKey( + chainId: number, + apiKey: string, + startTime: number, + endTime: number, + ): Promise { + const data = await this._db.getUserOperationV07(chainId).countDocuments({ + "metaData.apiKey": apiKey, + creationTime: { $gte: startTime, $lte: endTime }, + }); + return data; + } +} diff --git a/src/common/db/dao/index.ts b/src/common/db/dao/index.ts index 5ea09339..2c11808a 100644 --- a/src/common/db/dao/index.ts +++ b/src/common/db/dao/index.ts @@ -1,3 +1,4 @@ export * from "./TransactionDAO"; export * from "./UserOperationDAO"; export * from "./UserOperationStateDAO"; +export * from "./UserOperationV07DAO"; diff --git a/src/common/db/interface/IUserOperationDAO.ts b/src/common/db/interface/IUserOperationDAO.ts index cbd531c7..17e05bf8 100644 --- a/src/common/db/interface/IUserOperationDAO.ts +++ b/src/common/db/interface/IUserOperationDAO.ts @@ -5,20 +5,20 @@ export type InitialUserOperationDataType = { dappAPIKey?: string; entryPoint: string; sender: string; - nonce: number; initCode: string; + nonce: number; callData: string; callGasLimit: number; verificationGasLimit: number; preVerificationGas: number; maxFeePerGas: number; maxPriorityFeePerGas: number; - paymasterAndData: string; signature: string; userOpHash: string; chainId: number; status: string; paymaster: string; + paymasterAndData: string; creationTime: number; metaData?: object; }; @@ -33,6 +33,7 @@ export type FinalUserOperationDataType = { actualGasCost: number; actualGasUsed: number; reason: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any logs: any; }; diff --git a/src/common/db/interface/IUserOperationV07DAO.ts b/src/common/db/interface/IUserOperationV07DAO.ts new file mode 100644 index 00000000..d1b78044 --- /dev/null +++ b/src/common/db/interface/IUserOperationV07DAO.ts @@ -0,0 +1,76 @@ +import { IUserOperationV07 } from "../mongo/interface"; + +export type InitialUserOperationV07DataType = { + transactionId: string; + apiKey?: string; + entryPoint: string; + sender: string; + nonce: number; + callData: string; + callGasLimit: number; + verificationGasLimit: number; + preVerificationGas: number; + maxFeePerGas: number; + maxPriorityFeePerGas: number; + signature: string; + userOpHash: string; + chainId: number; + status: string; + paymaster: string; + paymasterVerificationGasLimit: number; + paymasterPostOpGasLimit: number; + factory: string; + factoryData: string; + paymasterData: string; + creationTime: number; + metaData?: object; +}; + +export type FinalUserOperationV07DataType = { + transactionHash?: string; + receipt: object; + status: string; + success: string; + blockNumber: number; + blockHash: string; + actualGasCost: number; + actualGasUsed: number; + reason: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + logs: any; +}; + +export interface IUserOperationV07DAO { + save( + chainId: number, + initialUserOperationData: InitialUserOperationV07DataType, + ): Promise; + getUserOperationDataByUserOpHash( + chainId: number, + userOpHash: string, + ): Promise; + updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + chainId: number, + userOpHash: string, + transactionId: string, + initialUserOperationData: FinalUserOperationV07DataType, + ): Promise; + getUserOpsByTransactionId( + chainId: number, + transactionId: string, + ): Promise; + getUserOperationsDataByApiKey( + chainId: number, + apiKey: string, + startTime: number, + endTime: number, + limit: number, + offSet: number, + ): Promise>; + getUserOperationsCountByApiKey( + chainId: number, + apiKey: string, + startTime: number, + endTime: number, + ): Promise; +} diff --git a/src/common/db/interface/index.ts b/src/common/db/interface/index.ts index 8392bedb..73582d41 100644 --- a/src/common/db/interface/index.ts +++ b/src/common/db/interface/index.ts @@ -1,4 +1,5 @@ export * from "./IDBService"; export * from "./ITransactionDAO"; export * from "./IUserOperationDAO"; +export * from "./IUserOperationV07DAO"; export * from "./IUserOperationStateDAO"; diff --git a/src/common/db/mongo/Mongo.ts b/src/common/db/mongo/Mongo.ts index 10cd59ac..19dabaec 100644 --- a/src/common/db/mongo/Mongo.ts +++ b/src/common/db/mongo/Mongo.ts @@ -1,5 +1,4 @@ -/* eslint-disable no-await-in-loop */ -/* eslint-disable import/no-import-module-exports */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import mongoose, { Mongoose } from "mongoose"; import { config } from "../../../config"; import { logger } from "../../logger"; @@ -11,6 +10,8 @@ import { UserOperationsMapType, UserOperationsStateMap, UserOperationsStateMapType, + UserOperationsV07Map, + UserOperationsV07MapType, } from "./models"; const log = logger.child({ @@ -31,7 +32,7 @@ export class Mongo implements IDBService { * for all collections in the database, if it doesn't already exist. */ async createTransactionIdIndexes(): Promise { - if (!this.client) { + if (!this.client || !this.client.connection.db) { throw new Error("Not connected to db"); } @@ -67,7 +68,6 @@ export class Mongo implements IDBService { } } - // eslint-disable-next-line class-methods-use-this async createIndexes( collectionObject: any, compoundIndexOne: object, @@ -103,7 +103,7 @@ export class Mongo implements IDBService { .map(([key, value]) => `${key}_${value}`) .join("_"); - log.info(`Compund index key name ${compoundIndexKeyTwo}`); + log.info(`Compound index key name ${compoundIndexKeyTwo}`); const compoundIndexExistsTwo = indexes.some( (index: any) => index.name === compoundIndexKeyTwo, ); @@ -147,7 +147,6 @@ export class Mongo implements IDBService { dbName: "relayer-node-service", }); } - log.info("Connected to db"); } catch (error) { log.error("error while connecting to mongo db"); log.error(error); @@ -186,6 +185,21 @@ export class Mongo implements IDBService { return UserOperationsMap[networkId]; } + /** + * Method returns user operation model for a given chain id + * @param networkId + * @returns user operation model for a given chain id + */ + getUserOperationV07(networkId: number): UserOperationsV07MapType[number] { + if (!this.client) { + throw new Error("Not connected to db"); + } + const supportedNetworksV07: number[] = config.supportedNetworksV07 || []; + if (!supportedNetworksV07.includes(networkId)) + throw new Error(`Network Id ${networkId} is not supported`); + return UserOperationsV07Map[networkId]; + } + /** * Method returns user operation state model for a given chain id * @param networkId diff --git a/src/common/db/mongo/interface/IBlockchainTransaction.ts b/src/common/db/mongo/interface/IBlockchainTransaction.ts index 31f88f38..014fa204 100644 --- a/src/common/db/mongo/interface/IBlockchainTransaction.ts +++ b/src/common/db/mongo/interface/IBlockchainTransaction.ts @@ -38,6 +38,7 @@ export interface IBlockchainTransaction { relayerDestinationContractName: string; relayerAddress: string; walletAddress: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any metaData: any; resubmitted: boolean; creationTime: number; diff --git a/src/common/db/mongo/interface/IUserOperationV07.ts b/src/common/db/mongo/interface/IUserOperationV07.ts new file mode 100644 index 00000000..8353cb74 --- /dev/null +++ b/src/common/db/mongo/interface/IUserOperationV07.ts @@ -0,0 +1,31 @@ +export interface IUserOperationV07 { + transactionId: string; + transactionHash: string; + entryPoint: string; + sender: string; + nonce: number; + factory: string; + factoryData: string; + callData: string; + callGasLimit: number; + verificationGasLimit: number; + preVerificationGas: number; + maxFeePerGas: number; + maxPriorityFeePerGas: number; + signature: string; + userOpHash: string; + receipt: object; + chainId: number; + status: string; + success: string; + blockNumber: number; + blockHash: string; + paymaster: string; + actualGasCost: number; + actualGasUsed: number; + reason: string; + logs: object; + creationTime: number; + metaData: object; + apiKey: string; +} diff --git a/src/common/db/mongo/interface/index.ts b/src/common/db/mongo/interface/index.ts index ec4e849b..9a7a6629 100644 --- a/src/common/db/mongo/interface/index.ts +++ b/src/common/db/mongo/interface/index.ts @@ -1,3 +1,4 @@ export * from "./IBlockchainTransaction"; export * from "./IUserOperation"; export * from "./IUserOperationState"; +export * from "./IUserOperationV07"; diff --git a/src/common/db/mongo/models/blockchain-transactions/model.ts b/src/common/db/mongo/models/blockchain-transactions/model.ts index 4d193119..56d93bd6 100644 --- a/src/common/db/mongo/models/blockchain-transactions/model.ts +++ b/src/common/db/mongo/models/blockchain-transactions/model.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-empty-object-type */ import mongoose from "mongoose"; import { config } from "../../../../../config"; import { IBlockchainTransaction } from "../../interface/IBlockchainTransaction"; diff --git a/src/common/db/mongo/models/user-operations-state/model.ts b/src/common/db/mongo/models/user-operations-state/model.ts index d2eb6964..464fc1fc 100644 --- a/src/common/db/mongo/models/user-operations-state/model.ts +++ b/src/common/db/mongo/models/user-operations-state/model.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-empty-object-type */ import mongoose from "mongoose"; import { config } from "../../../../../config"; import { IUserOperationState } from "../../interface"; diff --git a/src/common/db/mongo/models/user-operations/model.ts b/src/common/db/mongo/models/user-operations/model.ts index 84f4c175..c4f16c0d 100644 --- a/src/common/db/mongo/models/user-operations/model.ts +++ b/src/common/db/mongo/models/user-operations/model.ts @@ -1,15 +1,21 @@ +/* eslint-disable @typescript-eslint/no-empty-object-type */ import mongoose from "mongoose"; import { config } from "../../../../../config"; -import { IUserOperation } from "../../interface"; -import { UserOperationSchema } from "./schema"; +import { IUserOperation, IUserOperationV07 } from "../../interface"; +import { UserOperationSchema, UserOperationV07Schema } from "./schema"; -const { supportedNetworks } = config; +const { supportedNetworks, supportedNetworksV07 } = config; export type UserOperationsMapType = { [networkId: number]: mongoose.Model; }; +export type UserOperationsV07MapType = { + [networkId: number]: mongoose.Model; +}; + const UserOperationsMap: UserOperationsMapType = {}; +const UserOperationsV07Map: UserOperationsV07MapType = {}; for (const networkId of supportedNetworks) { const collectionName = `UserOperations_${networkId}`; @@ -19,4 +25,12 @@ for (const networkId of supportedNetworks) { ); } -export { UserOperationsMap }; +for (const networkId of supportedNetworksV07) { + const collectionNameV07 = `UserOperationsV07_${networkId}`; + UserOperationsV07Map[networkId] = mongoose.model( + collectionNameV07, + UserOperationV07Schema, + ); +} + +export { UserOperationsMap, UserOperationsV07Map }; diff --git a/src/common/db/mongo/models/user-operations/schema.ts b/src/common/db/mongo/models/user-operations/schema.ts index 40d202ee..60fcb610 100644 --- a/src/common/db/mongo/models/user-operations/schema.ts +++ b/src/common/db/mongo/models/user-operations/schema.ts @@ -1,5 +1,6 @@ import mongoose from "mongoose"; import { IUserOperation } from "../../interface/IUserOperation"; +import { IUserOperationV07 } from "../../interface/IUserOperationV07"; const { Schema } = mongoose; @@ -91,3 +92,86 @@ export const UserOperationSchema = new Schema({ type: String, }, }); + +export const UserOperationV07Schema = new Schema({ + transactionId: { + type: String, + required: true, + }, + transactionHash: { + type: String, + }, + chainId: { type: Number }, + sender: { + type: String, + }, + nonce: { + type: Number, + }, + callData: { + type: String, + }, + callGasLimit: { + type: Number, + }, + verificationGasLimit: { + type: Number, + }, + preVerificationGas: { + type: Number, + }, + maxFeePerGas: { + type: Number, + }, + maxPriorityFeePerGas: { + type: Number, + }, + signature: { + type: String, + }, + userOpHash: { + type: String, + }, + status: { + type: String, + }, + success: { + type: String, + }, + entryPoint: { + type: String, + }, + blockNumber: { + type: Number, + }, + blockHash: { + type: String, + }, + paymaster: { + type: String, + }, + actualGasCost: { + type: Number, + }, + actualGasUsed: { + type: Number, + }, + reason: { + type: String, + }, + logs: { + type: Object, + }, + receipt: { + type: Object, + }, + creationTime: { + type: Number, + }, + metaData: { + type: Object, + }, + apiKey: { + type: String, + }, +}); diff --git a/src/common/entrypoint-v7/PackedUserOperation.ts b/src/common/entrypoint-v7/PackedUserOperation.ts new file mode 100644 index 00000000..97a036f5 --- /dev/null +++ b/src/common/entrypoint-v7/PackedUserOperation.ts @@ -0,0 +1,389 @@ +import { + type Hex, + concat, + pad, + slice, + toHex, + keccak256, + encodeAbiParameters, + fromHex, +} from "viem"; +import { isUndefined } from "lodash"; +import type { UserOperationStruct, UserOperationType } from "../types"; +import { config } from "../../config"; + +const { entryPointV07Data } = config; + +export const COMMON_ENTRYPOINT_V7_ADDRESSES: Hex[] = Array.from( + Object.keys(entryPointV07Data).map((key) => key as Hex), +); + +// Definitions can be found in the ERC doc: https://eips.ethereum.org/EIPS/eip-4337#entrypoint-definition +export interface PackedUserOperation { + sender: Hex; + nonce: bigint; + initCode: Hex; + callData: Hex; + accountGasLimits: Hex; + preVerificationGas: bigint; + gasFees: Hex; + paymasterAndData: Hex; + signature: Hex; +} + +// /** +// * Packs two unsigned 16 byte (128-bit) integers into a single hexadecimal string. +// * The two integers are converted to hex and padded to ensure a fixed size. +// * +// * @param a - The first unsigned integer (128-bit). +// * @param b - The second unsigned integer (128-bit). +// * @returns A Hex string that represents the packed integers. +// */ +export function packUint(high128: bigint, low128: bigint): Hex { + + const packed = (BigInt(high128) << BigInt(128)) + BigInt(low128); + return pad(toHex(packed), { size: 32 }); +} + +// /** +// * Unpacks a hexadecimal string into two unsigned 16 bytes (128-bit) integers. +// * The hex string is sliced into two parts corresponding to the original integers. +// * +// * It is used to undo the application of packUint function. +// * @param packedUints - A Hex string containing the packed integers. +// * @returns An object containing the two unpacked integers as `a` and `b`. +// */ +export function unpackUint(packed: Hex): [high128: bigint, low128: bigint] { + const packedNumber = BigInt(fromHex(packed, "bigint")); + + const high128 = packedNumber >> BigInt(128); + + const low128 = packedNumber & ((BigInt(1) << BigInt(128)) - BigInt(1)); + return [high128, low128]; +} + +/** + * Packs the `verificationGasLimit` and `callGasLimit` fields from a UserOperationStruct into a single hexadecimal string. + * + * This function uses `packUint` to combine the two gas limits into one hex string, which can be used for + * encoding gas-related information of a user operation. + * + * @param userOperation - The UserOperationStruct containing the `verificationGasLimit` and `callGasLimit` fields to pack. + * @returns A hexadecimal string representing the packed gas limits. + */ +export function packAccountGasLimits(userOperation: UserOperationStruct): Hex { + return packUint( + userOperation.verificationGasLimit, + userOperation.callGasLimit, + ); +} + +/** + * Unpacks a hexadecimal string into `verificationGasLimit` and `callGasLimit` gas limit values. + * + * The function splits the packed hex string back into the two original gas limit values. + * + * @param accountGasLimits - The hexadecimal string containing packed gas limits. + * @returns An object with `verificationGasLimit` and `callGasLimit` as two unpacked gas limit values. + */ +export function unpackAccountGasLimits(accountGasLimits: Hex) { + const [verificationGasLimit, callGasLimit] = unpackUint(accountGasLimits); + return { verificationGasLimit, callGasLimit }; +} + +/** + * Packs the `maxPriorityFeePerGas` and `maxFeePerGas` fields from a UserOperationStruct into a single hexadecimal string. + * + * This function uses `packUint` to combine the two fee-related values into one hex string for compact storage + * or transmission. + * + * @param userOperation - The UserOperationStruct containing the `maxPriorityFeePerGas` and `maxFeePerGas` fields to pack. + * @returns A hexadecimal string representing the packed gas fee limits. + */ +export function packGasFees(userOperation: UserOperationStruct): Hex { + return packUint( + userOperation.maxPriorityFeePerGas, + userOperation.maxFeePerGas, + ); +} + +/** + * Unpacks a hexadecimal string into `maxPriorityFeePerGas` and `maxFeePerGas` gas fee values. + * + * The function extracts the two gas fee values from the packed hexadecimal string. + * + * @param gasFees - The hexadecimal string containing packed gas fee values. + * @returns An object with `maxPriorityFeePerGas` and `maxFeePerGas` as two unpacked gas fee values. + */ +export function unpackGasFees(gasFees: Hex) { + const [a, b] = unpackUint(gasFees); + const maxPriorityFeePerGas = a; + const maxFeePerGas = b; + + return { maxPriorityFeePerGas, maxFeePerGas }; +} + +/** + * Packs the factory and factoryData fields from a UserOperationStruct into a single hexadecimal string. + * + * This function checks if both `factory` and `factoryData` fields are not null or undefined. If both fields + * are valid, it concatenates them into a single hexadecimal string and returns it. If only the `factory` + * field is available, it returns its hexadecimal representation. If `factory` is null, it returns a default + * '0x' hex string. + * + * This function is useful for encoding initialization data of a user operation for later use or storage. + * + * @param userOperation - The UserOperationStruct containing the `factory` and `factoryData` fields to pack. + * @returns A hexadecimal string representing the packed initialization data, or '0x' if no data exists. + */ +export function packInitCode(userOperation: UserOperationStruct): Hex { + if ( + userOperation.factory !== null && + !isUndefined(userOperation.factory) && + userOperation.factoryData !== null && + !isUndefined(userOperation.factoryData) + ) { + return concat([userOperation.factory, userOperation.factoryData]); + } + return userOperation.factory == null + ? "0x" + : toHex(concat([userOperation.factory])); +} + +/** + * Unpacks the `initCode` into its components: `factory` and `factoryData`. + * + * This function checks if the `initCode` is valid (non-null and longer than 2 characters). It slices the + * first 20 bytes to extract the `factory` and uses the remaining portion as `factoryData`. If the `initCode` + * is invalid, it returns null. + * + * @param initCode - The hexadecimal string containing the packed factory and factoryData. + * @returns An object with `factory` and `factoryData` as separate components or null if invalid. + */ +export function unpackInitCode(initCode: Hex) { + if (initCode != null && initCode.length > 2) { + const factory = slice(initCode, 0, 20); + const factoryData = slice(initCode, 20); + return { factory, factoryData }; + } + return null; +} + +/** + * Packs the `paymaster`, `paymasterVerificationGasLimit`, `paymasterPostOpGasLimit`, and `paymasterData` fields + * from a UserOperationStruct into a single hexadecimal string. + * + * If the `paymaster` is null, it returns a default '0x' hex string. If any gas limits are missing while the + * `paymaster` is set, it throws an error. This packed data is useful for combining paymaster-related information + * into a single hex string for transmission or storage. + * + * @param userOperation - The UserOperationStruct containing the `paymaster`, gas limits, and `paymasterData`. + * @returns A hexadecimal string representing the packed paymaster and associated data. + * @throws An error if the `paymaster` is set but required gas limits are missing. + */ +export function packPaymasterAndData(userOperation: UserOperationStruct): Hex { + if (userOperation.paymaster == null) { + return `0x`; + } + if ( + userOperation.paymasterVerificationGasLimit == null || + userOperation.paymasterPostOpGasLimit == null + ) { + throw new Error("paymaster with no gas limits"); + } + return concat([ + userOperation.paymaster, + packUint( + userOperation.paymasterVerificationGasLimit, + userOperation.paymasterPostOpGasLimit, + ), + userOperation.paymasterData ?? "0x", + ]); +} + +/** + * Unpacks the `paymasterAndData` hex string into its components: `paymaster`, `paymasterVerificationGasLimit`, + * `paymasterPostOpGasLimit`, and `paymasterData`. + * + * It checks the length of the hex string for validity. If invalid, it returns null or throws an error. If valid, + * it slices the string to extract the `paymaster`, gas limits, and `paymasterData`. + * + * @param paymasterAndData - The hexadecimal string containing the packed paymaster-related information. + * @returns An object containing `paymaster`, `paymasterVerificationGasLimit`, `paymasterPostOpGasLimit`, and `paymasterData`. + * @throws An error if the `paymasterAndData` string is too short to contain valid gas limit data. + */ +export function unpackPaymasterAndData(paymasterAndData: Hex): { + paymaster: Hex; + paymasterVerificationGasLimit: bigint; + paymasterPostOpGasLimit: bigint; + paymasterData: Hex; +} | null { + if (paymasterAndData.length <= 2) return null; + if (paymasterAndData.length < 52) { + throw new Error(`invalid paymasterAndData: ${paymasterAndData as string}`); + } + const [a, b] = unpackUint(slice(paymasterAndData, 20, 52)); + return { + paymaster: slice(paymasterAndData, 0, 20), + paymasterVerificationGasLimit: a, + paymasterPostOpGasLimit: b, + paymasterData: slice(paymasterAndData, 52), + }; +} + +/** + * Packs a UserOperation object into a PackedUserOperation format. + * + * UserOperation is a well known object in Account Abstraction. + * PackedUserOperation is an object introduced in EntryPoint v0.7, + * as an object that is being sent to EntryPoint smart contract. + * + * @param userOperation - The UserOperationType object to be packed. + * @returns The PackedUserOperation object. + */ +export function packUserOperation( + userOperation: UserOperationStruct, +): PackedUserOperation { + return { + sender: userOperation.sender, + nonce: userOperation.nonce, + initCode: packInitCode(userOperation), + callData: userOperation.callData, + accountGasLimits: packAccountGasLimits(userOperation), + preVerificationGas: userOperation.preVerificationGas, + gasFees: packGasFees(userOperation), + paymasterAndData: packPaymasterAndData(userOperation), + signature: userOperation.signature, + }; +} + +/** + * Unpacks a PackedUserOperation object back into a UserOperationType format. + * + * @param packedUserOperation - The PackedUserOperation object to be unpacked. + * @returns The UserOperationType object. + */ +export function unpackUserOperation( + packedUserOperation: PackedUserOperation, +): UserOperationStruct { + const { verificationGasLimit, callGasLimit } = unpackAccountGasLimits( + packedUserOperation.accountGasLimits, + ); + const { maxPriorityFeePerGas, maxFeePerGas } = unpackGasFees( + packedUserOperation.gasFees, + ); + + let result: UserOperationStruct = { + sender: packedUserOperation.sender, + nonce: packedUserOperation.nonce, + callData: packedUserOperation.callData, + callGasLimit, + verificationGasLimit, + preVerificationGas: packedUserOperation.preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + signature: packedUserOperation.signature, + } as unknown as UserOperationStruct; + + if ( + packedUserOperation.initCode != null && + packedUserOperation.initCode.length > 2 + ) { + const factory = slice(packedUserOperation.initCode, 0, 20); + const factoryData = slice(packedUserOperation.initCode, 20); + result = { + ...result, + factory, + factoryData, + }; + } + const paymasterData = unpackPaymasterAndData( + packedUserOperation.paymasterAndData, + ); + if (paymasterData != null) { + result = { + ...result, + paymaster: paymasterData.paymaster, + paymasterVerificationGasLimit: + paymasterData.paymasterVerificationGasLimit, + paymasterPostOpGasLimit: paymasterData.paymasterPostOpGasLimit, + paymasterData: paymasterData.paymasterData, + }; + } + return result; +} + +export function encodePackedUserOp( + userOp: PackedUserOperation, + forSignature = true, +): `0x${string}` { + if (forSignature) { + return encodeAbiParameters( + [ + { type: "address" }, + { type: "uint256" }, + { type: "bytes32" }, + { type: "bytes32" }, + { type: "bytes32" }, + { type: "uint256" }, + { type: "bytes32" }, + { type: "bytes32" }, + ], + [ + userOp.sender, + userOp.nonce, + keccak256(userOp.initCode), + keccak256(userOp.callData), + userOp.accountGasLimits, + userOp.preVerificationGas, + userOp.gasFees, + keccak256(userOp.paymasterAndData), + ], + ); + } + return encodeAbiParameters( + [ + { type: "address" }, + { type: "uint256" }, + { type: "bytes32" }, + { type: "bytes32" }, + { type: "bytes32" }, + { type: "uint256" }, + { type: "bytes32" }, + { type: "bytes32" }, + { type: "bytes" }, + ], + [ + userOp.sender, + userOp.nonce, + userOp.initCode, + userOp.callData, + userOp.accountGasLimits, + userOp.preVerificationGas, + userOp.gasFees, + userOp.paymasterAndData, + userOp.signature, + ], + ); +} + +export function getUserOpHash( + userOp: PackedUserOperation, + entryPoint: `0x${string}`, + chainId: bigint, +): Hex { + const userOpHash = keccak256(encodePackedUserOp(userOp, true)); + + const fullEncodedData = encodeAbiParameters( + [{ type: "bytes32" }, { type: "address" }, { type: "uint256" }], + [userOpHash, entryPoint, chainId], + ); + + return keccak256(fullEncodedData); +} + +export function isUserOpV06( + userOp: UserOperationType | UserOperationStruct, +): boolean { + return "initCode" in userOp; +} diff --git a/src/common/entrypoint-v7/abiv7.ts b/src/common/entrypoint-v7/abiv7.ts new file mode 100644 index 00000000..e30c04cb --- /dev/null +++ b/src/common/entrypoint-v7/abiv7.ts @@ -0,0 +1,657 @@ +export const ENTRY_POINT_V07_ABI = [ + { + inputs: [ + { internalType: "bool", name: "success", type: "bool" }, + { internalType: "bytes", name: "ret", type: "bytes" }, + ], + name: "DelegateAndRevert", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "opIndex", type: "uint256" }, + { internalType: "string", name: "reason", type: "string" }, + ], + name: "FailedOp", + type: "error", + }, + { + inputs: [ + { internalType: "uint256", name: "opIndex", type: "uint256" }, + { internalType: "string", name: "reason", type: "string" }, + { internalType: "bytes", name: "inner", type: "bytes" }, + ], + name: "FailedOpWithRevert", + type: "error", + }, + { + inputs: [{ internalType: "bytes", name: "returnData", type: "bytes" }], + name: "PostOpReverted", + type: "error", + }, + { inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" }, + { + inputs: [{ internalType: "address", name: "sender", type: "address" }], + name: "SenderAddressResult", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "aggregator", type: "address" }], + name: "SignatureValidationFailed", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "userOpHash", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "factory", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "paymaster", + type: "address", + }, + ], + name: "AccountDeployed", + type: "event", + }, + { anonymous: false, inputs: [], name: "BeforeExecution", type: "event" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "totalDeposit", + type: "uint256", + }, + ], + name: "Deposited", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "userOpHash", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "revertReason", + type: "bytes", + }, + ], + name: "PostOpRevertReason", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "aggregator", + type: "address", + }, + ], + name: "SignatureAggregatorChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "totalStaked", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "unstakeDelaySec", + type: "uint256", + }, + ], + name: "StakeLocked", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "withdrawTime", + type: "uint256", + }, + ], + name: "StakeUnlocked", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "withdrawAddress", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "StakeWithdrawn", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "userOpHash", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "paymaster", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + { indexed: false, internalType: "bool", name: "success", type: "bool" }, + { + indexed: false, + internalType: "uint256", + name: "actualGasCost", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "actualGasUsed", + type: "uint256", + }, + ], + name: "UserOperationEvent", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "userOpHash", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + ], + name: "UserOperationPrefundTooLow", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "userOpHash", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "revertReason", + type: "bytes", + }, + ], + name: "UserOperationRevertReason", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "withdrawAddress", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "Withdrawn", + type: "event", + }, + { + inputs: [ + { internalType: "uint32", name: "unstakeDelaySec", type: "uint32" }, + ], + name: "addStake", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "target", type: "address" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + name: "delegateAndRevert", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "depositTo", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "deposits", + outputs: [ + { internalType: "uint256", name: "deposit", type: "uint256" }, + { internalType: "bool", name: "staked", type: "bool" }, + { internalType: "uint112", name: "stake", type: "uint112" }, + { internalType: "uint32", name: "unstakeDelaySec", type: "uint32" }, + { internalType: "uint48", name: "withdrawTime", type: "uint48" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "getDepositInfo", + outputs: [ + { + components: [ + { internalType: "uint256", name: "deposit", type: "uint256" }, + { internalType: "bool", name: "staked", type: "bool" }, + { internalType: "uint112", name: "stake", type: "uint112" }, + { internalType: "uint32", name: "unstakeDelaySec", type: "uint32" }, + { internalType: "uint48", name: "withdrawTime", type: "uint48" }, + ], + internalType: "struct IStakeManager.DepositInfo", + name: "info", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "sender", type: "address" }, + { internalType: "uint192", name: "key", type: "uint192" }, + ], + name: "getNonce", + outputs: [{ internalType: "uint256", name: "nonce", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes", name: "initCode", type: "bytes" }], + name: "getSenderAddress", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "sender", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "bytes", name: "initCode", type: "bytes" }, + { internalType: "bytes", name: "callData", type: "bytes" }, + { + internalType: "bytes32", + name: "accountGasLimits", + type: "bytes32", + }, + { + internalType: "uint256", + name: "preVerificationGas", + type: "uint256", + }, + { internalType: "bytes32", name: "gasFees", type: "bytes32" }, + { internalType: "bytes", name: "paymasterAndData", type: "bytes" }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct PackedUserOperation", + name: "userOp", + type: "tuple", + }, + ], + name: "getUserOpHash", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { internalType: "address", name: "sender", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "bytes", name: "initCode", type: "bytes" }, + { internalType: "bytes", name: "callData", type: "bytes" }, + { + internalType: "bytes32", + name: "accountGasLimits", + type: "bytes32", + }, + { + internalType: "uint256", + name: "preVerificationGas", + type: "uint256", + }, + { internalType: "bytes32", name: "gasFees", type: "bytes32" }, + { + internalType: "bytes", + name: "paymasterAndData", + type: "bytes", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct PackedUserOperation[]", + name: "userOps", + type: "tuple[]", + }, + { + internalType: "contract IAggregator", + name: "aggregator", + type: "address", + }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct IEntryPoint.UserOpsPerAggregator[]", + name: "opsPerAggregator", + type: "tuple[]", + }, + { internalType: "address payable", name: "beneficiary", type: "address" }, + ], + name: "handleAggregatedOps", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { internalType: "address", name: "sender", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "bytes", name: "initCode", type: "bytes" }, + { internalType: "bytes", name: "callData", type: "bytes" }, + { + internalType: "bytes32", + name: "accountGasLimits", + type: "bytes32", + }, + { + internalType: "uint256", + name: "preVerificationGas", + type: "uint256", + }, + { internalType: "bytes32", name: "gasFees", type: "bytes32" }, + { internalType: "bytes", name: "paymasterAndData", type: "bytes" }, + { internalType: "bytes", name: "signature", type: "bytes" }, + ], + internalType: "struct PackedUserOperation[]", + name: "ops", + type: "tuple[]", + }, + { internalType: "address payable", name: "beneficiary", type: "address" }, + ], + name: "handleOps", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint192", name: "key", type: "uint192" }], + name: "incrementNonce", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes", name: "callData", type: "bytes" }, + { + components: [ + { + components: [ + { internalType: "address", name: "sender", type: "address" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { + internalType: "uint256", + name: "verificationGasLimit", + type: "uint256", + }, + { + internalType: "uint256", + name: "callGasLimit", + type: "uint256", + }, + { + internalType: "uint256", + name: "paymasterVerificationGasLimit", + type: "uint256", + }, + { + internalType: "uint256", + name: "paymasterPostOpGasLimit", + type: "uint256", + }, + { + internalType: "uint256", + name: "preVerificationGas", + type: "uint256", + }, + { internalType: "address", name: "paymaster", type: "address" }, + { + internalType: "uint256", + name: "maxFeePerGas", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxPriorityFeePerGas", + type: "uint256", + }, + ], + internalType: "struct EntryPoint.MemoryUserOp", + name: "mUserOp", + type: "tuple", + }, + { internalType: "bytes32", name: "userOpHash", type: "bytes32" }, + { internalType: "uint256", name: "prefund", type: "uint256" }, + { internalType: "uint256", name: "contextOffset", type: "uint256" }, + { internalType: "uint256", name: "preOpGas", type: "uint256" }, + ], + internalType: "struct EntryPoint.UserOpInfo", + name: "opInfo", + type: "tuple", + }, + { internalType: "bytes", name: "context", type: "bytes" }, + ], + name: "innerHandleOp", + outputs: [ + { internalType: "uint256", name: "actualGasCost", type: "uint256" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "uint192", name: "", type: "uint192" }, + ], + name: "nonceSequenceNumber", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }], + name: "supportsInterface", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "unlockStake", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address payable", + name: "withdrawAddress", + type: "address", + }, + ], + name: "withdrawStake", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address payable", + name: "withdrawAddress", + type: "address", + }, + { internalType: "uint256", name: "withdrawAmount", type: "uint256" }, + ], + name: "withdrawTo", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { stateMutability: "payable", type: "receive" }, +] as const; diff --git a/src/common/entrypoint-v7/test/PackedUserOperation.test.ts b/src/common/entrypoint-v7/test/PackedUserOperation.test.ts new file mode 100644 index 00000000..55ec59f6 --- /dev/null +++ b/src/common/entrypoint-v7/test/PackedUserOperation.test.ts @@ -0,0 +1,117 @@ +import _ from "lodash"; +import { + PackedUserOperation, + getUserOpHash, + packUserOperation, + COMMON_ENTRYPOINT_V7_ADDRESSES, + unpackUserOperation, +} from "../PackedUserOperation"; +import { UserOperationStruct } from "../../types"; + +describe("PackedUserOperation simple test", () => { + const ENTRYPOINT_V7_ADDRESS = COMMON_ENTRYPOINT_V7_ADDRESSES[0]; + const chainId = 84532n; + + describe("user op convertion 1", () => { + const sender = "0x1234567890abcdef1234567890abcdef12345678"; + const callData = "0x"; + const correctHash = + "0x42bfcf0ecae5b274838cba8bbe6c6012f2259014607d22248b3414337c26f25d"; + const correctAccountGasLimits = + "0x0000000000000000000000000007a12000000000000000000000000000017ed0"; + const correctGasFees = + "0x000000000000000000000000000f424000000000000000000000000000c8bf5e"; + const userOp: UserOperationStruct = { + sender, + nonce: 1n, + // initCode: "0x", + callData, + // paymasterAndData: "0x", + callGasLimit: 98000n, + verificationGasLimit: 500000n, + preVerificationGas: 21000n, + maxPriorityFeePerGas: 1000000n, + maxFeePerGas: 13156190n, + signature: "0x", + } as unknown as UserOperationStruct; + + it("user op with simple fields", async () => { + const packedUserOp = packUserOperation(userOp); + expect(packedUserOp.sender).toEqual(sender); + expect(packedUserOp.accountGasLimits).toEqual(correctAccountGasLimits); + expect(packedUserOp.gasFees).toEqual(correctGasFees); + expect(packedUserOp.callData).toEqual(callData); + + expect( + getUserOpHash(packedUserOp, ENTRYPOINT_V7_ADDRESS, chainId), + ).toEqual(correctHash); + }); + + it("unpacked is the same as original user op", async () => { + const packedUserOp = packUserOperation(userOp); + + const unpackedUserOp = unpackUserOperation(packedUserOp); + + expect(packedUserOp.sender).toEqual(sender); + expect(packedUserOp.accountGasLimits).toEqual(correctAccountGasLimits); + expect(packedUserOp.gasFees).toEqual(correctGasFees); + expect(packedUserOp.callData).toEqual(callData); + + expect(_.isEqual(unpackedUserOp, userOp)).toBeTruthy(); + }); + + it("fail user op with simple fields", async () => { + userOp.verificationGasLimit = 0n; + + const packedUserOp = packUserOperation(userOp); + expect(packedUserOp.sender).toEqual(sender); + expect( + packedUserOp.accountGasLimits !== correctAccountGasLimits, + ).toBeTruthy(); + expect( + getUserOpHash(packedUserOp, ENTRYPOINT_V7_ADDRESS, chainId) !== + correctHash, + ).toBeTruthy(); + }); + }); +}); + +describe("Real world example", () => { + const ENTRYPOINT_V7_ADDRESS = COMMON_ENTRYPOINT_V7_ADDRESSES[0]; + const chainId = 84532n; + + // https://sepolia.basescan.org/tx/0x49cc4128c297d1e2a4a1e3892f5b84a99092a46fbfac96fb0294269ea10b95b4 + describe("correct real world packed user op", () => { + const correctHash = + "0xec5d98b7b476f62ed958f6622f9424c3c7afbfb7df2f640b02f81341149b0828"; + const packedUserOp: PackedUserOperation = { + sender: "0x745B9Fe41556A7e008CF1cD0adF36deE70C7B272", + nonce: + 456025572262225600221800371845431683190880916353967002629830005410307768320n, + initCode: + "0xd703aae79538628d27099b8c4f621be4ccd142d5c5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e4190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001243c3b752b015c97aa67ba578e3c54ec5942a7563ea9130e4f5f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000145e9b5f95975d4b1ba30b4f2f681e7b9f79744c240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + callData: + "0xe9ae5c5301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000080f3b8c46381d5cf4b737742d5fe323b7caa43b1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064a23f2985000000000000000000000000b4aa062cc685e7e2b6881ed57fb830cd7d4bcf25000000000000000000000000b4aa062cc685e7e2b6881ed57fb830cd7d4bcf25000000000000000000000000000000000000000000000000000000006709462300000000000000000000000000000000000000000000000000000000", + accountGasLimits: + "0x000000000000000000000000000bd1590000000000000000000000000002bc6b", + preVerificationGas: 67461260n, + gasFees: + "0x000000000000000000000000000f424000000000000000000000000000a2621d", + paymasterAndData: + "0x9d0021a869f1ed3a661ffe8c9b41ec6244261d9800000000000000000000000000005002000000000000000000000000000000010000000000000000000000000000000000000000000000000000000066ba2e7e0000000000000000000000000000000000000000000000000000000000000000a7cb1023c8623b6681c8ec5ade2b84ddf3dd3be57ddf2ed110d66dd678b44e7f70de48cb261dbacf9f64d4a107978141063046245605e3deb6456ee9a620f6a91b", + signature: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000016000067b436cad8a6d025df6c82c5bb43fbf11fc5b9b700000000000000000000000000000000000000000000000000000000000000000000000000000000002a00006a6f069e2a08c2468e7724ab3250cdbfba14d4ffd76f56d3b6deabcf5c008c7f7484fb936bb1b24600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ece9ae5c5300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c1dd0953a8032819ea88738b6db941ef18d8bf301cdc4319de4666fb9d309a33ec4c174b6a8efe3c1e70a3061e244f9f04795f05d11a527c3ea23f14a8d10848b11c9597c7a0bdb4d2e853ea0db626027f325bc211a288eb3d18806f76281cf910d200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001b0091d0d18984646971ebd26d9faafd9224cf5e080268fc114917513cde6135e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042ffd178e56e0ac6e8bd77f97234796f7af3cab853f183826b1a673b8253af5c803c229ede6a3af9120daa721dad1b593b88ff3d13a0626b6a57c6cecdb44d64019a1b000000000000000000000000000000000000000000000000000000000000", + }; + + it("user op with simple fields", async () => { + const userOp = unpackUserOperation(packedUserOp); + expect(userOp.sender).toEqual( + "0x745B9Fe41556A7e008CF1cD0adF36deE70C7B272", + ); + expect(userOp.maxPriorityFeePerGas).toEqual(1000000n); // f4240 from gasFees + expect( + getUserOpHash(packedUserOp, ENTRYPOINT_V7_ADDRESS, chainId), + ).toEqual(correctHash); + }); + }); +}); diff --git a/src/common/gas-price/GasPrice.ts b/src/common/gas-price/GasPrice.ts index 0188a1c1..e187a016 100644 --- a/src/common/gas-price/GasPrice.ts +++ b/src/common/gas-price/GasPrice.ts @@ -1,6 +1,4 @@ -/* eslint-disable prefer-const */ -/* eslint-disable import/no-import-module-exports */ -import { formatUnits, toHex } from "viem"; +import { formatGwei, formatUnits, toHex } from "viem"; import { IEVMAccount } from "../../relayer/account"; import { ICacheService } from "../cache"; import { logger } from "../logger"; @@ -9,12 +7,15 @@ import { EVMRawTransactionType, NetworkBasedGasPriceType } from "../types"; import { GasPriceType } from "./types"; import { customJSONStringify } from "../utils"; import { IGasPriceService } from "./interface/IGasPriceService"; +import pino from "pino"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); + +// TODO: This must be refactored export class GasPriceService implements IGasPriceService { - chainId: number; + readonly chainId: number; networkService: INetworkService; @@ -71,7 +72,12 @@ export class GasPriceService implements IGasPriceService { * @param price the gas price */ async setGasPrice(gasType: GasPriceType, price: string) { - await this.cacheService.set(this.getGasFeeKey(gasType), price.toString()); + const key = this.getGasFeeKey(gasType); + logger.info( + { chainId: this.chainId, price, key }, + `GasPriceService.setGasPrice`, + ); + await this.cacheService.set(key, price.toString()); } /** @@ -82,27 +88,54 @@ export class GasPriceService implements IGasPriceService { async getGasPrice( gasType = GasPriceType.DEFAULT, ): Promise { + const _log = logger.child({ chainId: this.chainId }); let gasPrice: NetworkBasedGasPriceType; if (this.EIP1559SupportedNetworks.includes(this.chainId)) { const maxFeePerGas = BigInt(await this.getMaxFeeGasPrice(gasType)); - log.info( - `maxFeePerGas: ${maxFeePerGas} from cache on chainId: ${this.chainId}`, - ); const maxPriorityFeePerGas = BigInt( await this.getMaxPriorityFeeGasPrice(gasType), ); - log.info( - `maxPriorityFeePerGas: ${maxPriorityFeePerGas} from cache on chainId: ${this.chainId}`, - ); + _log.info({ maxPriorityFeePerGas }, `Cache read maxPriorityFeePerGas`); if (!maxFeePerGas || !maxPriorityFeePerGas) { + // get from network gasPrice = await this.networkService.getEIP1559FeesPerGas(); - log.info( - `gasPrice: ${customJSONStringify( - gasPrice, - )} from network on chainId: ${this.chainId}`, - ); + + _log.info({ gasPrice }, `Network gasPrice`); + + // get from blocknative + if (this.networkService.supportsBlockNative) { + const blockNativeFeesPerGas = + await this.networkService.getBlockNativeFeesPerGas(); + + if ( + blockNativeFeesPerGas.maxPriorityFeePerGas > + gasPrice.maxPriorityFeePerGas + ) { + const maxPriorityFeePerGasDiff = + blockNativeFeesPerGas.maxPriorityFeePerGas - + gasPrice.maxPriorityFeePerGas; + + logger.info( + `Blocknative returned higher maxPriorityFeePerGas, diff is: ${formatGwei(maxPriorityFeePerGasDiff)} gwei`, + ); + + gasPrice.maxPriorityFeePerGas = + blockNativeFeesPerGas.maxPriorityFeePerGas; + } + + if (blockNativeFeesPerGas.maxFeePerGas > gasPrice.maxFeePerGas) { + const maxFeePerGasDiff = + blockNativeFeesPerGas.maxFeePerGas - gasPrice.maxFeePerGas; + + logger.info( + `Blocknative returned higher maxFeePerGas, diff is: ${formatGwei(maxFeePerGasDiff)} gwei`, + ); + + gasPrice.maxFeePerGas = blockNativeFeesPerGas.maxFeePerGas; + } + } } else { gasPrice = { maxFeePerGas, @@ -153,10 +186,14 @@ export class GasPriceService implements IGasPriceService { * @param bumpingPercentage how much to bump by * @returns new bumped up transaction */ - getBumpedUpGasPrice( + async getBumpedUpGasPrice( pastGasPrice: NetworkBasedGasPriceType, bumpingPercentage: number, - ): NetworkBasedGasPriceType { + ): Promise { + if (this.networkService.supportsBlockNative) { + pastGasPrice = await this.networkService.getBlockNativeFeesPerGas(99); + } + let result; log.info(`Bumping up gas price by ${bumpingPercentage}%`); log.info( @@ -256,10 +293,13 @@ export class GasPriceService implements IGasPriceService { * @returns price of max fee gas */ async getMaxFeeGasPrice(gasType: GasPriceType): Promise { - const result = await this.cacheService.get( - this.getMaxFeePerGasKey(gasType), + const key = this.getMaxFeePerGasKey(gasType); + const maxFeePerGas = await this.cacheService.get(key); + logger.debug( + { chainId: this.chainId, maxFeePerGas, key }, + `GasPriceService.getMaxFeeGasPrice`, ); - return result; + return maxFeePerGas; } /** @@ -268,10 +308,13 @@ export class GasPriceService implements IGasPriceService { * @returns price of max priority fee gas */ async getMaxPriorityFeeGasPrice(gasType: GasPriceType): Promise { - const result = await this.cacheService.get( - this.getMaxPriorityFeePerGasKey(gasType), + const key = this.getMaxPriorityFeePerGasKey(gasType); + const maxPriorityFeePerGas = await this.cacheService.get(key); + logger.info( + { chainId: this.chainId, maxPriorityFeePerGas, key }, + `GasPriceService.getMaxPriorityFeeGasPrice`, ); - return result; + return maxPriorityFeePerGas; } /** @@ -291,19 +334,24 @@ export class GasPriceService implements IGasPriceService { * @param baseFeePerGas */ async setBaseFeePerGas(baseFeePerGas: string): Promise { - await this.cacheService.set( - this.getBaseFeePerGasKey(), - baseFeePerGas.toString(), + const key = this.getBaseFeePerGasKey(); + logger.debug( + { baseFeePerGas, chainId: this.chainId, key }, + `GasPriceService.setBaseFeePerGas`, ); + await this.cacheService.set(key, baseFeePerGas.toString()); } /** * Method returns EIP 1559 base fee per gas */ async getBaseFeePerGas(): Promise { - const baseFeePerGas = await this.cacheService.get( - this.getBaseFeePerGasKey(), + const key = this.getBaseFeePerGasKey(); + logger.info( + { chainId: this.chainId, key }, + `GasPriceService.getBaseFeePerGas`, ); + const baseFeePerGas = await this.cacheService.get(key); return BigInt(baseFeePerGas); } @@ -332,16 +380,26 @@ export class GasPriceService implements IGasPriceService { * Method sets up gas price manager */ async setup() { + const _log = logger.child({ chainId: this.chainId }); try { // check if the network supports EIP 1559 if (this.EIP1559SupportedNetworks.includes(this.chainId)) { let { maxFeePerGas, maxPriorityFeePerGas } = await this.networkService.getEIP1559FeesPerGas(); - log.info( - `setup maxFeePerGas: ${maxFeePerGas} from network on chainId: ${this.chainId}`, - ); - log.info( - `setup maxPriorityFeePerGas: ${maxPriorityFeePerGas} from network on chainId: ${this.chainId}`, + + // get from blocknative + if (this.networkService.supportsBlockNative) { + ({ maxPriorityFeePerGas, maxFeePerGas } = + await this.overrideWithBlocknative( + _log, + maxPriorityFeePerGas, + maxFeePerGas, + )); + } + + _log.info( + { maxFeePerGas, maxPriorityFeePerGas }, + `GasPriceService.setup(): EIP-1559 fees per gas`, ); if ( @@ -351,8 +409,10 @@ export class GasPriceService implements IGasPriceService { maxPriorityFeePerGas = 30000000000n; } + const baseFeePerGas = await this.networkService.getBaseFeePerGas(); + await this.setBaseFeePerGas(formatUnits(baseFeePerGas, 0)); + if (maxPriorityFeePerGas > maxFeePerGas) { - const baseFeePerGas = await this.networkService.getBaseFeePerGas(); maxFeePerGas = BigInt(2) * (baseFeePerGas + maxPriorityFeePerGas); } @@ -378,26 +438,59 @@ export class GasPriceService implements IGasPriceService { formatUnits(maxPriorityFeePerGas, 0), ); } - - const baseFeePerGas = await this.networkService.getBaseFeePerGas(); - log.info( - `setup baseFeePerGas: ${baseFeePerGas} from network on chainId: ${this.chainId}`, - ); - - await this.setBaseFeePerGas(formatUnits(baseFeePerGas, 0)); } else { const gasPrice = await this.networkService.getLegacyGasPrice(); - log.info( - `setup legacy gasPrice: ${customJSONStringify( - gasPrice, - )} from network on chainId: ${this.chainId}`, - ); await this.setGasPrice(GasPriceType.DEFAULT, formatUnits(gasPrice, 0)); } - } catch (error) { - log.error( - `Error in setting gas price for network id ${this.chainId} - ${error}`, + } catch (err) { + _log.error({ err }, `Error in setting up gas price`); + } + } + + /** + * Override network values with blocknative values if they are higher + * @param _log Pino logger + * @param maxPriorityFeePerGas maxPriorityFeePerGas returned by network + * @param maxFeePerGas maxFeePerGas returned by network + * @returns Network or blocknative values, whichever is higher + */ + private async overrideWithBlocknative( + _log: pino.Logger, + maxPriorityFeePerGas: bigint, + maxFeePerGas: bigint, + ) { + const blockNative = await this.networkService.getBlockNativeFeesPerGas(); + + if (blockNative.maxPriorityFeePerGas > maxPriorityFeePerGas) { + const maxPriorityFeePerGasDiff = + blockNative.maxPriorityFeePerGas - maxPriorityFeePerGas; + + _log.info( + { + network: maxPriorityFeePerGas, + blocknative: blockNative.maxPriorityFeePerGas, + diffGwei: formatGwei(maxPriorityFeePerGasDiff), + }, + `Blocknative returned higher maxPriorityFeePerGas`, ); + + maxPriorityFeePerGas = blockNative.maxPriorityFeePerGas; + } + + if (blockNative.maxFeePerGas > maxFeePerGas) { + const maxFeePerGasDiff = blockNative.maxFeePerGas - maxFeePerGas; + + _log.info( + { + network: maxFeePerGas, + blocknative: blockNative.maxFeePerGas, + diffGwei: formatGwei(maxFeePerGasDiff), + }, + `Blocknative returned higher maxFeePerGas`, + ); + + maxFeePerGas = blockNative.maxFeePerGas; } + return { maxPriorityFeePerGas, maxFeePerGas }; } } diff --git a/src/common/gas-price/interface/IGasPriceService.ts b/src/common/gas-price/interface/IGasPriceService.ts index b39c0b11..74fc6623 100644 --- a/src/common/gas-price/interface/IGasPriceService.ts +++ b/src/common/gas-price/interface/IGasPriceService.ts @@ -24,7 +24,7 @@ export interface IGasPriceService { getBumpedUpGasPrice( pastGasPrice: NetworkBasedGasPriceType, bumpingPercentage: number, - ): NetworkBasedGasPriceType; + ): Promise; setBaseFeePerGas(baseFeePerGas: string): Promise; getBaseFeePerGas(): Promise; diff --git a/src/common/gas-price/jobs/CacheFees.ts b/src/common/gas-price/jobs/CacheFees.ts index 6a6282b5..39ffc92e 100644 --- a/src/common/gas-price/jobs/CacheFees.ts +++ b/src/common/gas-price/jobs/CacheFees.ts @@ -1,9 +1,7 @@ -/* eslint-disable import/no-import-module-exports */ import { CronJob } from "cron"; import { GasPriceService } from "../GasPrice"; import { logger } from "../../logger"; -import { parseError } from "../../utils"; -import { formatHrtimeSeconds } from "../../utils/formatting"; +import { logMeasureTime } from "../../utils/timing"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -23,20 +21,17 @@ export class CacheFeesJob extends CronJob { private gasPriceService: GasPriceService, ) { super(schedule, async () => { - log.info(`Start ${this.pretty}`); + const { chainId } = this.gasPriceService; + const _log = log.child({ chainId, schedule }); + _log.info(`Start CacheFeesJob`); + try { - const start = process.hrtime(); - await gasPriceService.setup(); - const end = process.hrtime(start); - log.info(`Finish ${this.pretty} after=${formatHrtimeSeconds(end)}s`); + logMeasureTime(_log, `Finish CacheFeesJob`, async () => { + await gasPriceService.setup(); + }); } catch (err) { - log.error(`Error running ${this.pretty}: ${parseError(err)} `); + _log.error({ err }, `Error running CacheFees job`); } }); } - - // Get pretty name for logging - public get pretty(): string { - return `CacheFeesJob(chainID: ${this.gasPriceService.chainId}, schedule: '${this.schedule}')`; - } } diff --git a/src/common/interface/index.ts b/src/common/interface/index.ts index 3d4dcb95..535237b6 100644 --- a/src/common/interface/index.ts +++ b/src/common/interface/index.ts @@ -1,15 +1,9 @@ -import { ConsumeMessage } from "amqplib"; +import { ConsumeMessage, Connection } from "amqplib"; export interface IQueue { chainId: number; - connect(): Promise; + connect(connection: Connection): Promise; publish(arg0: TransactionMessageType): Promise; consume(onMessageReceived: () => void): Promise; ack(arg0: ConsumeMessage): Promise; } - -export interface IRetryPolicy { - maxTries: number; - shouldRetry: (err: any) => Promise; - incrementTry: () => void; -} diff --git a/src/common/logger/index.ts b/src/common/logger/index.ts index cc201557..a6822a59 100644 --- a/src/common/logger/index.ts +++ b/src/common/logger/index.ts @@ -1,13 +1,21 @@ import pino from "pino"; -const logger = pino({ - // level: 'info', // Set the log level - transport: { - target: "pino-pretty", - options: { - colorize: true, - }, - }, // ... other options +// Use sync mode in development to avoid out-of-order logs +const destination = pino.destination({ + sync: process.env.NODE_ENV === "development", }); +// Use the pretty logger in development, and JSON logger in production +const logger = + process.env.NODE_ENV === "development" + ? pino({ + transport: { + target: "pino-pretty", + options: { + colorize: true, + }, + }, + }) + : pino({}, destination); + export { logger }; diff --git a/src/common/network/EVMNetworkService.test.ts b/src/common/network/EVMNetworkService.test.ts index 87fd9f08..8206ff3c 100644 --- a/src/common/network/EVMNetworkService.test.ts +++ b/src/common/network/EVMNetworkService.test.ts @@ -1,16 +1,45 @@ import { EVMNetworkService } from "./EVMNetworkService"; describe("EVMNetworkService", () => { - const networkService = new EVMNetworkService({ chainId: 137, rpcUrl: "" }); + // share fixture values for the test + const chainId = 11155111; + const rpcUrls = [ + "https://eth-sepolia.g.alchemy.com/v2/2d7XL_fP43hrJ6nMRVybXpK8Ocw5u1-b", + ]; + const mevProtectedRpcUrl = + "https://rpc-sepolia.flashbots.net/fast?url=https://eth-sepolia.g.alchemy.com/v2/2d7XL_fP43hrJ6nMRVybXpK8Ocw5u1-b"; - describe("waitForTransaction", () => { - // this test was added while working on a bugfix where our Bundler was crashing, because of an unhandled promise rejection - it("should handle unrecoverable network errors without crashing or retrying", async () => { - expect(networkService).toBeDefined(); + describe("constructor", () => { + it("should initialize properly", () => { + const networkService = new EVMNetworkService({ + chainId, + rpcUrls, + mevProtectedRpcUrl, + }); - expect(networkService.waitForTransaction("", "")).rejects.toContain( - "ECONNREFUSED", // we are using a fake URL so this should throw ECONNREFUSED - ); - }, 5000); // set a timeout of 5 seconds, because this should fail immediately without retrying + expect(networkService.chainId).toEqual(chainId); + expect(networkService.rpcUrls).toEqual(rpcUrls); + expect(networkService.mevProtectedRpcUrl).toEqual(mevProtectedRpcUrl); + }); + + it.todo("should construct with no parameters (use defaults)"); + }); + + describe("toJSON", () => { + const networkService = new EVMNetworkService({ + chainId, + rpcUrls, + mevProtectedRpcUrl, + }); + + it("should return the short JSON representation with API keys hidden", () => { + expect(networkService.toJSON()).toEqual({ + chainId, + rpcUrls: ["eth-sepolia.g.alchemy.com"], + mevProtectedRpcUrl: "rpc-sepolia.flashbots.net", + checkForReceiptIntervalMs: 1000, + checkForReceiptTimeoutMs: 60000, + }); + }); }); }); diff --git a/src/common/network/EVMNetworkService.ts b/src/common/network/EVMNetworkService.ts index 42855787..c875b1ff 100644 --- a/src/common/network/EVMNetworkService.ts +++ b/src/common/network/EVMNetworkService.ts @@ -1,95 +1,144 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable no-async-promise-executor */ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable @typescript-eslint/return-await */ -/* eslint-disable new-cap */ -// eslint-disable-next-line max-classes-per-file -import axios from "axios"; + +import axios, { AxiosResponse } from "axios"; +import axiosRetry from "axios-retry"; import { + Hex, PublicClient, Transaction, TransactionReceipt, createPublicClient, fallback, http, + parseGwei, + verifyMessage, } from "viem"; import { IEVMAccount } from "../../relayer/account"; -import { - AlchemyMethodType, - EVMRawTransactionType, - EthMethodType, -} from "../types"; +import { EVMRawTransactionType } from "../types"; import { INetworkService } from "./interface"; import { Type0TransactionGasPriceType, Type2TransactionGasPriceType, } from "./types"; import { logger } from "../logger"; -import { customJSONStringify, parseError } from "../utils"; -import { config } from "../../config"; +import { parseError } from "../utils"; +import { BLOCKCHAINS } from "../constants"; +import nodeconfig from "config"; +import { hideRpcUrlApiKey } from "./utils"; +import { FlashbotsClient, FlashbotsOptions } from "./FlashbotsClient"; +import { EthMethods } from "../../server/api/methods/eth"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -const LOAD_BALANCER_DEFAULT = { - rank: { - interval: 60_000, - sampleCount: 5, - timeout: 500, - weights: { - latency: 0.3, - stability: 0.7, - }, - }, -}; - -class ErrorCheckingTransactionReceipt extends Error { - constructor( - error: any, - transactionHash: string, - transactionId: string, - chainId: number, - ) { - super( - `Error checking transaction receipt: ${parseError( - error, - )} for transactionHash: ${transactionHash} on transactionId: ${transactionId} on chainId: ${chainId}`, - ); - } +interface IEVMNetworkServiceOptions { + chainId: number; + rpcUrls?: string[]; + mevProtectedRpcUrl?: string; + checkForReceiptIntervalMs?: number; + checkForReceiptTimeoutMs?: number; + flashbots?: FlashbotsOptions; } export class EVMNetworkService implements INetworkService { - chainId: number; - - rpcUrl: string; + readonly chainId: number; + // These are regular RPC URL providers we use for most calls + rpcUrls: string[]; provider: PublicClient; - constructor(options: { chainId: number; rpcUrl: string }) { - this.chainId = options.chainId; - this.rpcUrl = options.rpcUrl; + // This is an optional MEV-protected RPC URL if the chain has a private mempool solution like Flashbots + mevProtectedRpcUrl: string | undefined; + + // Interval in milliseconds to check for transaction receipt + private checkForReceiptIntervalMs: number; + + // Stop checking for receipt after this timeout + private checkForReceiptTimeoutMs: number; + + constructor( + { + chainId, + rpcUrls = getRpcUrls(chainId), + mevProtectedRpcUrl = getDefaultMevProtectedRpcUrl(chainId), + checkForReceiptIntervalMs = getCheckForReceiptIntervalMs(chainId), + checkForReceiptTimeoutMs = getCheckReceiptTimeoutMs(chainId), + }: IEVMNetworkServiceOptions, + // A Flashbots client for sending transactions to a private mempool + public flashbots?: FlashbotsClient, + // A private axios client with retry mechanism to handle flaky requests + private axiosClient = axios.create(), + ) { + this.chainId = chainId; - const providers = config.chains.providers[this.chainId]; - if (!providers) { + // Sanity check if the rpcUrls are not passed as an argument and missing from the default config + this.rpcUrls = rpcUrls; + if (this.rpcUrls.length === 0) { throw new Error( - `No providers found for chainId: ${this.chainId} in the config`, + `No RPC URLs set for chainId: ${this.chainId} in the config`, ); } - if (providers.length > 1) { + // If we have only one RPC URL, we create just an ordinary 'http' transport + // otherwise we use the 'fallback' transport for improved reliability in case of RPC provider issues + if (this.rpcUrls.length === 1) { this.provider = createPublicClient({ - transport: fallback( - config.chains.providers[this.chainId].map((p) => http(p.url)), - LOAD_BALANCER_DEFAULT, - ), + transport: http(this.rpcUrls[0]), }); } else { this.provider = createPublicClient({ - transport: http(providers[0].url), + transport: fallback( + this.rpcUrls.map((url) => http(url)), + nodeconfig.get("networkService.viemFallbackTransport.rank"), + ), }); } + + // If provided a MEV-protected RPC URL we use it for sending tx's to a private mempool to avoid frontrunning + this.mevProtectedRpcUrl = mevProtectedRpcUrl; + + // Check for the transaction receipt every `checkForReceiptIntervalMs` milliseconds + this.checkForReceiptIntervalMs = checkForReceiptIntervalMs; + + // Stop checking for receipt after this timeout + this.checkForReceiptTimeoutMs = checkForReceiptTimeoutMs; + + // Add a retry plugin to the axios client to handle flaky requests with exponential backoff + axiosRetry(this.axiosClient, { + retries: 3, + retryDelay: axiosRetry.exponentialDelay, + }); + + // Flashbots configuration for sending transactions to a private mempool + this.flashbots = flashbots; + } + + /** + * Getter for an rpcUrl, used for backwards compatibility when we supported only 1 per network + * @returns the first RPC URL from the list + */ + public get rpcUrl(): string { + return this.rpcUrls[0]; + } + + /** + * Used for pretty printing the network service configuration + * @returns JSON stringified object with API keys removed + */ + toJSON(): IEVMNetworkServiceOptions { + return { + chainId: this.chainId, + rpcUrls: this.rpcUrls.map((rpcUrl) => hideRpcUrlApiKey(rpcUrl)), + mevProtectedRpcUrl: this.mevProtectedRpcUrl + ? hideRpcUrlApiKey(this.mevProtectedRpcUrl) + : undefined, + checkForReceiptIntervalMs: this.checkForReceiptIntervalMs, + checkForReceiptTimeoutMs: this.checkForReceiptTimeoutMs, + }; } async sendRpcCall(method: string, params: Array): Promise { @@ -99,29 +148,43 @@ export class EVMNetworkService jsonrpc: "2.0", id: Date.now(), }; - const response = await axios.post(this.rpcUrl, requestData); + + const _log = log.child({ + rpcUrl: hideRpcUrlApiKey(this.rpcUrl), + requestData, + chainId: this.chainId, + }); + + let response: AxiosResponse; + try { + response = await axios.post(this.rpcUrl, requestData); + } catch (err) { + _log.error({ err }, `Error in EVMNetworkService.sendRpcCall`); + throw err; + } const { data } = response; - log.info( - `data from RPC call: ${customJSONStringify( - data, - )} received on JSON RPC Method: ${method} with params: ${customJSONStringify( - params, - )} on chainId: ${this.chainId}`, - ); + _log.debug({ result: data.result }, `RPC response received`); if (!data) { - log.error(`RPC Call failed without data on chainId: ${this.chainId}`); + _log.error(`RPC Call returned no data`); return null; } if (data.error) { - log.error( - `RPC called returned error on chainId: ${this.chainId} with code: ${data.error.code} and message: ${data.error.message}`, + const { code, message } = data.error; + _log.error( + { + code, + message, + }, + `RPC Call returned an error`, ); + if ( - method === EthMethodType.ETH_CALL || - method === EthMethodType.ESTIMATE_GAS + method === EthMethods.eth_call || + method === EthMethods.eth_estimateGas ) { return data; } + throw new Error(data.error.message); } return data.result; @@ -142,13 +205,24 @@ export class EVMNetworkService } async getLegacyGasPrice(): Promise { - return BigInt(await this.sendRpcCall(EthMethodType.GAS_PRICE, [])); + return BigInt(await this.sendRpcCall(EthMethods.eth_gasPrice, [])); } async getEIP1559FeesPerGas(): Promise { + // Using viem's estimateFeesPerGas instead of raw RPC call as Ankr sometimes + // gives unexpected errors + if (this.chainId === BLOCKCHAINS.GNOSIS_MAINNET) { + const { maxFeePerGas, maxPriorityFeePerGas } = + await this.provider.estimateFeesPerGas(); + + return { + maxFeePerGas: maxFeePerGas as bigint, + maxPriorityFeePerGas: maxPriorityFeePerGas as bigint, + }; + } const maxFeePerGasPromise = this.getLegacyGasPrice(); const maxPriorityFeePerGasPromise = this.sendRpcCall( - EthMethodType.MAX_PRIORITY_FEE_PER_GAS, + EthMethods.eth_maxPriorityFeePerGas, [], ); const [maxFeePerGas, maxPriorityFeePerGas] = await Promise.all([ @@ -162,8 +236,48 @@ export class EVMNetworkService }; } + async getBlockNativeFeesPerGas(confidenceLevel?: number) { + if (!this.supportsBlockNative) { + throw new Error(`Blocknative not supported for chainId: ${this.chainId}`); + } + + if (!nodeconfig.has("blocknative.apiKey")) { + throw new Error("Blocknative API key not set in the config"); + } + + confidenceLevel = + confidenceLevel || nodeconfig.get("blocknative.confidenceLevel"); + + const blockNativeGasFees = await this.axiosClient.get( + `https://api.blocknative.com/gasprices/blockprices?chainId=${this.chainId}&confidenceLevels=${confidenceLevel}`, + { + headers: { + Authorization: nodeconfig.get("blocknative.apiKey"), + }, + }, + ); + logger.info( + { blockNativeGasFees: blockNativeGasFees.data }, + `Blocknative gas fees received`, + ); + + const { maxFeePerGas, maxPriorityFeePerGas } = + blockNativeGasFees.data.blockPrices[0].estimatedPrices[0]; + + return { + maxFeePerGas: parseGwei(maxFeePerGas.toString()), + maxPriorityFeePerGas: parseGwei(maxPriorityFeePerGas.toString()), + }; + } + + public get supportsBlockNative() { + return nodeconfig + .get("blocknative.supportedNetworks") + .includes(this.chainId); + } + async getBalance(address: string): Promise { - const balance = await this.sendRpcCall(EthMethodType.GET_BALANCE, [ + const balance = await this.sendRpcCall(EthMethods.eth_getBalance, [ address, "latest", ]); @@ -182,26 +296,33 @@ export class EVMNetworkService async estimateGas( params: any, // TODO type define params ): Promise { - return await this.sendRpcCall(EthMethodType.ESTIMATE_GAS, params); + return await this.sendRpcCall(EthMethods.eth_estimateGas, params); } async ethCall(params: any): Promise { - return await this.sendRpcCall(EthMethodType.ETH_CALL, params); + return await this.sendRpcCall(EthMethods.eth_call, params); } /** * Get the nonce of the user * @param address address - * @param pendingNonce include the nonce of the pending transaction + * @param pending include the nonce of the pending transaction * @returns by default returns the next nonce of the address * if pendingNonce is set to false, returns the nonce of the mined transaction */ - async getNonce(address: string, pendingNonce = true): Promise { - const params = pendingNonce ? [address, "pending"] : [address]; - return await this.sendRpcCall(EthMethodType.GET_TRANSACTION_COUNT, params); + async getNonce(account: IEVMAccount, pending = true): Promise { + if (this.supportsFlashbots && this.flashbots != null) { + return this.flashbots.getNonce(account, pending); + } + + return this.getNetworkNonce(account, pending); + } + + async getNetworkNonce(account: IEVMAccount, pending = true): Promise { + const params = pending ? [account.address, "pending"] : [account.address]; + return this.sendRpcCall(EthMethods.eth_getTransactionCount, params); } - // eslint-disable-next-line class-methods-use-this async sendTransaction( rawTransactionData: EVMRawTransactionType, account: IEVMAccount, @@ -215,12 +336,20 @@ export class EVMNetworkService transactionHash: string, ): Promise { const response = await this.sendRpcCall( - EthMethodType.GET_TRANSACTION_RECEIPT, + EthMethods.eth_getTransactionReceipt, [transactionHash], ); return response; } + // TODO: Use this instead of checking for MEV protected RPC URL + public get supportsFlashbots(): boolean { + return ( + this.flashbots != null && + this.flashbots.options.supportedNetworks.includes(this.chainId) + ); + } + /** * @param transactionHash transaction hash * @returns receipt of the transaction once mined, else waits for the transaction to be mined @@ -231,18 +360,23 @@ export class EVMNetworkService // confirmations?: number, // timeout?: number, ): Promise { - log.info( - `Starting waitFortransaction polling on transactionHash: ${transactionHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + const _log = log.child({ + transactionHash, + transactionId, + chainId: this.chainId, + }); + + _log.info(`Start EVMNetworkService.waitForTransaction`); const response: TransactionReceipt | null = await new Promise( async (resolve, reject) => { - // Set interval to check every 1 second (adjust the interval as needed) + // how often we check for the receipt + const checkForReceiptIntervalMs = getCheckForReceiptIntervalMs( + this.chainId, + ); const intervalId = setInterval(async () => { try { - log.info( - `Polling started to fetch receipt for transactionHash: ${transactionHash} on transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.info(`Polling for receipt started`); const transactionReceipt = await this.getTransactionReceipt(transactionHash); @@ -253,20 +387,12 @@ export class EVMNetworkService if (isTransactionMined) { // Transaction resolved successfully - log.info( - `Transaction receipt: ${customJSONStringify( - transactionReceipt, - )} fetched for transactionHash: ${transactionHash} on transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); + _log.info({ transactionReceipt }, "Received transaction receipt"); clearInterval(intervalId); resolve(transactionReceipt); } else { // Transaction is still pending - log.info( - `Transaction is still pending for transactionHash: ${transactionHash} on transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.info(`Transaction is still pending`); } } catch (error) { const wrappedError = new ErrorCheckingTransactionReceipt( @@ -279,34 +405,35 @@ export class EVMNetworkService clearInterval(intervalId); reject(wrappedError); } - }, 1000); + }, checkForReceiptIntervalMs); // Uncomment the line below to stop the interval after a certain number of iterations (optional) - setTimeout( - () => { - clearInterval(intervalId); - reject( - new Error( - "Timeout: The transaction is taking too long to confirm.", - ), - ); - }, - 1 * 60 * 1000, - ); + const stopCheckingAfterMs = getCheckReceiptTimeoutMs(this.chainId); + + setTimeout(() => { + clearInterval(intervalId); + reject( + new Error( + "Timeout: The transaction is taking too long to confirm.", + ), + ); + }, stopCheckingAfterMs); }, ); - log.info( - `waitForTransactionReceipt from provider response: ${customJSONStringify( + + _log.info( + { response, - )} for transactionHash: ${transactionHash} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, + }, + `EVMNetworkService.waitForTransaction response received`, ); + if (response === null) { throw new Error( `Error in fetching transactionReceipt for transactionHash: ${transactionHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); } + return response; } @@ -319,19 +446,122 @@ export class EVMNetworkService return await this.provider.getTransaction({ hash: transactionHash as `0x${string}`, }); + // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { return null; } } - async getLatesBlockNumber(): Promise { + async getLatestBlockNumber(): Promise { const block = await this.provider.getBlock({ blockTag: "latest", }); return block.number; } - async runAlchemySimulation(params: any): Promise { - return await this.sendRpcCall(AlchemyMethodType.SIMULATE_EXECUTION, params); + // TODO: Move this to EVMAccount + async verifySignature( + address: string, + message: string, + signature: string, + ): Promise { + log.info( + `Verifying signature for address: ${address}, message: ${message}, signature: ${signature}`, + ); + + return verifyMessage({ + address: address as Hex, + message, + signature: signature as Hex, + }); + } +} + +/** + * Get the RPC URLs for the chain from the config + * @param chainId Chain Id + * @returns A list of RPC URLs for the chain (excluding MEV-protected URLs) + */ +export const getRpcUrls = (chainId: number) => { + return nodeconfig + .get>(`chains.providers.${chainId}`) + .filter((p) => p.type !== "mev-protected") + .map((p) => p.url); +}; + +/** + * Get the MEV-protected RPC URL for the chain from the config + * @param chainId Chain Id + * @returns MEV-protected RPC URL for the chain (usually Flashbots) + */ +export const getDefaultMevProtectedRpcUrl = (chainId: number) => { + return nodeconfig + .get>(`chains.providers.${chainId}`) + .find((p) => p.type === "mev-protected")?.url; +}; + +/** + * Try get the interval to check for transaction receipt from the config or fallback to default + * @param chainId Chain Id + * @returns Interval in milliseconds to check for transaction receipt + */ +const getCheckForReceiptIntervalMs = (chainId: number) => { + const defaultInterval = 1000; // 1 second + + return nodeconfig.has(`chains.checkForReceiptInterval.${chainId}`) + ? nodeconfig.get(`chains.checkForReceiptInterval.${chainId}`) + : defaultInterval; +}; + +/** + * Try get the timeout to stop checking for transaction receipt from the config or fallback to default + * @param chainId Chain Id + * @returns Timeout in milliseconds to stop checking for transaction receipt + */ +const getCheckReceiptTimeoutMs = (chainId: number) => { + const defaultTimeout = 60_000; // 60 seconds + + return nodeconfig.has(`chains.checkReceiptTimeout.${chainId}`) + ? nodeconfig.get(`chains.checkReceiptTimeout.${chainId}`) + : defaultTimeout; +}; + +export interface BlockNativeResponse { + system: string; + network: string; + unit: string; + maxPrice: number; + currentBlockNumber: number; + msSinceLastBlock: number; + blockPrices: BlockPrice[]; +} + +export interface BlockPrice { + blockNumber: number; + estimatedTransactionCount: number; + baseFeePerGas: number; + blobBaseFeePerGas: number; + estimatedPrices: EstimatedPrice[]; +} + +export interface EstimatedPrice { + confidence: number; + price: number; + maxPriorityFeePerGas: number; + maxFeePerGas: number; +} + +class ErrorCheckingTransactionReceipt extends Error { + constructor( + error: any, + transactionHash: string, + transactionId: string, + chainId: number, + ) { + super( + `Error checking transaction receipt: ${parseError( + error, + )} for transactionHash: ${transactionHash} on transactionId: ${transactionId} on chainId: ${chainId}`, + ); } } diff --git a/src/common/network/FlashbotsClient.ts b/src/common/network/FlashbotsClient.ts new file mode 100644 index 00000000..d40509e5 --- /dev/null +++ b/src/common/network/FlashbotsClient.ts @@ -0,0 +1,260 @@ +import axios, { AxiosInstance, AxiosResponse, isAxiosError } from "axios"; +import config from "config"; +import { + createPublicClient, + Hex, + http, + keccak256, + PublicClient, + toHex, +} from "viem"; +import path from "path"; +import axiosRetry from "axios-retry"; +import { logger } from "../logger"; +import { customJSONStringify } from "../utils"; +import { IEVMAccount } from "../../relayer/account/interface/IEVMAccount"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +/** + * Flashbots configuration options. + * @param sendTransactionUrl URL used for sending private transactions to Flashbots + * @param getTransactionUrl URL used for getting the status of a transaction + * @param getStatusIntervalDelay Delay multiplier for the status interval + */ +export interface FlashbotsOptions { + rpcUrl: string; + statusUrl: string; + getStatusIntervalDelay: number; + maxBlockWait: number; + supportedNetworks: number[]; +} + +/** + * Flashbots client for sending transactions and checking their status. + * @param options Flashbots configuration + * @param rpcClient PublicClient for getting the current block number + * @param httpClient Axios instance for sending requests + */ +export class FlashbotsClient { + constructor( + public options: FlashbotsOptions = getFlashbotsConfig(), + private rpcClient: Pick< + PublicClient, + "getBlockNumber" + > = createPublicClient({ + transport: http(options.rpcUrl), + }), + // TS hack to handle this axios problem: https://github.com/axios/axios/issues/5095 + private httpClient?: Pick & { + create: typeof axios.create; + }, + ) { + if (!httpClient) { + const newHttpClient = axios.create(); + axiosRetry(newHttpClient, { retryDelay: axiosRetry.exponentialDelay }); + // again we have to do 'as any' because of https://github.com/axios/axios/issues/5095 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.httpClient = newHttpClient as any; + } + } + + /** + * Waits for a Flashbots transaction to be included in a block. + * Polls the Flashbots status API until the transaction is included or times out. + * @param transactionHash Transaction hash + * @returns Flashbots status response + */ + async waitForTransaction( + transactionHash: string, + ): Promise { + const _log = log.child({ transactionHash }); + + if (!this.httpClient) { + throw new Error("FlashbotsClient httpClient is not initialized"); + } + + // get current block so we can determine the max block + const currentBlock = Number(await this.rpcClient.getBlockNumber()); + + // by default a Flashbots transaction times out if it's not included within 25 blocks + const maxBlock = currentBlock + this.options.maxBlockWait; + + // it should be safe to clone the client because axios.create just copies the config + // and we have no keep-alive connections + const statusClient = this.httpClient.create(); + + // configure the httpClient so it retries until max block is reached + // and performs exponential backoff to handle rate limiting + axiosRetry(statusClient, { + retries: 100, // Sanity check: this won't be reached because we have a retryCondition + // if the transaction is not included within 25 blocks, we should stop retrying + retryCondition: async () => { + const currentBlock = await this.rpcClient.getBlockNumber(); + return currentBlock < maxBlock; + }, + // this is how long we wait between retries + retryDelay: (retryCount) => { + return retryCount * this.options.getStatusIntervalDelay; // 5s, 10s, 15s, 30s, 60s, ... + }, + // this exists only to log each retry + onRetry: (retryCount, error) => { + const { code, message, cause } = error; + _log.warn( + { retryCount, maxBlock, code, message, cause }, + "Retrying Flashbots transaction status", + ); + }, + // we want to stop retrying if the tx is included or failed + validateResponse: (response: AxiosResponse): boolean => { + if (isFlashbotsStatusResponse(response.data)) { + return [ + FlashbotsTxStatus.INCLUDED, + FlashbotsTxStatus.FAILED, + ].includes(response.data.status); + } + + return false; + }, + }); + + try { + const statusPath = path.join(this.options.statusUrl, transactionHash); + _log.info({ maxBlock, statusPath }, "Waiting for Flashbots transaction"); + + const response = + await statusClient.get(statusPath); + + return response.data; + } catch (err) { + const label = "Error waiting for Flashbots transaction"; + if (isAxiosError(err)) { + const { message, code, cause } = err; + _log.error({ message, code, cause }, label); + } else { + _log.error({ err }, label); + } + throw err; + } + } + + /** + * Getting the nonce on flasbots requires a signature from the account + * See https://docs.flashbots.net/flashbots-protect/nonce-management + * @param account account to get the nonce for + * @param pending include the nonce of the pending transaction (default: true) + * @returns nonce of the account + */ + async getNonce(account: IEVMAccount, pending = true): Promise { + const { address } = account; + const _log = logger.child({ address, pending }); + + if (!this.httpClient) { + throw new Error("FlashbotsClient httpClient is not initialized"); + } + + _log.info(`Getting Flashbots nonce`); + + const body = JSON.stringify({ + jsonrpc: "2.0", + method: "eth_getTransactionCount", + params: pending ? [account.address, "pending"] : [account.address], + id: Date.now(), + }); + + const signedBody = await account.signMessage(keccak256(toHex(body))); + const signature = `${account.address}:${signedBody}`; + + const response = await this.httpClient.post( + this.options.rpcUrl, + body, + { + headers: { + "Content-Type": "application/json", + "X-Flashbots-Signature": signature, + }, + }, + ); + + const { data } = response; + + if (!isFlashbotsNonceResponse(data)) { + throw new Error( + `Invalid Flashbots nonce response: ${customJSONStringify(data)}`, + ); + } + + const nonce = parseInt(data.result, 16); + logger.info({ address: account.address }, `Flashbots nonce: ${nonce}`); + + return nonce; + } +} + +/** + * The default Flashbots configuration read from the config file. + * All values have defaults in default.json. + * A custom sendTransactionUrl (for example specifying a private read RPC URL) + * can (and should) be set in development.json or production.json. + * @returns Default Flashbots configuration + */ +export const getFlashbotsConfig = (): FlashbotsOptions => { + return { + rpcUrl: config.get("flashbots.rpcUrl"), + statusUrl: config.get("flashbots.statusUrl"), + getStatusIntervalDelay: config.get( + `flashbots.getStatusIntervalDelay`, + ), + maxBlockWait: config.get(`flashbots.maxBlockWait`), + supportedNetworks: config.get("flashbots.supportedNetworks"), + }; +}; + +export enum FlashbotsTxStatus { + PENDING = "PENDING", + INCLUDED = "INCLUDED", + FAILED = "FAILED", + CANCELLED = "CANCELLED", + UNKNOWN = "UNKNOWN", +} + +export interface FlashbotsStatusResponse { + status: FlashbotsTxStatus; + hash: string; + maxBlockNumber: number; + transaction: FlashbotsTransactionResponse; + fastMode: boolean; + seenInMempool: boolean; +} + +function isFlashbotsStatusResponse( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + response: any, +): response is FlashbotsStatusResponse { + return response && response.status; +} + +export interface FlashbotsTransactionResponse { + from: string; + to: string; + gasLimit: string; + maxFeePerGas: string; + maxPriorityFeePerGas: string; + nonce: string; + value: string; +} + +interface FlashbotsNonceResponse { + id: number; + result: Hex; + jsonrpc: "2.0"; +} + +function isFlashbotsNonceResponse( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + response: any, +): response is FlashbotsNonceResponse { + return response.result && typeof response.result === "string"; +} diff --git a/src/common/network/interface/INetworkService.ts b/src/common/network/interface/INetworkService.ts index 55b40ae7..72263d28 100644 --- a/src/common/network/interface/INetworkService.ts +++ b/src/common/network/interface/INetworkService.ts @@ -1,20 +1,34 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { PublicClient, Transaction, TransactionReceipt } from "viem"; import { Type0TransactionGasPriceType, Type2TransactionGasPriceType, } from "../types"; +import { IEVMAccount } from "../../../relayer/account"; +import { FlashbotsClient } from "../FlashbotsClient"; export interface INetworkService { chainId: number; + rpcUrls: string[]; rpcUrl: string; + mevProtectedRpcUrl?: string; provider: PublicClient; + supportsBlockNative: boolean; + flashbots?: FlashbotsClient; + supportsFlashbots: boolean; + + getBlockNativeFeesPerGas(confidenceLevel?: number): Promise<{ + maxFeePerGas: bigint; + maxPriorityFeePerGas: bigint; + }>; sendRpcCall(method: string, params: Array): Promise; getBaseFeePerGas(): Promise; getLegacyGasPrice(): Promise; getEIP1559FeesPerGas(): Promise; getBalance(address: string): Promise; - getNonce(address: string, pendingNonce?: boolean): Promise; + getNonce(account: IEVMAccount, pendingNonce?: boolean): Promise; + getNetworkNonce(account: IEVMAccount, pending?: boolean): Promise; estimateGas(params: any): Promise; sendTransaction( rawTransactionData: RawTransactionType, @@ -29,8 +43,7 @@ export interface INetworkService { confirmations?: number, timeout?: number, ): Promise; - getLatesBlockNumber(): Promise; + getLatestBlockNumber(): Promise; getTransaction(transactionHash: string): Promise; - runAlchemySimulation(params: any): Promise; ethCall(params: any): Promise; } diff --git a/src/common/network/utils.test.ts b/src/common/network/utils.test.ts new file mode 100644 index 00000000..f599f5f4 --- /dev/null +++ b/src/common/network/utils.test.ts @@ -0,0 +1,33 @@ +import { hideRpcUrlApiKey } from "./utils"; + +describe("network/utils", () => { + describe("hideApiKey", () => { + // These are not actual API keys, just random strings, but the hostnames are real + const testCases = [ + { + name: "private", + rpcUrl: + "https://eth-sepolia.g.alchemy.com/v2/2d7XL_fP43hrJ6nMRVybXpK8Ocw5u1-b", + expected: "eth-sepolia.g.alchemy.com", + }, + { + name: "flashbots", + rpcUrl: + "https://rpc-sepolia.flashbots.net/fast?url=https://eth-sepolia.g.alchemy.com/v2/4a8YU_sH56pkD2tPLWyqLvB9Jtq8j2-m", + expected: "rpc-sepolia.flashbots.net", + }, + // public RPC URLs should not have an API key, edge case + { + name: "public", + rpcUrl: "https://rpc-quicknode-holesky.morphl2.io", + expected: "rpc-quicknode-holesky.morphl2.io", + }, + ]; + + for (const { name, rpcUrl, expected } of testCases) { + it(`should hide the API key for a ${name} RPC URL`, () => { + expect(hideRpcUrlApiKey(rpcUrl)).toEqual(expected); + }); + } + }); +}); diff --git a/src/common/network/utils.ts b/src/common/network/utils.ts new file mode 100644 index 00000000..0cb32051 --- /dev/null +++ b/src/common/network/utils.ts @@ -0,0 +1,9 @@ +/** + * Used for logging the RPC URL used without exposing the API key. + * @param rpcUrl A URL that may contain an API key. + * @returns The hostname of the URL, with the API key removed. + */ +export function hideRpcUrlApiKey(rpcUrl: string): string { + const parsedUrl = new URL(rpcUrl); + return parsedUrl.hostname; +} diff --git a/src/common/notification/NotificationUtil.ts b/src/common/notification/NotificationUtil.ts index a52a6257..89be236f 100644 --- a/src/common/notification/NotificationUtil.ts +++ b/src/common/notification/NotificationUtil.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { IEVMAccount } from "../../relayer/account"; import { TransactionType } from "../types"; import { parseError } from "../utils"; diff --git a/src/common/notification/interface/ISlackNotificationService.ts b/src/common/notification/interface/ISlackNotificationService.ts index af9211bd..cb45b841 100644 --- a/src/common/notification/interface/ISlackNotificationService.ts +++ b/src/common/notification/interface/ISlackNotificationService.ts @@ -5,7 +5,7 @@ import { } from "../types"; export interface ISlackNotificationService { - web: WebClient; + web: WebClient | undefined; slackToken: string; slackChannel: string; diff --git a/src/common/notification/slack/SlackNotificationService.ts b/src/common/notification/slack/SlackNotificationService.ts index 23894c7c..7c879a20 100644 --- a/src/common/notification/slack/SlackNotificationService.ts +++ b/src/common/notification/slack/SlackNotificationService.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import { WebClient } from "@slack/web-api"; import { logger } from "../../logger"; import { ISlackNotificationService } from "../interface"; @@ -12,7 +11,7 @@ const log = logger.child({ }); export class SlackNotificationService implements ISlackNotificationService { - web: WebClient; + web: WebClient | undefined; slackToken: string; @@ -23,15 +22,11 @@ export class SlackNotificationService implements ISlackNotificationService { this.slackChannel = slackChannel; if (this.slackChannel && this.slackToken) { this.web = new WebClient(this.slackToken); - if (!this.web) { - throw new Error( - "Slack web client is not initialized. Check if slack configurations are present", - ); - } - } else { - throw new Error( - "Slack token or channel name for relayer node is not present in slack configuration", - ); + // if (!this.web) { + // throw new Error( + // "Slack web client is not initialized. Check if slack configurations are present", + // ); + // } } } @@ -65,11 +60,13 @@ export class SlackNotificationService implements ISlackNotificationService { async postMessage( postSlackMessageParams: PostSlackMessageParamsType, ): Promise { - const result = await this.web.chat.postMessage(postSlackMessageParams); - log.info(result); - log.info( - `Successfully sent message ${postSlackMessageParams.text} to Slack channel with id ${postSlackMessageParams.channel}`, - ); + if (this.web) { + const result = await this.web.chat.postMessage(postSlackMessageParams); + log.info(result); + log.info( + `Successfully sent message ${postSlackMessageParams.text} to Slack channel with id ${postSlackMessageParams.channel}`, + ); + } } /** diff --git a/src/common/queue/BundlerTransactionQueue.ts b/src/common/queue/BundlerTransactionQueue.ts index 656e65f6..e7bfd231 100644 --- a/src/common/queue/BundlerTransactionQueue.ts +++ b/src/common/queue/BundlerTransactionQueue.ts @@ -1,90 +1,131 @@ -/* eslint-disable import/no-import-module-exports */ -import amqp, { Channel, ConsumeMessage, Replies } from "amqplib"; -import { config } from "../../config"; +import { Channel, ConsumeMessage, Connection } from "amqplib"; import { logger } from "../logger"; -import { BundlerTransactionMessageType, TransactionType } from "../types"; +import { SendUserOperation, TransactionType } from "../types"; import { IQueue } from "./interface/IQueue"; -import { customJSONStringify, parseError } from "../utils"; +import { customJSONStringify } from "../utils"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -const queueUrl = process.env.BUNDLER_QUEUE_URL || config.queueUrl; +export class BundlerTransactionQueue implements IQueue { + readonly chainId: number; -export class BundlerTransactionQueue - implements IQueue -{ private channel!: Channel; - transactionType: TransactionType = TransactionType.BUNDLER; + private transactionType: TransactionType = TransactionType.BUNDLER; private exchangeName = `relayer_queue_exchange_${this.transactionType}`; private exchangeType = "direct"; - chainId: number; + private prefetch = 1; - queueName: string; + private exchangeKey: string; - msg!: ConsumeMessage | null; + private queueName: string; constructor(options: { chainId: number }) { this.chainId = options.chainId; - this.queueName = `relayer_queue_${this.transactionType}_${this.chainId}`; + this.exchangeKey = `chainid.${this.chainId}.type.${this.transactionType}`; + this.queueName = `relayer_queue_${this.chainId}_type_${this.transactionType}`; } - async connect() { - const connection = await amqp.connect(queueUrl); - if (!this.channel) { - this.channel = await connection.createChannel(); - this.channel.assertExchange(this.exchangeName, this.exchangeType, { - durable: true, - }); + async connect(connection: Connection) { + const _log = log.child({ + chainId: this.chainId, + transactionType: this.transactionType, + queueName: this.queueName, + exchangeKey: this.exchangeKey, + }); + + try { + if (!this.channel) { + this.channel = await connection.createChannel(); + this.channel + .assertExchange(this.exchangeName, this.exchangeType, { + durable: true, + }) + .catch((err) => { + _log.error({ err }, `assertExchange() failed`); + }); + } + } catch (err) { + _log.error({ err }, `Error while connecting to the queue`); } } - async publish(data: BundlerTransactionMessageType) { - const key = `chainid.${this.chainId}.type.${this.transactionType}`; - log.info( - `Publishing data to retry queue on chainId: ${this.chainId} and key ${key}`, - ); - this.channel.publish( - this.exchangeName, + async publish(data: SendUserOperation) { + const key = this.exchangeKey; + const _log = log.child({ + data, key, - Buffer.from(customJSONStringify(data)), - { - persistent: true, - }, - ); - return true; + transactionId: data.transactionId, + }); + + _log.info(`Publishing data to retry queue`); + + try { + this.channel.publish( + this.exchangeName, + key, + Buffer.from(customJSONStringify(data)), + { + persistent: true, + }, + ); + return true; + } catch (err) { + _log.error({ err }, `Error while publishing the data to the queue`); + return false; + } } async consume(onMessageReceived: () => void) { - log.info( - `[x] Setting up consumer for queue with chainId: ${this.chainId} for transaction type ${this.transactionType}`, - ); - this.channel.prefetch(1); + const _log = log.child({ + chainId: this.chainId, + transactionType: this.transactionType, + queueName: this.queueName, + exchangeKey: this.exchangeKey, + }); + + this.channel.prefetch(this.prefetch).catch((err) => { + _log.error({ err }, `Error while prefetching`); + }); + + _log.info(`Setting up consumer for queue`); try { // setup a consumer - const queue: Replies.AssertQueue = await this.channel.assertQueue( - `relayer_queue_${this.chainId}_type_${this.transactionType}`, - ); - const key = `chainid.${this.chainId}.type.${this.transactionType}`; - log.info( - `[*] Waiting for transactions on chainId: ${this.chainId} for transaction type ${this.transactionType}`, - ); - this.channel.bindQueue(queue.queue, this.exchangeName, key); + const queue = await this.channel.assertQueue(this.queueName); + + this.channel + .bindQueue(queue.queue, this.exchangeName, this.exchangeKey) + .catch((err) => { + _log.error({ err }, `Error while binding queue`); + }); + + _log.info(`Waiting for transactions...`); await this.channel.consume(queue.queue, onMessageReceived); return true; - } catch (error) { - log.error(parseError(error)); + } catch (err) { + _log.error({ err }, `Error while consuming queue`); return false; } } async ack(data: ConsumeMessage) { - this.channel.ack(data); + const _log = log.child({ + chainId: this.chainId, + transactionType: this.transactionType, + queueName: this.queueName, + exchangeKey: this.exchangeKey, + }); + + try { + this.channel.ack(data); + } catch (err) { + _log.error({ err }, `Error while acknowledging `); + } } } diff --git a/src/common/queue/RetryTransactionHandlerQueue.ts b/src/common/queue/RetryTransactionHandlerQueue.ts index 89777084..25a986f5 100644 --- a/src/common/queue/RetryTransactionHandlerQueue.ts +++ b/src/common/queue/RetryTransactionHandlerQueue.ts @@ -1,17 +1,14 @@ -/* eslint-disable import/no-import-module-exports */ -import amqp, { Channel, ConsumeMessage, Replies } from "amqplib"; -import { config } from "../../config"; +import { Channel, ConsumeMessage, Replies, Connection } from "amqplib"; +import nodeconfig from "config"; import { logger } from "../logger"; import { IQueue } from "./interface/IQueue"; import { RetryTransactionQueueData } from "./types"; -import { customJSONStringify, parseError } from "../utils"; +import { customJSONStringify } from "../utils"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -const { queueUrl } = config; - export class RetryTransactionHandlerQueue implements IQueue { @@ -29,85 +26,135 @@ export class RetryTransactionHandlerQueue msg!: ConsumeMessage | null; - constructor(options: { chainId: number; nodePathIndex: number }) { + private queueUrl: string; + + private retryTransactionInterval: number; + + constructor(options: { + chainId: number; + nodePathIndex: number; + queueUrl?: string; + }) { this.chainId = options.chainId; this.nodePathIndex = options.nodePathIndex; this.exchangeName = `retry_transaction_queue_exchange_${this.nodePathIndex}`; this.queueName = `retry_transaction_queue_${this.nodePathIndex}`; + this.queueUrl = nodeconfig.get("queueUrl"); + this.retryTransactionInterval = nodeconfig.has( + `chains.retryTransactionInterval.${this.chainId}`, + ) + ? nodeconfig.get(`chains.retryTransactionInterval.${this.chainId}`) + : 30_000; } - async connect() { - const connection = await amqp.connect(queueUrl); - if (!this.channel) { - this.channel = await connection.createChannel(); - this.channel.assertExchange(this.exchangeName, this.exchangeType, { - durable: true, - arguments: { - "x-delayed-type": "direct", - }, - }); + async connect(connection: Connection) { + const _log = log.child({ + chainId: this.chainId, + queueName: this.queueName, + exchangeName: this.exchangeName, + nodePathIndex: this.nodePathIndex, + }); + + try { + if (!this.channel) { + this.channel = await connection.createChannel(); + this.channel + .assertExchange(this.exchangeName, this.exchangeType, { + durable: true, + arguments: { + "x-delayed-type": "direct", + }, + }) + .catch((err) => { + _log.error({ err }, `Error while calling assertExchange()`); + }); + } + } catch (err) { + _log.error({ err }, `Error while connecting to the queue`); } } async publish(data: RetryTransactionQueueData) { const key = `retry_chainid.${this.chainId}_${this.nodePathIndex}`; - log.info( - `Publishing data to retry queue on chainId: ${ - this.chainId - } with interval ${ - config.chains.retryTransactionInterval[this.chainId] - } and key ${key}`, - ); - if (this.channel) { - this.channel.publish( - this.exchangeName, - key, - Buffer.from(customJSONStringify(data)), - { - persistent: true, - headers: { - "x-delay": config.chains.retryTransactionInterval[this.chainId], + const _log = log.child({ + key, + retryTransactionInterval: this.retryTransactionInterval, + transactionId: data.transactionId, + }); + + _log.info({ data }, `Publishing data to retry queue`); + + try { + if (this.channel) { + this.channel.publish( + this.exchangeName, + key, + Buffer.from(customJSONStringify(data)), + { + persistent: true, + headers: { + "x-delay": this.retryTransactionInterval, + }, }, - }, - ); - return true; + ); + return true; + } + return false; + } catch (err) { + _log.error({ err }, `Error while publishing the data to the queue`); + return false; } - return false; } async consume(onMessageReceived: () => void) { - log.info( - `[x] Setting up consumer for queue with chainId: ${this.chainId} for retry transaction queue`, - ); - this.channel.prefetch(1); + const _log = log.child({ + chainId: this.chainId, + queueName: this.queueName, + exchangeName: this.exchangeName, + nodePathIndex: this.nodePathIndex, + }); + + _log.info(`Setting up consumer for retry transaction queue`); + this.channel.prefetch(1).catch((err) => { + _log.error({ err }, `Error while prefetching`); + }); try { // setup a consumer const retryTransactionQueue: Replies.AssertQueue = await this.channel.assertQueue(`${this.queueName}_${this.chainId}`); const key = `retry_chainid.${this.chainId}_${this.nodePathIndex}`; - log.info( - `[*] Waiting for retry transactions on chainId: ${this.chainId}`, - ); + _log.info(`Waiting for retry transactions`); - this.channel.bindQueue( - retryTransactionQueue.queue, - this.exchangeName, - key, - ); + this.channel + .bindQueue(retryTransactionQueue.queue, this.exchangeName, key) + .catch((err) => { + _log.error({ err }, `Error while binging queue`); + }); await this.channel.consume( retryTransactionQueue.queue, onMessageReceived, ); return true; - } catch (error) { - log.error(parseError(error)); + } catch (err) { + _log.error({ err }, `Error while consuming retry transaction queue`); return false; } } async ack(data: ConsumeMessage) { - this.channel.ack(data); + const _log = log.child({ + chainId: this.chainId, + queueName: this.queueName, + exchangeName: this.exchangeName, + nodePathIndex: this.nodePathIndex, + }); + + try { + this.channel.ack(data); + } catch (err) { + _log.error({ err }, `Error while acknowledging message`); + } } } diff --git a/src/common/queue/interface/IQueue.ts b/src/common/queue/interface/IQueue.ts index aa2e96ca..535237b6 100644 --- a/src/common/queue/interface/IQueue.ts +++ b/src/common/queue/interface/IQueue.ts @@ -1,8 +1,8 @@ -import { ConsumeMessage } from "amqplib"; +import { ConsumeMessage, Connection } from "amqplib"; export interface IQueue { chainId: number; - connect(): Promise; + connect(connection: Connection): Promise; publish(arg0: TransactionMessageType): Promise; consume(onMessageReceived: () => void): Promise; ack(arg0: ConsumeMessage): Promise; diff --git a/src/common/queue/types/index.ts b/src/common/queue/types/index.ts index cbe09287..1a167855 100644 --- a/src/common/queue/types/index.ts +++ b/src/common/queue/types/index.ts @@ -10,6 +10,7 @@ export type RetryTransactionQueueData = { transactionId: string; rawTransaction: EVMRawTransactionType; walletAddress: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any metaData: any; relayerManagerName: string; }; diff --git a/src/common/relay-service/BundlerRelayService.ts b/src/common/relay-service/BundlerRelayService.ts index ca474fd9..750bb937 100644 --- a/src/common/relay-service/BundlerRelayService.ts +++ b/src/common/relay-service/BundlerRelayService.ts @@ -1,23 +1,16 @@ -/* eslint-disable import/no-import-module-exports */ -import { STATUSES } from "../../server/api/shared/middleware"; +import { STATUSES } from "../../server/api/shared/statuses"; import { IQueue } from "../interface"; import { logger } from "../logger"; -import { - BundlerTransactionMessageType, - RelayServiceResponseType, -} from "../types"; -import { parseError } from "../utils"; +import { SendUserOperation, RelayServiceResponseType } from "../types"; +import { customJSONStringify } from "../utils"; import { IRelayService } from "./interface/IRelayService"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -export class BundlerRelayService - implements IRelayService -{ - queue: IQueue; - constructor(queue: IQueue) { +export class BundlerRelayService implements IRelayService { + constructor(private queue: Pick, "publish">) { this.queue = queue; } @@ -26,12 +19,15 @@ export class BundlerRelayService * @param data raw transaction data received in the request * @returns transaction id */ - async sendTransactionToRelayer( - data: BundlerTransactionMessageType, + async sendUserOperation( + data: SendUserOperation, ): Promise { - log.info( - `Sending transaction to Bundler transaction queue with transactionId: ${data.transactionId}`, - ); + const _log = log.child({ + sendUserOperation: data, + }); + + _log.info(`Sending user operation to Bundler mempool`); + let response: RelayServiceResponseType; try { await this.queue.publish(data); @@ -39,11 +35,14 @@ export class BundlerRelayService code: STATUSES.SUCCESS, transactionId: data.transactionId, }; - } catch (error) { - log.error(parseError(error)); + } catch (err) { + _log.error( + { err }, + `Error while sending user operation to Bundler mempool`, + ); response = { code: STATUSES.INTERNAL_SERVER_ERROR, - error: `Internal server error: ${error}`, + error: `Internal server error: ${customJSONStringify(err)}`, }; } return response; diff --git a/src/common/relay-service/interface/IRelayService.ts b/src/common/relay-service/interface/IRelayService.ts index 9df56e26..e7c0b439 100644 --- a/src/common/relay-service/interface/IRelayService.ts +++ b/src/common/relay-service/interface/IRelayService.ts @@ -1,7 +1,7 @@ import { RelayServiceResponseType } from "../../types"; export interface IRelayService { - sendTransactionToRelayer( + sendUserOperation( data: TransactionMessageType, ): Promise; } diff --git a/src/common/service-manager/index.ts b/src/common/service-manager/index.ts index 9aa44289..27ef5f14 100644 --- a/src/common/service-manager/index.ts +++ b/src/common/service-manager/index.ts @@ -1,8 +1,6 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable no-await-in-loop */ +import nodeconfig from "config"; import { getContract, parseEther } from "viem"; -import { chain } from "lodash"; -import { ENTRY_POINT_ABI } from "entry-point-gas-estimations"; +import amqp from "amqplib"; import { config } from "../../config"; import { EVMAccount, IEVMAccount } from "../../relayer/account"; import { BundlerConsumer } from "../../relayer/consumer"; @@ -18,29 +16,40 @@ import { EVMTransactionService } from "../../relayer/transaction-service"; import { RedisCacheService } from "../cache"; import { Mongo, TransactionDAO } from "../db"; import { UserOperationDAO } from "../db/dao/UserOperationDAO"; +import { UserOperationV07DAO } from "../db/dao/UserOperationV07DAO"; import { IQueue } from "../interface"; import { logger } from "../logger"; import { relayerManagerTransactionTypeNameMap } from "../maps"; import { EVMNetworkService } from "../network"; -import { NotificationManager } from "../notification"; -import { SlackNotificationService } from "../notification/slack/SlackNotificationService"; import { BundlerTransactionQueue, RetryTransactionHandlerQueue, } from "../queue"; import { BundlerRelayService } from "../relay-service"; -import { BundlerSimulationService } from "../simulation"; +import { + BundlerSimulationService, + BundlerSimulationServiceV07, +} from "../simulation"; import { IStatusService, StatusService } from "../status"; import { - BundlerTransactionMessageType, + SendUserOperation, EntryPointMapType, + EntryPointV07MapType, EVMRawTransactionType, TransactionType, } from "../types"; import { UserOperationStateDAO } from "../db/dao/UserOperationStateDAO"; -import { customJSONStringify, parseError } from "../utils"; +import { parseError } from "../utils"; import { GasPriceService } from "../gas-price"; import { CacheFeesJob } from "../gas-price/jobs/CacheFees"; +import { FlashbotsClient } from "../network/FlashbotsClient"; +import { + ENTRYPOINT_V6_ABI, + ENTRYPOINT_V7_ABI, + supportedChains, +} from "@biconomy/gas-estimations"; +import { Logger } from "pino"; +import { logMeasureTime } from "../utils/timing"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -60,7 +69,12 @@ const bundlerSimulationServiceMap: { [chainId: number]: BundlerSimulationService; } = {}; +const bundlerSimulationServiceMapV07: { + [chainId: number]: BundlerSimulationServiceV07; +} = {}; + const entryPointMap: EntryPointMapType = {}; +const entryPointMapV07: EntryPointV07MapType = {}; const dbInstance = Mongo.getInstance(); const cacheService = RedisCacheService.getInstance(); @@ -75,6 +89,8 @@ const EVMRelayerManagerMap: { const transactionDao = new TransactionDAO(); const userOperationDao = new UserOperationDAO(); +const userOperationV07Dao = new UserOperationV07DAO(); + const userOperationStateDao = new UserOperationStateDAO(); const retryTransactionServiceMap: Record = @@ -86,306 +102,541 @@ const retryTransactionQueueMap: { } = {}; const networkServiceMap: Record = {}; -// eslint-disable-next-line import/no-mutable-exports let statusService: IStatusService; +// >> Main function to start the bundler << (async () => { + const startTime = Date.now(); + const bootId = startTime.toString(); + + const _log = log.child({ bootId }); + + _log.info({ supportedNetworks }, "Bundler is booting up, please wait..."); + await dbInstance.connect(); await cacheService.connect(); - const slackNotificationService = new SlackNotificationService( - process.env.BUNDLER_SLACK_TOKEN || config.slack.token, - process.env.BUNDLER_SLACK_CHANNEL || config.slack.channel, - ); - const notificationManager = new NotificationManager(slackNotificationService); + const queueUrl = + process.env.BUNDLER_QUEUE_URL || nodeconfig.get("queueUrl"); - log.info( - `Setting up instances for following chainIds: ${customJSONStringify( - supportedNetworks, - )}`, - ); + // disable heartbeat as per CloudAMQP recommendation + const rabbitMqConnection = await amqp.connect(queueUrl, { + heartbeat: 0, + }); - log.info(`Supported networks are: ${supportedNetworks}`); + const networkSetupPromises: Promise[] = []; for (const chainId of supportedNetworks) { - log.info(`Setup of services started for chainId: ${chainId}`); - routeTransactionToRelayerMap[chainId] = {}; - entryPointMap[chainId] = []; + networkSetupPromises.push(setupNetwork(chainId, rabbitMqConnection, _log)); + } - if (!config.chains.providers || !config.chains.providers[chainId]) { - throw new Error(`No providers in config for chainId: ${chainId}`); - } + await Promise.all(networkSetupPromises); - const [firstProvider] = config.chains.providers[chainId]; - const rpcUrl = firstProvider.url; + statusService = new StatusService({ + cacheService, + networkServiceMap, + evmRelayerManagerMap: EVMRelayerManagerMap, + dbInstance, + gasPriceServiceMap, + bundlerSimulationServiceMap, + bundlerSimulationServiceMapV07, + }); - log.info(`Setting up network service for chainId: ${chainId}`); - const networkService = new EVMNetworkService({ - chainId, - rpcUrl, - }); - log.info(`Network service setup complete for chainId: ${chainId}`); - networkServiceMap[chainId] = networkService; + const bootTimeSeconds = (Date.now() - startTime) / 1000; - log.info(`Setting up gas price manager for chainId: ${chainId}`); - const gasPriceService = new GasPriceService(cacheService, networkService, { - chainId, - EIP1559SupportedNetworks: config.EIP1559SupportedNetworks, - }); - log.info(`Gas price service setup complete for chainId: ${chainId}`); + _log.info({ supportedNetworks, bootTimeSeconds }, "Bundler is up!"); +})(); - // added check for relayer node path in order to run on only one server - if (gasPriceService && config.relayer.nodePathIndex === 0) { - const gasPriceSchedule = `*/${config.chains.updateFrequencyInSeconds[chainId]} * * * * *`; - log.info( - `Scheduling CacheFeesJob for chainId: ${chainId} with schedule: '${gasPriceSchedule}'`, - ); - try { - const cacheFeesJob = new CacheFeesJob( - gasPriceSchedule, - gasPriceService, - ); - cacheFeesJob.start(); - } catch (err) { - log.error( - `Error in scheduling gas price job for chainId: ${chain} and schedule: ${gasPriceSchedule}: ${parseError( - err, - )}`, - ); - } - } - if (!gasPriceService) { - throw new Error(`Gasprice service is not setup for chainId ${chainId}`); - } - gasPriceServiceMap[chainId] = gasPriceService; - log.info(`Gas price service setup complete for chainId: ${chainId}`); +export { + routeTransactionToRelayerMap, + bundlerSimulationServiceMap, + bundlerSimulationServiceMapV07, + entryPointMap, + entryPointMapV07, + EVMRelayerManagerMap, + transactionServiceMap, + transactionDao, + userOperationDao, + userOperationStateDao, + userOperationV07Dao, + statusService, + networkServiceMap, + gasPriceServiceMap, +}; - log.info(`Setting up retry transaction queue for chainId: ${chainId}`); - const retryTransactionQueue = new RetryTransactionHandlerQueue({ - chainId, - nodePathIndex: config.relayer.nodePathIndex, - }); - retryTransactionQueueMap[chainId] = retryTransactionQueue; - await retryTransactionQueueMap[chainId].connect(); - log.info(`Retry transaction queue setup complete for chainId: ${chainId}`); +async function setupNetwork( + chainId: number, + rabbitMqConnection: amqp.Connection, + log: Logger, +) { + const chainName = supportedChains[chainId]?.name; + const _log = log.child({ chainId, chainName }); - log.info(`Setting up nonce manager for chainId: ${chainId}`); - const nonceExpiryTTL = 3600; - const nonceManager = new EVMNonceManager({ - options: { - chainId, - nonceExpiryTTL, - }, - networkService, - cacheService, - }); - log.info(`Nonce manager setup complete for chainId: ${chainId}`); + _log.info(`Start network setup...`); - log.info(`Setting up transaction listener for chainId: ${chainId}`); - const transactionListener = new EVMTransactionListener({ - networkService, - cacheService, - retryTransactionQueue, - transactionDao, - userOperationDao, - userOperationStateDao, - options: { + routeTransactionToRelayerMap[chainId] = {}; + entryPointMap[chainId] = []; + entryPointMapV07[chainId] = []; + + if (!config.chains.providers || !config.chains.providers[chainId]) { + throw new Error(`No providers in config for chainId: ${chainId}`); + } + + const networkService = await setupEVMNetworkService(chainId, _log); + networkServiceMap[chainId] = networkService; + + _log.info(`Start Gas Price Service setup...`); + const [gasPriceService] = await logMeasureTime( + _log, + "Gas Price Service setup complete!", + async () => setupGasPriceService(networkService, chainId, _log), + ); + gasPriceServiceMap[chainId] = gasPriceService; + + _log.info(`Start Retry Transaction Queue setup...`); + const [retryTransactionQueue] = await logMeasureTime( + _log, + "Retry Transaction Queue setup complete!", + async () => setupRetryTransactionQueue(chainId, rabbitMqConnection), + ); + retryTransactionQueueMap[chainId] = retryTransactionQueue; + + _log.info(`Start EVM Transaction Listener setup...`); + const [transactionListener] = await logMeasureTime( + _log, + "EVM Transaction Listener setup complete!", + async () => + setupEVMTransactionListener( + networkService, + retryTransactionQueue, chainId, - entryPointMap, - }, - }); - transactionListenerMap[chainId] = transactionListener; - log.info(`Transaction listener setup complete for chainId: ${chainId}`); + ), + ); + transactionListenerMap[chainId] = transactionListener; + + _log.info(`Start Nonce Manager setup...`); + const [nonceManager] = await logMeasureTime( + _log, + "Nonce manager setup complete!", + async () => setupNonceManager(chainId, networkService), + ); + + _log.info(`Start EVM Transaction Service setup...`); + const [transactionService] = await logMeasureTime( + _log, + "EVM Transaction Service setup complete!", + async () => + new EVMTransactionService({ + networkService, + transactionListener, + nonceManager, + gasPriceService, + transactionDao, + cacheService, + userOperationStateDao, + options: { + chainId, + }, + }), + ); + transactionServiceMap[chainId] = transactionService; - log.info(`Setting up transaction service for chainId: ${chainId}`); - const transactionService = new EVMTransactionService({ + _log.info(`Start Relayer Manager setup...`); + await logMeasureTime(_log, "Relayer Manager setup complete!", async () => + setupRelayerManagers( networkService, - transactionListener, - nonceManager, gasPriceService, - transactionDao, - cacheService, - notificationManager, - userOperationStateDao, - options: { + transactionService, + nonceManager, + chainId, + _log, + ), + ); + + _log.info(`Start Retry Transaction Service setup...`); + await logMeasureTime( + _log, + "Retry transaction service setup complete!", + async () => + setupTransactionService( chainId, - }, - }); - transactionServiceMap[chainId] = transactionService; - log.info(`Transaction service setup complete for chainId: ${chainId}`); - - log.info(`Setting up relayer manager for chainId: ${chainId}`); - for (const relayerManager of config.relayerManagers) { - const relayerQueue = new EVMRelayerQueue([]); - if (!EVMRelayerManagerMap[relayerManager.name]) { - EVMRelayerManagerMap[relayerManager.name] = {}; - } - const relayerMangerInstance = new EVMRelayerManager({ + retryTransactionQueue, + transactionService, networkService, - gasPriceService, - cacheService, + ), + ); + + _log.info(`Start Bundler Simulation Service setup...`); + await logMeasureTime( + _log, + "Bundler Simulation Service setup complete!", + async () => + setupBundlerSimulationServices(chainId, networkService, gasPriceService), + ); + + const { entryPointData, entryPointV07Data } = config; + + _log.info(`Start Entry Point Contracts setup...`); + await logMeasureTime( + _log, + "Entry Point Contracts setup complete!", + async () => + setupEntryPointContracts( + entryPointData, + chainId, + networkService, + entryPointV07Data, + ), + ); + + // for each network get transaction type + await setupSupportedTransactionTypes( + chainId, + _log, + rabbitMqConnection, + transactionService, + ); + + _log.info(`Network setup complete!`); +} + +async function setupSupportedTransactionTypes( + chainId: number, + _log: Logger, + rabbitMqConnection: amqp.Connection, + transactionService: EVMTransactionService, +) { + for (const type of supportedTransactionType[chainId]) { + if (type === TransactionType.BUNDLER) { + const bundlerRelayerManager = + EVMRelayerManagerMap[relayerManagerTransactionTypeNameMap[type]][ + chainId + ]; + if (!bundlerRelayerManager) { + throw new Error(`Relayer manager not found for ${type}`); + } + _log.info(`Setting up Bundler transaction queue`); + const bundlerQueue: IQueue = + new BundlerTransactionQueue({ + chainId, + }); + + await bundlerQueue.connect(rabbitMqConnection); + _log.info(`Bundler transaction queue setup complete`); + + _log.info( + `Setting up Bundler consumer, relay service, simulation & validation service`, + ); + const bundlerConsumer = new BundlerConsumer({ + queue: bundlerQueue, + relayerManager: bundlerRelayerManager, transactionService, - nonceManager, - relayerQueue, - notificationManager, + cacheService, options: { chainId, - name: relayerManager.name, - relayerSeed: relayerManager.relayerSeed, - minRelayerCount: relayerManager.minRelayerCount[chainId], - maxRelayerCount: relayerManager.maxRelayerCount[chainId], - inactiveRelayerCountThreshold: - relayerManager.inactiveRelayerCountThreshold[chainId], - pendingTransactionCountThreshold: - relayerManager.pendingTransactionCountThreshold[chainId], - newRelayerInstanceCount: - relayerManager.newRelayerInstanceCount[chainId], - fundingBalanceThreshold: parseEther( - relayerManager.fundingBalanceThreshold[chainId].toString(), - ), - fundingRelayerAmount: relayerManager.fundingRelayerAmount[chainId], - ownerAccountDetails: new EVMAccount( - relayerManager.ownerAddress, - relayerManager.ownerPrivateKey, - networkService.rpcUrl, - ), - gasLimitMap: relayerManager.gasLimitMap, + entryPointMap, + entryPointMapV07, }, }); - EVMRelayerManagerMap[relayerManager.name][chainId] = - relayerMangerInstance; - - const addressList = await relayerMangerInstance.createRelayers(); - log.info( - `Relayer address list length: ${ - addressList.length - } and minRelayerCount: ${customJSONStringify( - relayerManager.minRelayerCount, - )} for relayerManager: ${relayerManager.name}`, - ); - await relayerMangerInstance.fundRelayers(addressList); - log.info( - `Relayer manager setup complete for chainId: ${chainId} for relayerManager: ${relayerManager.name}`, + // start listening for transaction + await bundlerQueue.consume(bundlerConsumer.onMessageReceived); + + const bundlerRelayService = new BundlerRelayService(bundlerQueue); + routeTransactionToRelayerMap[chainId][type] = bundlerRelayService; + + _log.info( + `Bundler consumer, relay service, simulation and validation service setup complete`, ); } + } +} + +function setupEntryPointContracts( + entryPointData: { [address: string]: { supportedChainIds: Array } }, + chainId: number, + networkService: EVMNetworkService, + entryPointV07Data: { + [address: `0x${string}`]: { supportedChainIds: Array }; + }, +) { + for (const [ + entryPointAddress, + entryPointSupportedChainIdsAndAbi, + ] of Object.entries(entryPointData)) { + if (entryPointSupportedChainIdsAndAbi.supportedChainIds.includes(chainId)) { + entryPointMap[chainId].push({ + address: entryPointAddress, + entryPointContract: getContract({ + abi: ENTRYPOINT_V6_ABI, + address: entryPointAddress as `0x${string}`, + client: { + public: networkService.provider, + }, + }), + }); + } + } + + for (const [ + entryPointAddress, + entryPointSupportedChainIdsAndAbi, + ] of Object.entries(entryPointV07Data)) { + if (entryPointSupportedChainIdsAndAbi.supportedChainIds.includes(chainId)) { + entryPointMapV07[chainId].push({ + address: entryPointAddress, + entryPointContract: getContract({ + abi: ENTRYPOINT_V7_ABI, + address: entryPointAddress as `0x${string}`, + client: { + public: networkService.provider, + }, + }), + }); + } + } +} + +function setupTransactionService( + chainId: number, + retryTransactionQueue: RetryTransactionHandlerQueue, + transactionService: EVMTransactionService, + networkService: EVMNetworkService, +) { + retryTransactionServiceMap[chainId] = new EVMRetryTransactionService({ + retryTransactionQueue, + transactionService, + networkService, + cacheService, + options: { + chainId, + EVMRelayerManagerMap, + }, + }); - log.info(`Relayer manager setup complete for chainId: ${chainId}`); + retryTransactionQueueMap[chainId].consume( + retryTransactionServiceMap[chainId].onMessageReceived, + ); +} + +async function setupRelayerManagers( + networkService: EVMNetworkService, + gasPriceService: GasPriceService, + transactionService: EVMTransactionService, + nonceManager: EVMNonceManager, + chainId: number, + _log: Logger, +) { + for (const relayerManager of config.relayerManagers) { + const relayerQueue = new EVMRelayerQueue([]); + if (!EVMRelayerManagerMap[relayerManager.name]) { + EVMRelayerManagerMap[relayerManager.name] = {}; + } - log.info(`Setting up retry transaction service for chainId: ${chainId}`); - retryTransactionServiceMap[chainId] = new EVMRetryTransactionService({ - retryTransactionQueue, - transactionService, + const relayerMangerInstance = new EVMRelayerManager({ networkService, - notificationManager, + gasPriceService, cacheService, + transactionService, + nonceManager, + relayerQueue, options: { chainId, - EVMRelayerManagerMap, // TODO // Review a better way + name: relayerManager.name, + relayerSeed: relayerManager.relayerSeed, + minRelayerCount: relayerManager.minRelayerCount[chainId], + maxRelayerCount: relayerManager.maxRelayerCount[chainId], + inactiveRelayerCountThreshold: + relayerManager.inactiveRelayerCountThreshold[chainId], + pendingTransactionCountThreshold: + relayerManager.pendingTransactionCountThreshold[chainId], + newRelayerInstanceCount: + relayerManager.newRelayerInstanceCount[chainId], + fundingBalanceThreshold: parseEther( + relayerManager.fundingBalanceThreshold[chainId].toString(), + ), + fundingRelayerAmount: relayerManager.fundingRelayerAmount[chainId], + ownerAccountDetails: new EVMAccount( + relayerManager.ownerAddress, + relayerManager.ownerPrivateKey, + networkService.rpcUrl, + chainId, + nonceManager, + networkService, + ), + gasLimitMap: relayerManager.gasLimitMap, }, }); + EVMRelayerManagerMap[relayerManager.name][chainId] = relayerMangerInstance; - retryTransactionQueueMap[chainId].consume( - retryTransactionServiceMap[chainId].onMessageReceived, - ); - log.info(`Retry transaction service setup for chainId: ${chainId}`); + const addressList = await relayerMangerInstance.createRelayers(); - // eslint-disable-next-line max-len - bundlerSimulationServiceMap[chainId] = new BundlerSimulationService( - networkService, - gasPriceService, + _log.info( + `Relayer address list length: ${addressList.length} for relayerManager: ${relayerManager.name}`, ); - const { entryPointData } = config; - - for (const [ - entryPointAddress, - entryPointSupportedChainIdsAndAbi, - ] of Object.entries(entryPointData)) { - if ( - entryPointSupportedChainIdsAndAbi.supportedChainIds.includes(chainId) - ) { - entryPointMap[chainId].push({ - address: entryPointAddress, - entryPointContract: getContract({ - abi: ENTRY_POINT_ABI, - address: entryPointAddress as `0x${string}`, - client: { - public: networkService.provider, - }, - }), - }); - } + /* + UNSAFE: Disable funding relayers for testing purposes or local devenv. + This is enabled by default but you can set it to false if you want to run smoke tests for example + */ + if (nodeconfig.get("boot.fundRelayers")) { + await relayerMangerInstance.fundRelayers(addressList); + } else { + _log.warn(`Relayer funding is disabled`); } + } +} + +function setupEVMTransactionListener( + networkService: EVMNetworkService, + retryTransactionQueue: RetryTransactionHandlerQueue, + chainId: number, +) { + return new EVMTransactionListener({ + networkService, + cacheService, + retryTransactionQueue, + transactionDao, + userOperationDao, + userOperationDaoV07: userOperationV07Dao, + userOperationStateDao, + options: { + chainId, + entryPointMap, + entryPointMapV07, + }, + }); +} - // for each network get transaction type - for (const type of supportedTransactionType[chainId]) { - if (type === TransactionType.BUNDLER) { - const bundlerRelayerManager = - EVMRelayerManagerMap[relayerManagerTransactionTypeNameMap[type]][ - chainId - ]; - if (!bundlerRelayerManager) { - throw new Error(`Relayer manager not found for ${type}`); - } - log.info( - `Setting up Bundler transaction queue for chainId: ${chainId}`, - ); - const bundlerQueue: IQueue = - new BundlerTransactionQueue({ - chainId, - }); +function setupNonceManager(chainId: number, networkService: EVMNetworkService) { + const nonceExpiryTTL = 3600; + const nonceManager = new EVMNonceManager({ + options: { + chainId, + nonceExpiryTTL, + }, + networkService, + cacheService, + }); + return nonceManager; +} + +async function setupRetryTransactionQueue( + chainId: number, + rabbitMqConnection: amqp.Connection, +) { + const retryTransactionQueue = new RetryTransactionHandlerQueue({ + chainId, + nodePathIndex: config.relayer.nodePathIndex, + }); - await bundlerQueue.connect(); - log.info( - `Bundler transaction queue setup complete for chainId: ${chainId}`, - ); + await retryTransactionQueue.connect(rabbitMqConnection); - log.info( - `Setting up Bundler consumer, relay service, simulation & validation service for chainId: ${chainId}`, - ); - const bundlerConsumer = new BundlerConsumer({ - queue: bundlerQueue, - relayerManager: bundlerRelayerManager, - transactionService, - cacheService, - options: { - chainId, - entryPointMap, - }, - }); - // start listening for transaction - await bundlerQueue.consume(bundlerConsumer.onMessageReceived); + return retryTransactionQueue; +} - const bundlerRelayService = new BundlerRelayService(bundlerQueue); - routeTransactionToRelayerMap[chainId][type] = bundlerRelayService; +function setupGasPriceService( + networkService: EVMNetworkService, + chainId: number, + log: Logger, +) { + const gasPriceService = new GasPriceService(cacheService, networkService, { + chainId, + EIP1559SupportedNetworks: config.EIP1559SupportedNetworks, + }); - log.info( - `Bundler consumer, relay service, simulation and validation service setup complete for chainId: ${chainId}`, - ); - } + // added check for relayer node path in order to run on only one server + if (gasPriceService && config.relayer.nodePathIndex === 0) { + const gasPriceSchedule = `*/${config.chains.updateFrequencyInSeconds[chainId]} * * * * *`; + const _log = log.child({ gasPriceSchedule }); + + _log.info(`Scheduling CacheFeesJob...'`); + try { + const cacheFeesJob = new CacheFeesJob(gasPriceSchedule, gasPriceService); + cacheFeesJob.start(); + } catch (err) { + _log.error(`Error in scheduling CacheFeesJob: ${parseError(err)}`); } } - // eslint-disable-next-line no-new - statusService = new StatusService({ - cacheService, - networkServiceMap, - evmRelayerManagerMap: EVMRelayerManagerMap, - dbInstance, - gasPriceServiceMap, - bundlerSimulationServiceMap, + return gasPriceService; +} + +async function setupEVMNetworkService(chainId: number, log: Logger) { + log.info(`Setting up the EVMNetworkService...`); + + const [networkService] = await logMeasureTime( + log, + "EVMNetworkService setup complete!", + async () => { + if ( + nodeconfig + .get("flashbots.supportedNetworks") + .includes(chainId) + ) { + return new EVMNetworkService( + { + chainId, + }, + new FlashbotsClient(), + ); + } + return new EVMNetworkService({ + chainId, + }); + }, + ); + + return networkService; +} + +// TODO: Refactor this, no need to have two different services +function setupBundlerSimulationServices( + chainId: number, + networkService: EVMNetworkService, + gasPriceService: GasPriceService, +) { + log.info({ chainId }, `Setting up the BundlerSimulationService...`); + + // For EPv0.6.0 + bundlerSimulationServiceMap[chainId] = new BundlerSimulationService({ + networkService, + gasPriceService, }); - log.info("<=== Config setup completed ===>"); -})(); -export { - routeTransactionToRelayerMap, - bundlerSimulationServiceMap, - entryPointMap, - EVMRelayerManagerMap, - transactionServiceMap, - transactionDao, - userOperationDao, - userOperationStateDao, - statusService, - networkServiceMap, - gasPriceServiceMap, -}; + // For EPv0.7.0 + // TODO: Refactor the V07 service the same way you did v6, or not necessary if you merge them + bundlerSimulationServiceMapV07[chainId] = new BundlerSimulationServiceV07( + networkService, + gasPriceService, + ); + + log.info( + { + chainId, + }, + `BundlerSimulationService setup complete!`, + ); +} + +export interface BlockNativeResponse { + system: string; + network: string; + unit: string; + maxPrice: number; + currentBlockNumber: number; + msSinceLastBlock: number; + blockPrices: BlockPrice[]; +} + +export interface BlockPrice { + blockNumber: number; + estimatedTransactionCount: number; + baseFeePerGas: number; + blobBaseFeePerGas: number; + estimatedPrices: EstimatedPrice[]; +} + +export interface EstimatedPrice { + confidence: number; + price: number; + maxPriorityFeePerGas: number; + maxFeePerGas: number; +} diff --git a/src/common/simulation/BundlerSimulationService.test.ts b/src/common/simulation/BundlerSimulationService.test.ts deleted file mode 100644 index 2be834c7..00000000 --- a/src/common/simulation/BundlerSimulationService.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { GasEstimator } from "entry-point-gas-estimations/dist/gas-estimator/entry-point-v6/GasEstimator/GasEstimator"; -import { config } from "../../config"; -import { GasPriceService } from "../gas-price"; -import { EVMNetworkService } from "../network"; -import { UserOperationType } from "../types"; -import { BundlerSimulationService } from "./BundlerSimulationService"; -import RpcError from "../utils/rpc-error"; - -describe("BundlerSimulationService", () => { - const networkService = new EVMNetworkService({ - chainId: 137, - rpcUrl: "https://random-rpc-url.com", - }); - const gasPriceService = {} as unknown as GasPriceService; - const bundlerSimulationService = new BundlerSimulationService( - networkService, - gasPriceService, - ); - - describe("user op rejection", () => { - it("user op has insufficient max priority fee per gas", async () => { - bundlerSimulationService.gasEstimator = { - calculatePreVerificationGas: jest.fn().mockResolvedValue(50000n), - } as unknown as GasEstimator; - - const networkMaxFeePerGas = 10n; - const networkMaxPriorityFeePerGas = 10n; - - const userOp: UserOperationType = { - sender: "0xabc", - nonce: 1n, - initCode: "0x", - callData: "0xdef", - paymasterAndData: "0x", - callGasLimit: 5000n, - verificationGasLimit: 5000n, - preVerificationGas: 5000n, - maxPriorityFeePerGas: 1n, - maxFeePerGas: 10n, - signature: "0xsignature", - }; - - try { - await bundlerSimulationService.checkUserOperationForRejection({ - userOp, - networkMaxFeePerGas, - networkMaxPriorityFeePerGas, - }); - } catch (error) { - expect((error as RpcError).message).toEqual( - `maxPriorityFeePerGas in userOp: ${userOp.maxPriorityFeePerGas} is lower than expected maxPriorityFeePerGas: ${Number(networkMaxPriorityFeePerGas) * config.maxPriorityFeePerGasThresholdPercentage}`, - ); - } - }); - - it("user op has insufficient max fee per gas", async () => { - bundlerSimulationService.gasEstimator = { - calculatePreVerificationGas: jest.fn().mockResolvedValue(50000n), - } as unknown as GasEstimator; - - const networkMaxFeePerGas = 10n; - const networkMaxPriorityFeePerGas = 10n; - - const userOp: UserOperationType = { - sender: "0xabc", - nonce: 1n, - initCode: "0x", - callData: "0xdef", - paymasterAndData: "0x", - callGasLimit: 5000n, - verificationGasLimit: 5000n, - preVerificationGas: 5000n, - maxPriorityFeePerGas: 20n, - maxFeePerGas: 1n, - signature: "0xsignature", - }; - try { - await bundlerSimulationService.checkUserOperationForRejection({ - userOp, - networkMaxFeePerGas, - networkMaxPriorityFeePerGas, - }); - } catch (error) { - expect((error as RpcError).message).toEqual( - `maxFeePerGas in userOp: ${userOp.maxFeePerGas} is lower than expected maxFeePerGas: ${Number(networkMaxFeePerGas) * config.maxFeePerGasThresholdPercentage}`, - ); - } - }); - - it("user op has insufficient preVerificationGas", async () => { - bundlerSimulationService.gasEstimator = { - calculatePreVerificationGas: jest.fn().mockResolvedValue(50000n), - } as unknown as GasEstimator; - - const networkMaxFeePerGas = 1n; - const networkMaxPriorityFeePerGas = 1n; - - const userOp: UserOperationType = { - sender: "0xabc", - nonce: 1n, - initCode: "0x", - callData: "0xdef", - paymasterAndData: "0x", - callGasLimit: 5000n, - verificationGasLimit: 5000n, - preVerificationGas: 5000n, - maxPriorityFeePerGas: 10n, - maxFeePerGas: 10n, - signature: "0xsignature", - }; - try { - bundlerSimulationService.checkUserOperationForRejection({ - userOp, - networkMaxFeePerGas, - networkMaxPriorityFeePerGas, - }); - } catch (error) { - expect((error as RpcError).message).toEqual( - `preVerificationGas in userOp: ${userOp.preVerificationGas} is lower than expected preVerificationGas: ${50000 * config.preVerificationGasThresholdPercentage}`, - ); - } - }); - - it("user op has sufficient max fee values and preVerificationGas", async () => { - bundlerSimulationService.gasEstimator = { - calculatePreVerificationGas: jest.fn().mockResolvedValue(5n), - } as unknown as GasEstimator; - - const networkMaxFeePerGas = 1n; - const networkMaxPriorityFeePerGas = 1n; - - const userOp: UserOperationType = { - sender: "0xabc", - nonce: 1n, - initCode: "0x", - callData: "0xdef", - paymasterAndData: "0x", - callGasLimit: 5000n, - verificationGasLimit: 5000n, - preVerificationGas: 5000n, - maxPriorityFeePerGas: 10n, - maxFeePerGas: 10n, - signature: "0xsignature", - }; - const response = - await bundlerSimulationService.checkUserOperationForRejection({ - userOp, - networkMaxFeePerGas, - networkMaxPriorityFeePerGas, - }); - expect(response).toBe(true); - }); - }); -}); diff --git a/src/common/simulation/BundlerSimulationService.ts b/src/common/simulation/BundlerSimulationService.ts index a298639c..a99b64ca 100644 --- a/src/common/simulation/BundlerSimulationService.ts +++ b/src/common/simulation/BundlerSimulationService.ts @@ -1,5 +1,4 @@ -/* eslint-disable import/no-extraneous-dependencies */ -/* eslint-disable import/no-import-module-exports */ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable prefer-const */ import { decodeErrorResult, @@ -8,25 +7,18 @@ import { keccak256, parseAbiParameters, toHex, + zeroAddress, } from "viem"; -import { - createArbitrumGasEstimator, - createGasEstimator, - createMantleGasEstimator, - createMorphGasEstimator, - createOptimismGasEstimator, - createScrollGasEstimator, - IGasEstimator, -} from "entry-point-gas-estimations"; +import nodeconfig from "config"; import { config } from "../../config"; import { IEVMAccount } from "../../relayer/account"; -import { - BUNDLER_ERROR_CODES, - STATUSES, -} from "../../server/api/shared/middleware"; import { logger } from "../logger"; import { INetworkService } from "../network"; -import { EVMRawTransactionType, UserOperationType } from "../types"; +import { + EntryPointContractType, + EVMRawTransactionType, + UserOperationType, +} from "../types"; import { customJSONStringify, packUserOpForUserOpHash, @@ -41,58 +33,70 @@ import { } from "./types"; import { BLOCKCHAINS } from "../constants"; import { IGasPriceService } from "../gas-price"; +import { UserOperationV6 } from "@biconomy/gas-estimations"; +import { GasEstimator } from "@biconomy/gas-estimations"; +import { createGasEstimator } from "@biconomy/gas-estimations"; +import { isEstimateUserOperationGasResultV6 } from "@biconomy/gas-estimations"; +import { CallTracerResult, findErrorsInTrace } from "./trace"; +import { STATUSES } from "../../server/api/shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../../server/api/shared/errors/codes"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); +/** + * Options for the BundlerSimulationService constructor + * @member {SimulationNetworkService} networkService - The network service that will be used to call `eth_estimateGas` + * @member {IGasPriceService} gasPriceService - The gas price service used to fetch both legacy and EIP-1559 gas prices + * @member {SimulationGasEstimator} gasEstimator - The estimator used for eth_estimateUserOperationGas. + * You can pass your own gas estimator or the default one will be used + */ +interface IBundlerSimulationServiceOptions { + networkService: SimulationNetworkService; + gasPriceService: IGasPriceService; + gasEstimator?: SimulationGasEstimator; +} + +/** + * BundlerSimulationService is responsible for estimating the gas limits & values for user operations + * and validating the user operation before sending it to the network. + */ export class BundlerSimulationService { - networkService: INetworkService; + networkService: SimulationNetworkService; gasPriceService: IGasPriceService; - gasEstimator: IGasEstimator; + gasEstimator: SimulationGasEstimator; - constructor( - networkService: INetworkService, - gasPriceService: IGasPriceService, - ) { + constructor({ + networkService, + gasPriceService, + gasEstimator = createGasEstimator({ + chainId: networkService.chainId, + rpc: config.chains.providers[networkService.chainId][0].url, + }), + }: IBundlerSimulationServiceOptions) { this.networkService = networkService; this.gasPriceService = gasPriceService; - this.gasEstimator = createGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - }); - - if (config.optimismNetworks.includes(this.networkService.chainId)) { - this.gasEstimator = createOptimismGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - }); - } - - if (config.arbitrumNetworks.includes(this.networkService.chainId)) - this.gasEstimator = createArbitrumGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - }); - - if (config.mantleNetworks.includes(this.networkService.chainId)) { - this.gasEstimator = createMantleGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - }); - } - - if (config.scrollNetworks.includes(this.networkService.chainId)) { - this.gasEstimator = createScrollGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - }); - } + this.gasEstimator = gasEstimator; + } - if (config.morphNetworks.includes(this.networkService.chainId)) { - this.gasEstimator = createMorphGasEstimator({ - rpcUrl: this.networkService.rpcUrl, - }); - } + /** + * Used for pretty printing the service configuration + * @returns JSON stringified object with API keys removed + */ + toJSON(): Omit< + IBundlerSimulationServiceOptions, + "gasPriceService" | "newGasEstimator" + > { + return { + networkService: this.networkService, + gasEstimator: this.gasEstimator, + }; } + // TODO: Add option to pass an entrypoint address to the new gas estimator async estimateUserOperationGas( estimateUserOperationGasData: EstimateUserOperationGasDataType, ): Promise { @@ -100,8 +104,6 @@ export class BundlerSimulationService { const { userOp, entryPointContract, chainId, stateOverrideSet } = estimateUserOperationGasData; - this.gasEstimator.setEntryPointAddress(entryPointContract.address); - const start = performance.now(); log.info( `userOp received: ${customJSONStringify( @@ -121,55 +123,14 @@ export class BundlerSimulationService { "0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b"; } - // for userOp completeness - if ( - !userOp.maxFeePerGas || - userOp.maxFeePerGas === BigInt(0) || - (userOp.maxFeePerGas as unknown as string) === "0x" || - (userOp.maxFeePerGas as unknown as string) === "0" - ) { - // setting a non zero value as division with maxFeePerGas will happen - userOp.maxFeePerGas = BigInt(1); - if ( - config.optimismNetworks.includes(chainId) || - config.mantleNetworks.includes(chainId) - ) { - const gasPrice = await this.gasPriceService.getGasPrice(); - if (typeof gasPrice === "bigint") { - userOp.maxFeePerGas = gasPrice; - } else { - const { maxFeePerGas } = gasPrice; - userOp.maxFeePerGas = maxFeePerGas; - } - } - } - - // for userOp completeness - userOp.callGasLimit = BigInt(20000000); - userOp.verificationGasLimit = BigInt(10000000); - userOp.preVerificationGas = BigInt(100000); - - // for userOp completeness - if ( - !userOp.maxPriorityFeePerGas || - userOp.maxPriorityFeePerGas === BigInt(0) || - (userOp.maxPriorityFeePerGas as unknown as string) === "0x" || - (userOp.maxPriorityFeePerGas as unknown as string) === "0" - ) { - // setting a non zero value as division with maxPriorityFeePerGas will happen - userOp.maxPriorityFeePerGas = BigInt(1); - if ( - config.optimismNetworks.includes(chainId) || - config.mantleNetworks.includes(chainId) - ) { - const gasPrice = await this.gasPriceService.getGasPrice(); - if (typeof gasPrice === "bigint") { - userOp.maxPriorityFeePerGas = gasPrice; - } else { - const { maxPriorityFeePerGas } = gasPrice; - userOp.maxPriorityFeePerGas = maxPriorityFeePerGas; - } - } + const gasPrice = await this.gasPriceService.getGasPrice(); + if (typeof gasPrice === "bigint") { + userOp.maxFeePerGas = gasPrice; + userOp.maxPriorityFeePerGas = gasPrice; + } else { + const { maxFeePerGas } = gasPrice; + userOp.maxFeePerGas = maxFeePerGas; + userOp.maxPriorityFeePerGas = maxFeePerGas; } log.info( @@ -178,37 +139,33 @@ export class BundlerSimulationService { )} on chainId: ${chainId}`, ); - let supportsEthCallStateOverride = true; - let supportsEthCallByteCodeOverride = true; - if (config.networksNotSupportingEthCallStateOverrides.includes(chainId)) { - supportsEthCallStateOverride = false; - } else if ( - config.networksNotSupportingEthCallBytecodeStateOverrides.includes( - chainId, - ) - ) { - supportsEthCallByteCodeOverride = false; - } - - if (userOp.initCode !== "0x") { - supportsEthCallByteCodeOverride = false; + let baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); + if (chainId === BLOCKCHAINS.OP_BNB_MAINNET && baseFeePerGas === 0n) { + baseFeePerGas = BigInt( + config.gasOverrides[BLOCKCHAINS.OP_BNB_MAINNET].baseFeePerGas, + ); } - const baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); - const response = await this.gasEstimator.estimateUserOperationGas({ - userOperation: userOp, - stateOverrideSet, - supportsEthCallByteCodeOverride, - supportsEthCallStateOverride, + unEstimatedUserOperation: userOp, baseFeePerGas, + stateOverrides: stateOverrideSet, + options: { + entryPointAddress: entryPointContract.address, + }, }); + log.info( - `estimation respone from gas estimation package: ${customJSONStringify( - response, - )} on chainId: ${chainId}`, + { chainId }, + `estimateUserOperationGas: ${customJSONStringify(response)}`, ); + if (!isEstimateUserOperationGasResultV6(response)) { + throw new Error( + "Invalid response from the gas estimator. Expected a V6 response.", + ); + } + const { validAfter, validUntil } = response; let { verificationGasLimit, callGasLimit, preVerificationGas } = response; @@ -251,6 +208,7 @@ export class BundlerSimulationService { callGasLimit + BigInt(verificationGasLimitMultiplier) * verificationGasLimit + preVerificationGas; + log.info(`totalGas: ${totalGas} on chainId: ${chainId}`); const pvgMarkUp = config.pvgMarkUp[chainId] || 0.1; // setting default pvgMarkUp to 10% incase the value for a given chainId is not set @@ -266,6 +224,12 @@ export class BundlerSimulationService { const end = performance.now(); log.info(`Estimating the userOp took: ${end - start} milliseconds`); + if (chainId === BLOCKCHAINS.OP_BNB_MAINNET && baseFeePerGas === 0n) { + preVerificationGas = BigInt( + config.gasOverrides[BLOCKCHAINS.OP_BNB_MAINNET].preVerificationGas, + ); + } + return { code: STATUSES.SUCCESS, message: `Gas successfully estimated for userOp: ${customJSONStringify( @@ -312,11 +276,14 @@ export class BundlerSimulationService { await this.gasPriceService.get1559GasPrice(); let gasPrice = Math.ceil(Number(maxFeePerGas) * 2).toString(16); - await this.checkUserOperationForRejection({ - userOp, - networkMaxPriorityFeePerGas: maxPriorityFeePerGas, - networkMaxFeePerGas: maxFeePerGas, - }); + await this.checkUserOperationForRejection( + { + userOp, + networkMaxPriorityFeePerGas: maxPriorityFeePerGas, + networkMaxFeePerGas: maxFeePerGas, + }, + entryPointContract, + ); const data = encodeFunctionData({ abi: entryPointContract.abi, @@ -489,7 +456,10 @@ export class BundlerSimulationService { */ async checkUserOperationForRejection( validationData: ValidationData, + entryPointContract: EntryPointContractType, ): Promise { + // TODO: Check if balance >= required prefund if wallet deployment because we can't simulate deployment tx's + const { userOp, networkMaxFeePerGas, networkMaxPriorityFeePerGas } = validationData; @@ -518,7 +488,10 @@ export class BundlerSimulationService { ); log.info(`Checking if maxPriorityFeePerGas is within acceptable limits`); - if (minimumAcceptableMaxPriorityFeePerGas > Number(maxPriorityFeePerGas)) { + if ( + !config.disableFeeValidation.includes(this.networkService.chainId) && + minimumAcceptableMaxPriorityFeePerGas > Number(maxPriorityFeePerGas) + ) { log.info( `maxPriorityFeePerGas in userOp: ${maxPriorityFeePerGas} is lower than expected maxPriorityFeePerGas: ${minimumAcceptableMaxPriorityFeePerGas}`, ); @@ -530,7 +503,10 @@ export class BundlerSimulationService { log.info(`maxPriorityFeePerGas is within acceptable limits`); log.info(`Checking if maxFeePerGas is within acceptable limits`); - if (minimumAcceptableMaxFeePerGas > Number(maxFeePerGas)) { + if ( + !config.disableFeeValidation.includes(this.networkService.chainId) && + minimumAcceptableMaxFeePerGas > Number(maxFeePerGas) + ) { log.info( `maxFeePerGas in userOp: ${maxFeePerGas} is lower than expected maxFeePerGas: ${minimumAcceptableMaxFeePerGas}`, ); @@ -542,26 +518,100 @@ export class BundlerSimulationService { log.info(`maxFeePerGas is within acceptable limits`); log.info(`Checking if preVerificationGas is within acceptable limits`); - const baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); + let baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); + if ( + this.networkService.chainId === BLOCKCHAINS.OP_BNB_MAINNET && + baseFeePerGas === 0n + ) { + baseFeePerGas = BigInt( + config.gasOverrides[BLOCKCHAINS.OP_BNB_MAINNET].baseFeePerGas, + ); + } - const { preVerificationGas: networkPreVerificationGas } = - await this.gasEstimator.calculatePreVerificationGas({ - userOperation: userOp, - baseFeePerGas, - }); - log.info(`networkPreVerificationGas: ${networkPreVerificationGas}`); + if (!config.disableFeeValidation.includes(this.networkService.chainId)) { + const userOperation: UserOperationV6 = { + sender: userOp.sender, + nonce: userOp.nonce, + initCode: userOp.initCode, + paymasterAndData: userOp.paymasterAndData, + signature: userOp.signature, + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + callData: userOp.callData, + callGasLimit: BigInt(userOp.callGasLimit), + verificationGasLimit: BigInt(userOp.verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + }; - const minimumAcceptablePreVerificationGas = - Number(networkPreVerificationGas) * preVerificationGasThresholdPercentage; + const networkPreVerificationGas = + await this.gasEstimator.estimatePreVerificationGas( + userOperation, + BigInt(baseFeePerGas), + ); - if (minimumAcceptablePreVerificationGas > Number(preVerificationGas)) { - log.info( - `preVerificationGas in userOp: ${preVerificationGas} is lower than minimumAcceptablePreVerificationGas: ${minimumAcceptablePreVerificationGas}`, - ); - throw new RpcError( - `preVerificationGas in userOp: ${preVerificationGas} is lower than minimumAcceptablePreVerificationGas: ${minimumAcceptablePreVerificationGas}`, - BUNDLER_ERROR_CODES.PRE_VERIFICATION_GAS_TOO_LOW, - ); + log.info(`networkPreVerificationGas: ${networkPreVerificationGas}`); + + const minimumAcceptablePreVerificationGas = + Number(networkPreVerificationGas) * + preVerificationGasThresholdPercentage; + + if (minimumAcceptablePreVerificationGas > Number(preVerificationGas)) { + log.info( + `preVerificationGas in userOp: ${preVerificationGas} is lower than minimumAcceptablePreVerificationGas: ${minimumAcceptablePreVerificationGas}`, + ); + throw new RpcError( + `preVerificationGas in userOp: ${preVerificationGas} is lower than minimumAcceptablePreVerificationGas: ${minimumAcceptablePreVerificationGas}`, + BUNDLER_ERROR_CODES.PRE_VERIFICATION_GAS_TOO_LOW, + ); + } + } + + // Check the user operation for validation and execution errors (if debug_traceCall is supported) + if ( + // we can't simulate deployment transactions, they will fail because of insufficient entrypoint deposit + userOp.initCode === "0x" && + nodeconfig.has("supportsDebugTraceCall") && + nodeconfig + .get("supportsDebugTraceCall") + .includes(this.networkService.chainId) + ) { + const traceStart = performance.now(); + + const traceResult: CallTracerResult = + await this.networkService.provider.request({ + method: "debug_traceCall" as any, // coalesce so viem doesn't complain + params: [ + { + from: zeroAddress, // so we don't get balance errors + to: entryPointContract.address, + data: encodeFunctionData({ + abi: entryPointContract.abi, + functionName: "handleOps", + args: [[userOp], "0xc75Bb3956c596efc6DB663cd3e2f64929d6AB0fc"], + }), + }, + "latest", + { + tracer: "callTracer", + tracerConfig: { + onlyTopCall: false, // we need deep traces + disableStack: false, // and we want the stack + enableReturnData: true, // and the return data so we can decode the error + }, + }, + ], + }); + const traceEnd = performance.now(); + + log.info(`debug_traceCall took ${traceEnd - traceStart} milliseconds`); + + const errors = findErrorsInTrace(traceResult, []); + + if (errors.length > 0) { + log.info(`Errors found in trace: ${customJSONStringify(errors)}`); + log.info(customJSONStringify(traceResult)); + throw new Error(errors.join(", ")); + } } log.info( @@ -570,7 +620,6 @@ export class BundlerSimulationService { return true; } - // eslint-disable-next-line class-methods-use-this removeSpecialCharacters(input: string): string { const match = input.match(/AA(\d+)\s(.+)/); @@ -590,80 +639,6 @@ export class BundlerSimulationService { return input; } - static parseSimulateHandleOpResult( - userOp: UserOperationType, - simulateHandleOpResult: any, - ) { - if (!simulateHandleOpResult?.errorName?.startsWith("ExecutionResult")) { - log.info( - `Inside ${!simulateHandleOpResult?.errorName?.startsWith( - "ExecutionResult", - )}`, - ); - // parse it as FailedOp - // if its FailedOp, then we have the paymaster param... otherwise its an Error(string) - log.info( - `simulateHandleOpResult.errorArgs: ${simulateHandleOpResult.errorArgs}`, - ); - if (!simulateHandleOpResult.errorArgs) { - throw new RpcError( - `Error: ${customJSONStringify(simulateHandleOpResult)}`, - BUNDLER_ERROR_CODES.WALLET_TRANSACTION_REVERTED, - ); - } - let { paymaster } = simulateHandleOpResult.errorArgs; - if (paymaster === config.zeroAddress) { - paymaster = undefined; - } - // eslint-disable-next-line - const msg: string = - simulateHandleOpResult.errorArgs?.reason ?? - simulateHandleOpResult.toString(); - - if (paymaster == null) { - log.info( - `account validation failed: ${msg} for userOp: ${customJSONStringify( - userOp, - )}`, - ); - throw new RpcError(msg, BUNDLER_ERROR_CODES.SIMULATE_VALIDATION_FAILED); - } else { - log.info( - `paymaster validation failed: ${msg} for userOp: ${customJSONStringify( - userOp, - )}`, - ); - throw new RpcError( - msg, - BUNDLER_ERROR_CODES.SIMULATE_PAYMASTER_VALIDATION_FAILED, - ); - } - } - - const preOpGas = simulateHandleOpResult.errorArgs[0]; - log.info(`preOpGas: ${preOpGas}`); - const paid = simulateHandleOpResult.errorArgs[1]; - log.info(`paid: ${paid}`); - const validAfter = simulateHandleOpResult.errorArgs[2]; - log.info(`validAfter: ${validAfter}`); - const validUntil = simulateHandleOpResult.errorArgs[3]; - log.info(`validUntil: ${validUntil}`); - const targetSuccess = simulateHandleOpResult.errorArgs[4]; - log.info(`targetSuccess: ${targetSuccess}`); - const targetResult = simulateHandleOpResult.errorArgs[5]; - log.info(`targetResult: ${targetResult}`); - - return { - preOpGas, - paid, - validAfter, - validUntil, - targetSuccess, - targetResult, - }; - } - - // eslint-disable-next-line class-methods-use-this getUserOpHash( entryPointAddress: `0x${string}`, userOp: UserOperationType, @@ -677,3 +652,17 @@ export class BundlerSimulationService { return keccak256(enc); } } + +// The following are the dependencies of the BundlerSimulationService class + +// πŸ’‘ TIP: Always pick only the required fields from the interface +// so we don't depend on properties we don't use (easier to refactor) +export type SimulationNetworkService = Pick< + INetworkService, + "chainId" | "rpcUrl" | "estimateGas" | "sendRpcCall" | "provider" +>; + +export type SimulationGasEstimator = Pick< + GasEstimator, + "estimateUserOperationGas" | "estimatePreVerificationGas" +>; diff --git a/src/common/simulation/BundlerSimulationService.v7.ts b/src/common/simulation/BundlerSimulationService.v7.ts new file mode 100644 index 00000000..73b4aad0 --- /dev/null +++ b/src/common/simulation/BundlerSimulationService.v7.ts @@ -0,0 +1,651 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable prefer-const */ +import { + decodeErrorResult, + encodeFunctionData, + toHex, + zeroAddress, +} from "viem"; +import { config } from "../../config"; +import { IEVMAccount } from "../../relayer/account"; + +import { logger } from "../logger"; +import { INetworkService } from "../network"; +import { + EntryPointV07ContractType, + EVMRawTransactionType, + UserOperationType, +} from "../types"; +import { customJSONStringify, parseError } from "../utils"; +import RpcError from "../utils/rpc-error"; +import { + EstimateUserOperationGasDataTypeV07, + EstimateUserOperationGasReturnTypeV07, + SimulationDataV07, + ValidationDataV07, +} from "./types"; +import { IGasPriceService } from "../gas-price"; +import { ENTRY_POINT_V07_ABI } from "../entrypoint-v7/abiv7"; +import { + getUserOpHash, + packUserOperation, +} from "../entrypoint-v7/PackedUserOperation"; +import nodeconfig from "config"; +import { CallTracerResult, findErrorsInTrace } from "./trace"; +import { + GasEstimator, + createGasEstimator, + isEstimateUserOperationGasResultV7, + EstimateUserOperationGasResult, + toPackedUserOperation, + bumpBigIntPercent, +} from "@biconomy/gas-estimations"; +import { STATUSES } from "../../server/api/shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../../server/api/shared/errors/codes"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +// TODO: We probably don't need a separate file for this, but I'm keeping it for now as a reference +export class BundlerSimulationServiceV07 { + networkService: INetworkService; + + gasPriceService: IGasPriceService; + + gasEstimator: GasEstimator; + + constructor( + networkService: INetworkService, + gasPriceService: IGasPriceService, + ) { + this.networkService = networkService; + this.gasPriceService = gasPriceService; + this.gasEstimator = createGasEstimator({ + chainId: networkService.chainId, + rpc: config.chains.providers[networkService.chainId][0].url, + }); + } + + async estimateUserOperationGas( + estimateUserOperationGasData: EstimateUserOperationGasDataTypeV07, + ): Promise { + try { + const { userOp, entryPointContract, chainId, stateOverrideSet } = + estimateUserOperationGasData; + + const start = performance.now(); + log.info( + `userOp received: ${customJSONStringify( + userOp, + )} on chainId: ${chainId}`, + ); + + const gasPrice = await this.gasPriceService.getGasPrice(); + if (typeof gasPrice === "bigint") { + userOp.maxFeePerGas = gasPrice; + userOp.maxPriorityFeePerGas = gasPrice; + } else { + const { maxFeePerGas } = gasPrice; + userOp.maxFeePerGas = maxFeePerGas; + userOp.maxPriorityFeePerGas = maxFeePerGas; + } + + // for userOp completeness + userOp.callGasLimit = BigInt(5000000); + userOp.verificationGasLimit = BigInt(5000000); + userOp.preVerificationGas = BigInt(5000000); + + userOp.factory = userOp.factory ?? "0x"; + userOp.factoryData = userOp.factoryData ?? "0x"; + + log.info( + `userOp to be used for estimation: ${customJSONStringify( + userOp, + )} on chainId: ${chainId}`, + ); + + const baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); + + let response: EstimateUserOperationGasResult; + try { + response = await this.gasEstimator.estimateUserOperationGas({ + unEstimatedUserOperation: userOp, + baseFeePerGas, + stateOverrides: stateOverrideSet, + options: { + entryPointAddress: entryPointContract.address, + }, + }); + } catch (error: any) { + let errorMessage: string = error.message; + + log.error( + { chainId }, + `[@biconomy/gas-estimations] package threw an error: ${errorMessage}`, + ); + throw errorMessage; + } + + if (!isEstimateUserOperationGasResultV7(response)) { + throw new Error( + "Invalid response from the gas estimator. Expected a V7 response.", + ); + } + + let { + verificationGasLimit, + callGasLimit, + preVerificationGas, + paymasterPostOpGasLimit, + paymasterVerificationGasLimit, + } = response; + + log.info( + { chainId }, + `estimateUserOperationGas: ${customJSONStringify(response)}`, + ); + + callGasLimit += BigInt(Math.ceil(Number(callGasLimit) * 0.1)); + verificationGasLimit = bumpBigIntPercent(verificationGasLimit, 20); + paymasterPostOpGasLimit += BigInt( + Math.ceil(Number(paymasterPostOpGasLimit) * 0.1), + ); + paymasterVerificationGasLimit += BigInt( + Math.ceil(Number(paymasterVerificationGasLimit) * 0.1), + ); + + const requiredGas = + verificationGasLimit + + callGasLimit + + preVerificationGas + + paymasterVerificationGasLimit + + paymasterPostOpGasLimit; + + log.info({ chainId }, `requiredGas: ${requiredGas}`); + + const pvgMarkUp = config.pvgMarkUp[chainId] || 0.1; // setting default pvgMarkUp to 10% incase the value for a given chainId is not set + + preVerificationGas += BigInt( + Math.ceil(Number(toHex(requiredGas)) * pvgMarkUp), + ); + + log.info( + `preVerificationGas after bumping it up: ${preVerificationGas} on chainId: ${chainId}`, + ); + + const end = performance.now(); + log.info(`Estimating the userOp took: ${end - start} milliseconds`); + + const data = { + preVerificationGas, + verificationGasLimit, + callGasLimit, + paymasterPostOpGasLimit, + paymasterVerificationGasLimit, + }; + log.info( + `estimateUserOperationGas result: ${customJSONStringify(data)}`, + { chainId }, + ); + + return { + code: STATUSES.SUCCESS, + message: `Gas successfully estimated for userOp: ${customJSONStringify( + userOp, + )} on chainId: ${chainId}`, + data, + }; + } catch (error: any) { + log.error( + `Error in estimating user op: ${parseError(error)} on chainId: ${estimateUserOperationGasData.chainId}`, + ); + return { + code: error.code, + message: parseError(error), + data: { + preVerificationGas: BigInt(0), + verificationGasLimit: BigInt(0), + callGasLimit: BigInt(0), + paymasterPostOpGasLimit: BigInt(0), + paymasterVerificationGasLimit: BigInt(0), + }, + }; + } + } + + async simulateValidation(simulateValidationData: SimulationDataV07) { + let handleOpsCallData; + try { + const { userOp, entryPointContract, chainId } = simulateValidationData; + + // for userOp completeness + userOp.factory = userOp.factory ?? "0x"; + userOp.factoryData = userOp.factoryData ?? "0x"; + + log.info( + `userOp received: ${customJSONStringify( + userOp, + )} on chainId: ${chainId}`, + ); + + const { maxPriorityFeePerGas, maxFeePerGas } = + await this.gasPriceService.get1559GasPrice(); + let gasPrice = Math.ceil(Number(maxFeePerGas) * 2).toString(16); + + await this.checkUserOperationForRejection( + { + userOp, + networkMaxPriorityFeePerGas: maxPriorityFeePerGas, + networkMaxFeePerGas: maxFeePerGas, + }, + entryPointContract, + ); + + const packed = packUserOperation(userOp); + const data = encodeFunctionData({ + abi: ENTRY_POINT_V07_ABI, + functionName: "handleOps", + args: [[packed], userOp.sender], + }); + + const { ownerAddress } = config.relayerManagers[0]; + log.info( + `Simulating with from address: ${ownerAddress} on chainId: ${chainId}`, + ); + + const ethEstimateGasParams = [ + { + from: ownerAddress, + to: entryPointContract.address, + data, + gasPrice: `0x${gasPrice}`, + }, + ]; + + log.info( + `ethEstimateGasParams: ${customJSONStringify( + ethEstimateGasParams, + )} on chainId: ${chainId}`, + ); + + const ethEstimateGasStart = performance.now(); + + const ethEstimateGasResponse = + await this.networkService.estimateGas(ethEstimateGasParams); + const ethEstimateGasEnd = performance.now(); + log.info( + `eth_estimateGas took: ${ + ethEstimateGasEnd - ethEstimateGasStart + } milliseconds`, + ); + + log.info( + `Response from eth_estimateGas: ${customJSONStringify( + ethEstimateGasResponse, + )} on chainId: ${chainId}`, + ); + + const ethEstimateGasError = ethEstimateGasResponse.error; + let totalGas = 0; + + if ( + ethEstimateGasError && + Object.keys(ethEstimateGasError).length > 0 && + ethEstimateGasError.data + ) { + const errorDescription = decodeErrorResult({ + abi: entryPointContract.abi, + data: ethEstimateGasError.data, + }); + + if (errorDescription.errorName === "FailedOp") { + const { args } = errorDescription; + + const reason = args[1]; + log.info( + `Transaction failed with reason: ${reason} on chainId: ${chainId}`, + ); + if (reason.includes("AA1") || reason.includes("AA2")) { + log.info(`error in account on chainId: ${chainId}`); + const message = this.removeSpecialCharacters(reason); + log.info(`message after removing special characters: ${message}`); + throw new RpcError( + message, + BUNDLER_ERROR_CODES.SIMULATE_VALIDATION_FAILED, + ); + } else if (reason.includes("AA3")) { + log.info(`error in paymaster on chainId: ${chainId}`); + const message = this.removeSpecialCharacters(reason); + log.info(`message after removing special characters: ${message}`); + throw new RpcError( + message, + BUNDLER_ERROR_CODES.SIMULATE_PAYMASTER_VALIDATION_FAILED, + ); + } else if (reason.includes("AA9")) { + log.info(`error in inner handle op on chainId: ${chainId}`); + const message = this.removeSpecialCharacters(reason); + log.info(`message after removing special characters: ${message}`); + throw new RpcError( + message, + BUNDLER_ERROR_CODES.WALLET_TRANSACTION_REVERTED, + ); + } else if (reason.includes("AA4")) { + log.info("error in verificationGasLimit being incorrect"); + const message = this.removeSpecialCharacters(reason); + log.info(`message after removing special characters: ${message}`); + throw new RpcError( + message, + BUNDLER_ERROR_CODES.SIMULATE_VALIDATION_FAILED, + ); + } + const message = this.removeSpecialCharacters(reason); + throw new RpcError( + message, + BUNDLER_ERROR_CODES.WALLET_TRANSACTION_REVERTED, + ); + } else { + const { args } = errorDescription; + + const reason = args[1] + ? args[1].toString() + : errorDescription.errorName; + const message = this.removeSpecialCharacters(reason); + throw new RpcError( + message, + BUNDLER_ERROR_CODES.WALLET_TRANSACTION_REVERTED, + ); + } + } else if (typeof ethEstimateGasResponse === "string") { + totalGas = Number(ethEstimateGasResponse); + } else { + return { + code: STATUSES.INTERNAL_SERVER_ERROR, + message: `Error in estimating handleOps gas: ${parseError( + ethEstimateGasResponse.error.message, + )}`, + data: { + totalGas: 0, + userOpHash: null, + handleOpsCallData, + }, + }; + } + + const start = performance.now(); + let packedUserOp = packUserOperation(userOp); + const userOpHash = getUserOpHash( + packedUserOp, + entryPointContract.address, + BigInt(chainId), + ); + log.info(`userOpHash: ${userOpHash} on chainId: ${chainId}`); + const end = performance.now(); + log.info(`Getting userOpHash took ${end - start} milliseconds`); + + return { + code: STATUSES.SUCCESS, + message: "userOp Validated", + data: { + totalGas, + userOpHash, + handleOpsCallData: null, + }, + }; + } catch (error: any) { + log.error( + `Error in simulateValidation: ${parseError(error)} on chainId: ${simulateValidationData.chainId}`, + ); + return { + code: error.code, + message: parseError(error), + data: { + totalGas: 0, + userOpHash: null, + handleOpsCallData, + }, + }; + } + } + + /** + * Check if the user operation is valid for the network + * @param validationData + * @returns true if the user operation is valid + */ + async checkUserOperationForRejection( + validationData: ValidationDataV07, + entryPointContract: EntryPointV07ContractType, + ): Promise { + // TODO: Check if balance >= required prefund if wallet deployment because we can't simulate deployment tx's + + const { userOp, networkMaxFeePerGas, networkMaxPriorityFeePerGas } = + validationData; + + const { maxPriorityFeePerGas, maxFeePerGas, preVerificationGas } = userOp; + + const { + maxPriorityFeePerGasThresholdPercentage, + maxFeePerGasThresholdPercentage, + preVerificationGasThresholdPercentage, + } = config; + + log.info( + `maxPriorityFeePerGasThresholdPercentage: ${maxPriorityFeePerGasThresholdPercentage} + maxFeePerGasThresholdPercentage: ${maxFeePerGasThresholdPercentage} + preVerificationGasThresholdPercentage: ${preVerificationGasThresholdPercentage}`, + ); + + const minimumAcceptableMaxPriorityFeePerGas = + Number(networkMaxPriorityFeePerGas) * + maxPriorityFeePerGasThresholdPercentage; + const minimumAcceptableMaxFeePerGas = + Number(networkMaxFeePerGas) * maxFeePerGasThresholdPercentage; + + log.info( + `minimumAcceptableMaxPriorityFeePerGas: ${minimumAcceptableMaxPriorityFeePerGas} minimumAcceptableMaxFeePerGas: ${minimumAcceptableMaxFeePerGas}`, + ); + log.info(`Checking if maxPriorityFeePerGas is within acceptable limits`); + + if (minimumAcceptableMaxPriorityFeePerGas > Number(maxPriorityFeePerGas)) { + log.info( + `maxPriorityFeePerGas in userOp: ${maxPriorityFeePerGas} is lower than expected maxPriorityFeePerGas: ${minimumAcceptableMaxPriorityFeePerGas}`, + ); + throw new RpcError( + `maxPriorityFeePerGas in userOp: ${maxPriorityFeePerGas} is lower than expected maxPriorityFeePerGas: ${minimumAcceptableMaxPriorityFeePerGas}`, + BUNDLER_ERROR_CODES.MAX_PRIORITY_FEE_PER_GAS_TOO_LOW, + ); + } + log.info(`maxPriorityFeePerGas is within acceptable limits`); + log.info(`Checking if maxFeePerGas is within acceptable limits`); + + if (minimumAcceptableMaxFeePerGas > Number(maxFeePerGas)) { + log.info( + `maxFeePerGas in userOp: ${maxFeePerGas} is lower than expected maxFeePerGas: ${minimumAcceptableMaxFeePerGas}`, + ); + throw new RpcError( + `maxFeePerGas in userOp: ${maxFeePerGas} is lower than expected maxFeePerGas: ${minimumAcceptableMaxFeePerGas}`, + BUNDLER_ERROR_CODES.MAX_FEE_PER_GAS_TOO_LOW, + ); + } + log.info(`maxFeePerGas is within acceptable limits`); + log.info(`Checking if preVerificationGas is within acceptable limits`); + + const baseFeePerGas = await this.gasPriceService.getBaseFeePerGas(); + + const networkPreVerificationGas = + await this.gasEstimator.estimatePreVerificationGas( + userOp, + BigInt(baseFeePerGas), + ); + log.info(`networkPreVerificationGas: ${networkPreVerificationGas}`); + + const minimumAcceptablePreVerificationGas = + Number(networkPreVerificationGas) * preVerificationGasThresholdPercentage; + + if (minimumAcceptablePreVerificationGas > Number(preVerificationGas)) { + log.info( + `preVerificationGas in userOp: ${Number(preVerificationGas)} is lower than minimumAcceptablePreVerificationGas: ${minimumAcceptablePreVerificationGas}`, + ); + throw new RpcError( + `preVerificationGas in userOp: ${preVerificationGas} is lower than minimumAcceptablePreVerificationGas: ${minimumAcceptablePreVerificationGas}`, + BUNDLER_ERROR_CODES.PRE_VERIFICATION_GAS_TOO_LOW, + ); + } + + // Check the user operation for validation and execution errors (if debug_traceCall is supported) + if ( + // we can't simulate deployment transactions, they will fail because of insufficient entrypoint deposit + userOp.factory === "0x" && + userOp.factoryData === "0x" && + nodeconfig.has("supportsDebugTraceCall") && + nodeconfig + .get("supportsDebugTraceCall") + .includes(this.networkService.chainId) + ) { + const traceStart = performance.now(); + + const packedUserOperation = toPackedUserOperation(userOp); + + log.info( + { chainId: this.networkService.chainId }, + `packedUserOperation: ${customJSONStringify(packedUserOperation)}`, + ); + const traceResult: CallTracerResult = + await this.networkService.provider.request({ + method: "debug_traceCall" as any, // coalesce so viem doesn't complain + params: [ + { + from: zeroAddress, // so we don't get balance errors + to: entryPointContract.address, + data: encodeFunctionData({ + abi: entryPointContract.abi, + functionName: "handleOps", + args: [ + [packedUserOperation], + "0xc75Bb3956c596efc6DB663cd3e2f64929d6AB0fc", + ], + }), + }, + "latest", + { + tracer: "callTracer", + tracerConfig: { + onlyTopCall: false, // we need deep traces + disableStack: false, // and we want the stack + enableReturnData: true, // and the return data so we can decode the error + }, + }, + ], + }); + const traceEnd = performance.now(); + + log.info(`debug_traceCall took ${traceEnd - traceStart} milliseconds`); + + const errors = findErrorsInTrace(traceResult, []); + + if (errors.length > 0) { + log.info(`Errors found in trace: ${customJSONStringify(errors)}`); + log.info(customJSONStringify(traceResult)); + throw new Error(errors.join(", ")); + } + } + + log.info( + `maxFeePerGas, maxPriorityFeePerGas and preVerification are within acceptable limits`, + ); + return true; + } + + removeSpecialCharacters(input: string): string { + const match = input.match(/AA(\d+)\s(.+)/); + + if (match) { + const errorCode = match[1]; // e.g., "25" + const errorMessage = match[2]; // e.g., "invalid account nonce" + const newMatch = `AA${errorCode} ${errorMessage}`.match( + // eslint-disable-next-line no-control-regex + /AA.*?(?=\\u|\u0000)/, + ); + if (newMatch) { + const extractedString = newMatch[0]; + return extractedString; + } + return `AA${errorCode} ${errorMessage}`; + } + return input; + } + + static parseSimulateHandleOpResult( + userOp: UserOperationType, + simulateHandleOpResult: any, + ) { + if (!simulateHandleOpResult?.errorName?.startsWith("ExecutionResult")) { + log.info( + `Inside ${!simulateHandleOpResult?.errorName?.startsWith( + "ExecutionResult", + )}`, + ); + // parse it as FailedOp + // if its FailedOp, then we have the paymaster param... otherwise its an Error(string) + log.info( + `simulateHandleOpResult.errorArgs: ${simulateHandleOpResult.errorArgs}`, + ); + if (!simulateHandleOpResult.errorArgs) { + throw new RpcError( + `Error: ${customJSONStringify(simulateHandleOpResult)}`, + BUNDLER_ERROR_CODES.WALLET_TRANSACTION_REVERTED, + ); + } + let { paymaster } = simulateHandleOpResult.errorArgs; + if (paymaster === config.zeroAddress) { + paymaster = undefined; + } + + const msg: string = + simulateHandleOpResult.errorArgs?.reason ?? + simulateHandleOpResult.toString(); + + if (paymaster == null) { + log.info( + `account validation failed: ${msg} for userOp: ${customJSONStringify( + userOp, + )}`, + ); + throw new RpcError(msg, BUNDLER_ERROR_CODES.SIMULATE_VALIDATION_FAILED); + } else { + log.info( + `paymaster validation failed: ${msg} for userOp: ${customJSONStringify( + userOp, + )}`, + ); + throw new RpcError( + msg, + BUNDLER_ERROR_CODES.SIMULATE_PAYMASTER_VALIDATION_FAILED, + ); + } + } + + const preOpGas = simulateHandleOpResult.errorArgs[0]; + log.info(`preOpGas: ${preOpGas}`); + const paid = simulateHandleOpResult.errorArgs[1]; + log.info(`paid: ${paid}`); + const validAfter = simulateHandleOpResult.errorArgs[2]; + log.info(`validAfter: ${validAfter}`); + const validUntil = simulateHandleOpResult.errorArgs[3]; + log.info(`validUntil: ${validUntil}`); + const targetSuccess = simulateHandleOpResult.errorArgs[4]; + log.info(`targetSuccess: ${targetSuccess}`); + const targetResult = simulateHandleOpResult.errorArgs[5]; + log.info(`targetResult: ${targetResult}`); + + return { + preOpGas, + paid, + validAfter, + validUntil, + targetSuccess, + targetResult, + }; + } +} diff --git a/src/common/simulation/index.ts b/src/common/simulation/index.ts index 043288fd..41851a90 100644 --- a/src/common/simulation/index.ts +++ b/src/common/simulation/index.ts @@ -1 +1,2 @@ export * from "./BundlerSimulationService"; +export * from "./BundlerSimulationService.v7"; diff --git a/src/common/simulation/test/mumbai.spec.ts b/src/common/simulation/test/mumbai.spec.ts deleted file mode 100644 index f43f9923..00000000 --- a/src/common/simulation/test/mumbai.spec.ts +++ /dev/null @@ -1,420 +0,0 @@ -describe("broken test", () => { - it.todo("should be fixed"); -}); - -// /* eslint-disable import/no-import-module-exports */ -// /* eslint-disable import/no-extraneous-dependencies */ -// import { BytesLike, ethers } from "ethers"; -// import { BiconomySmartAccount } from "@biconomy/account"; -// import { logger } from "../../logger"; -// import { BundlerSimulationService } from "../BundlerSimulationService"; -// import { config } from "../../../config"; -// import { EVMNetworkService } from "../../network"; -// import { -// AlchemySimulationService, -// TenderlySimulationService, -// } from "../external-simulation"; -// import { RedisCacheService } from "../../cache"; -// import { MumbaiGasPrice } from "../../gas-price/networks/MumbaiGasPrice"; -// import { EntryPointContractType, UserOperationType } from "../../types"; -// import { PublicClient, createPublicClient, getContract, http } from "viem"; - -// const log = logger.child({ -// module: module.filename.split("/").slice(-4).join("/"), -// }); - -// // mumbai gas estimations -// describe("Mumbai 4337 Gas Estimations", () => { -// let bundlerSimulationService: BundlerSimulationService; -// let entryPointContract: EntryPointContractType; -// let biconomySmartAccount: any; -// let eoa: any; -// let provider: PublicClient; - -// beforeEach(async () => { -// const networkService = new EVMNetworkService({ -// chainId: 80001, -// rpcUrl: config.chains.provider[80001], -// }); -// const cacheService = RedisCacheService.getInstance(); -// const gasPriceService = new MumbaiGasPrice(cacheService, networkService, { -// chainId: 80001, -// EIP1559SupportedNetworks: [80001], -// }); -// const alchemySimulationService = new AlchemySimulationService( -// networkService, -// ); -// const tenderlySimulationService = new TenderlySimulationService( -// gasPriceService, -// cacheService, -// { -// tenderlyUser: config.simulationData.tenderlyData.tenderlyUser, -// tenderlyProject: config.simulationData.tenderlyData.tenderlyProject, -// tenderlyAccessKey: config.simulationData.tenderlyData.tenderlyAccessKey, -// }, -// ); -// bundlerSimulationService = new BundlerSimulationService( -// networkService, -// tenderlySimulationService, -// alchemySimulationService, -// gasPriceService, -// ); - -// provider = createPublicClient({ -// transport: http(config.chains.provider[80001]), -// }); -// // this.provider = createPublicClient({ -// // transport: http(this.rpcUrl), -// // }); -// // entryPointContract = new ethers.Contract( -// // "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", -// // config.abi.entryPointAbi, -// // provider, -// // ); -// entryPointContract = getContract({ -// abi: config.abi.entryPointAbi, -// address: "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", -// publicClient: provider, -// }); - -// // dummy private key generated for test cases -// const privateKey = -// "3ad70468b6436cdcb10056758ff81bf9cb70b29ba8c6cba8d80b932649136c82"; -// const signer = new ethers.Wallet(privateKey, provider); -// eoa = await signer.getAddress(); -// log.info(`EOA address: ${eoa}`); - -// const biconomySmartAccountConfig = { -// signer, -// chainId: 80001, -// rpcUrl: config.chains.provider[80001], -// }; - -// const biconomyAccount = new BiconomySmartAccount( -// biconomySmartAccountConfig, -// ); -// biconomySmartAccount = await biconomyAccount.init({ accountIndex: 0 }); -// }); - -// /** callGasLimit success test cases */ -// // Biconomy Smart Account native asset transfer -// it("Biconomy Smart Account native asset transfer", async () => { -// const transaction = { -// to: eoa, -// value: ethers.utils.parseEther("0.0001"), -// data: "0x", -// }; - -// const partialUserOp = await biconomySmartAccount.buildUserOp([transaction]); - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// // Biconomy Smart Account single NFT transfer -// it("Biconomy Smart Account single NFT transfer", async () => { -// const nftInterface = new ethers.utils.Interface([ -// "function safeMint(address _to)", -// ]); -// const scwAddress = await biconomySmartAccount.getSmartAccountAddress(0); -// const data = nftInterface.encodeFunctionData("safeMint", [scwAddress]); - -// const nftAddress = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; // Todo // use from config -// const transaction = { -// to: nftAddress, -// value: "0x", -// data, -// }; - -// const partialUserOp = await biconomySmartAccount.buildUserOp([transaction]); - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// // Biconomy Smart Account 30 NFT transfers -// it("Biconomy Smart Account 30 NFT transfers", async () => { -// const nftInterface = new ethers.utils.Interface([ -// "function safeMint(address _to)", -// ]); -// const scwAddress = await biconomySmartAccount.getSmartAccountAddress(0); -// const data = nftInterface.encodeFunctionData("safeMint", [scwAddress]); - -// const nftAddress = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; // Todo // use from config -// const transaction = { -// to: nftAddress, -// value: "0x", -// data, -// }; - -// const transactions = [transaction]; -// for (let index = 0; index < 30; index += 1) { -// transactions.push(transaction); -// } - -// const partialUserOp = await biconomySmartAccount.buildUserOp(transactions); - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// // Biconomy Smart Account 100 NFT transfers -// it("Biconomy Smart Account 100 NFT transfers", async () => { -// const nftInterface = new ethers.utils.Interface([ -// "function safeMint(address _to)", -// ]); -// const scwAddress = await biconomySmartAccount.getSmartAccountAddress(0); -// const data = nftInterface.encodeFunctionData("safeMint", [scwAddress]); - -// const nftAddress = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; // Todo // use from config -// const transaction = { -// to: nftAddress, -// value: "0x", -// data, -// }; - -// const transactions = [transaction]; -// for (let index = 0; index < 99; index += 1) { -// transactions.push(transaction); -// } - -// const partialUserOp = await biconomySmartAccount.buildUserOp(transactions); - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// /** callGasLimit failure test cases */ -// // Biconomy Smart Account native asset transfer -// it("Biconomy Smart Account native asset transfer", async () => { -// const transaction = { -// to: eoa, -// value: ethers.utils.parseEther("10"), -// data: "0x", -// }; - -// const partialUserOp = await biconomySmartAccount.buildUserOp([transaction]); - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// /** verificationGasLimit failure test cases */ -// // failure in validation phase for factory -// // failure in validation phase for account -// // failure in validation phase for payamster - -// /** verificationGasLimit success test cases */ -// // basic smart account deployment + signature and nonce validation -// // high gas for factory smart account deployment -// it("Should estimate correctly for deployment with factory with high gas", async () => { -// const getSenderAndInitCode = async () => { -// const smartAccountFactoryAddress = -// "0x5991eEFb3498C98AEB4f2Dd5034768D394AD1EA6"; - -// const smartAccountFactory = new ethers.Contract( -// smartAccountFactoryAddress, -// [ -// { -// inputs: [ -// { internalType: "address", name: "_owner", type: "address" }, -// { internalType: "uint256", name: "_index", type: "uint256" }, -// ], -// name: "deployCounterFactualAccount", -// outputs: [ -// { internalType: "address", name: "proxy", type: "address" }, -// ], -// stateMutability: "nonpayable", -// type: "function", -// }, -// { -// inputs: [ -// { internalType: "address", name: "_owner", type: "address" }, -// { internalType: "uint256", name: "_index", type: "uint256" }, -// ], -// name: "getAddressForCounterFactualAccount", -// outputs: [ -// { internalType: "address", name: "_account", type: "address" }, -// ], -// stateMutability: "view", -// type: "function", -// }, -// ], -// provider, -// ); - -// const sender = -// await smartAccountFactory.getAddressForCounterFactualAccount(eoa, 0); - -// const { data } = -// await smartAccountFactory.populateTransaction.deployCounterFactualAccount( -// eoa, -// ethers.BigNumber.from("0"), -// ); - -// const initCode = ethers.utils.hexConcat([ -// smartAccountFactoryAddress, -// data as BytesLike, -// ]); - -// return { -// sender, -// initCode, -// }; -// }; - -// const { sender, initCode } = await getSenderAndInitCode(); - -// const partialUserOp = { -// sender, -// nonce: 0, -// initCode, -// callData: "0x", -// paymasterAndData: "0x", -// verificationGasLimit: 0, -// preVerificationGas: 0, -// callGasLimit: 0, -// maxFeePerGas: 0, -// maxPriorityFeePerGas: 0, -// signature: -// "0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b", -// }; - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// // high gas for account validateUserOp -// it("Should estimate correctly for account validateUserOp with high gas", async () => { -// const getSender = async () => { -// const smartAccountFactoryAddress = -// "0xF6dD70865c1d97aB2871aE25abA761334700c591"; - -// const smartAccountFactory = new ethers.Contract( -// smartAccountFactoryAddress, -// [ -// { -// inputs: [ -// { internalType: "address", name: "_owner", type: "address" }, -// { internalType: "uint256", name: "_index", type: "uint256" }, -// ], -// name: "deployCounterFactualAccount", -// outputs: [ -// { internalType: "address", name: "proxy", type: "address" }, -// ], -// stateMutability: "nonpayable", -// type: "function", -// }, -// { -// inputs: [ -// { internalType: "address", name: "_owner", type: "address" }, -// { internalType: "uint256", name: "_index", type: "uint256" }, -// ], -// name: "getAddressForCounterFactualAccount", -// outputs: [ -// { internalType: "address", name: "_account", type: "address" }, -// ], -// stateMutability: "view", -// type: "function", -// }, -// ], -// provider, -// ); - -// const sender = -// await smartAccountFactory.getAddressForCounterFactualAccount(eoa, 0); - -// return { -// sender, -// }; -// }; - -// const { sender } = await getSender(); - -// // eslint-disable-next-line @typescript-eslint/no-unused-vars -// const proxy = new ethers.Contract( -// sender, -// [ -// { -// inputs: [ -// { internalType: "uint256", name: "batchId", type: "uint256" }, -// ], -// name: "getNonce", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// ], -// provider, -// ); - -// const partialUserOp = { -// sender, -// nonce: 0, // await proxy.getNonce(0) once deployed -// initCode: "0x", -// callData: "0x", -// paymasterAndData: "0x", -// verificationGasLimit: 0, -// preVerificationGas: 0, -// callGasLimit: 0, -// maxFeePerGas: 0, -// maxPriorityFeePerGas: 0, -// signature: -// "0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b", -// }; - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// // high gas for paymaster validatePaymasterUserOp -// it("Should estimate correctly for paymaster validatePaymasterUserOp with high gas", async () => { -// const partialUserOp = await biconomySmartAccount.buildUserOp([]); - -// // paymaster address with high validationPaymasterUserOp() gas -// // const paymasterAddress = '0xf7609a20a1DD9614fA0B3Cc35508F686e50dF517'; -// const paymasterAndData = -// "0xf7609a20a1dd9614fa0b3cc35508f686e50df51700000000000000000000000002649f6d43556e76cf7a515a9f589bb23287378d00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041f27674355fc0b55eb09f57156156c4ef9ee1c27d11788d8526d8b0ca95b550db6d29d010f8a3a920ff8666e8d1b083913a55e9cae493dd8dec7b5814aa57abfe1b00000000000000000000000000000000000000000000000000000000000000"; -// partialUserOp.paymasterAndData = paymasterAndData; - -// const response = await bundlerSimulationService.estimateUserOperationGas({ -// userOp: partialUserOp as UserOperationType, -// chainId: 80001, -// entryPointContract, -// }); -// expect(response); -// }); - -// /** preVerificationGas test cases */ -// // should give 5% profit -// // should give 10% profit -// // should give 50% profit -// }); diff --git a/src/common/simulation/trace.ts b/src/common/simulation/trace.ts new file mode 100644 index 00000000..191509d9 --- /dev/null +++ b/src/common/simulation/trace.ts @@ -0,0 +1,49 @@ +import { Address, Hex } from "viem"; + +export interface CallTracerResult { + from: Address; + gas: Hex; + gasUsed: Hex; + to: Address; + input: Hex; + output: Hex; + value: Hex; + type: string; + error: string; +} + +export interface TraceCall { + from: string; + gas: string; + gasUsed: string; + to: string; + input: string; + output?: string; + error?: string; + calls?: TraceCall[]; + value?: string; + type: string; +} + +/** + * Recursively checks if any nested call contains errors and returns all errors. + * @param trace The top-level trace call object. + * @returns An array of errors (if found) + */ +export function findErrorsInTrace( + trace: TraceCall, + errors: string[], +): string[] { + if (trace.error) { + errors.push(trace.error); + } + + // If there are nested calls, check them recursively + if (trace.calls && trace.calls.length > 0) { + for (const nestedCall of trace.calls) { + findErrorsInTrace(nestedCall, errors); + } + } + + return errors; +} diff --git a/src/common/simulation/types/index.ts b/src/common/simulation/types/index.ts index 93796595..9bb1bf59 100644 --- a/src/common/simulation/types/index.ts +++ b/src/common/simulation/types/index.ts @@ -1,7 +1,9 @@ import { EntryPointContractType, + EntryPointV07ContractType, StateOverrideSetType, UserOperationType, + UserOperationStruct, } from "../../types"; // data response type that external simulation serivcereturns @@ -11,7 +13,7 @@ export type ExternalSimulationResponseType = { data: { refundAmount: number; refundAmountInUSD: number; - gasLimitFromSimulation: number | BigInt; + gasLimitFromSimulation: number | bigint; }; }; @@ -35,6 +37,12 @@ export type BundlerSimulationDataType = { chainId: number; }; +export type BundlerSimulationDataTypeV07 = { + userOp: UserOperationStruct; + entryPointContract: EntryPointV07ContractType; + chainId: number; +}; + export type EstimateUserOperationGasDataType = { userOp: UserOperationType; entryPointContract: EntryPointContractType; @@ -42,6 +50,13 @@ export type EstimateUserOperationGasDataType = { stateOverrideSet?: StateOverrideSetType; }; +export type EstimateUserOperationGasDataTypeV07 = { + userOp: UserOperationStruct; + entryPointContract: EntryPointV07ContractType; + chainId: number; + stateOverrideSet?: StateOverrideSetType; +}; + // data response type that simulation service returns export type SimulationResponseType = { isSimulationSuccessful: boolean; @@ -49,7 +64,7 @@ export type SimulationResponseType = { refundAmount?: number; refundAmountInUSD?: number; userOpHash?: string; - gasLimitFromSimulation: number | BigInt; + gasLimitFromSimulation: number | bigint; }; message: string; code?: number; @@ -68,6 +83,19 @@ export type EstimateUserOperationGasReturnType = { }; }; +export type EstimateUserOperationGasReturnTypeV07 = { + code: number; + message: string; + data: { + preVerificationGas: bigint; + verificationGasLimit: bigint; + callGasLimit: bigint; + paymasterVerificationGasLimit: bigint; + paymasterPostOpGasLimit: bigint; + userOpHash?: string; + }; +}; + export type EstimateUserOpGasFieldsType = { code: number; message: string; @@ -97,8 +125,20 @@ export type SimulationData = { chainId: number; }; +export type SimulationDataV07 = { + userOp: UserOperationStruct; + entryPointContract: EntryPointV07ContractType; + chainId: number; +}; + export type ValidationData = { userOp: UserOperationType; networkMaxPriorityFeePerGas: bigint; networkMaxFeePerGas: bigint; }; + +export type ValidationDataV07 = { + userOp: UserOperationStruct; + networkMaxPriorityFeePerGas: bigint; + networkMaxFeePerGas: bigint; +}; diff --git a/src/common/status/StatusService.test.ts b/src/common/status/StatusService.test.ts index bc45f369..48d26466 100644 --- a/src/common/status/StatusService.test.ts +++ b/src/common/status/StatusService.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { EVMNetworkService } from "../network"; import { NetworkBasedGasPriceType } from "../types"; import { StatusService } from "./StatusService"; @@ -9,6 +10,7 @@ describe("StatusService", () => { it("should return no errors if RPC returns the correct chain ID", async () => { const statusService = new StatusService({ // we mock the network service because it's used in this test + bundlerSimulationServiceMapV07: {} as any, networkServiceMap: { 137: { getChainId: (): Promise => Promise.resolve(chainId), @@ -44,6 +46,7 @@ describe("StatusService", () => { dbInstance: {} as any, gasPriceServiceMap: {} as any, bundlerSimulationServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = await statusService.checkRPC(chainId); @@ -68,6 +71,7 @@ describe("StatusService", () => { dbInstance: {} as any, networkServiceMap: {} as any, bundlerSimulationServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = @@ -94,6 +98,7 @@ describe("StatusService", () => { dbInstance: {} as any, networkServiceMap: {} as any, bundlerSimulationServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = @@ -128,6 +133,7 @@ describe("StatusService", () => { dbInstance: {} as any, bundlerSimulationServiceMap: {} as any, gasPriceServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = @@ -136,7 +142,9 @@ describe("StatusService", () => { expect(errors).toEqual([]); expect(durationSeconds).toBeGreaterThan(0); }); + }); + describe("checkMasterAccount", () => { it("should return an error if the master account was never funded", async () => { // we mock only the services that are used const statusService = new StatusService({ @@ -160,12 +168,15 @@ describe("StatusService", () => { dbInstance: {} as any, bundlerSimulationServiceMap: {} as any, gasPriceServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = - await statusService.checkRelayers(chainId); + await statusService.checkMasterAccount(chainId); - expect(errors).toEqual([`Relayer for chainId: ${chainId} is not funded`]); + expect(errors).toEqual([ + `Master account for chainId: ${chainId} is not funded`, + ]); expect(durationSeconds).toBeGreaterThan(0); }); }); @@ -183,6 +194,7 @@ describe("StatusService", () => { networkServiceMap: {} as any, gasPriceServiceMap: {} as any, bundlerSimulationServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = await statusService.checkRedis(); @@ -204,6 +216,7 @@ describe("StatusService", () => { networkServiceMap: {} as any, gasPriceServiceMap: {} as any, bundlerSimulationServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = await statusService.checkRedis(); @@ -225,6 +238,7 @@ describe("StatusService", () => { networkServiceMap: {} as any, gasPriceServiceMap: {} as any, bundlerSimulationServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = await statusService.checkMongo(); @@ -244,6 +258,7 @@ describe("StatusService", () => { networkServiceMap: {} as any, gasPriceServiceMap: {} as any, bundlerSimulationServiceMap: {} as any, + bundlerSimulationServiceMapV07: {} as any, }); const { durationSeconds, errors } = await statusService.checkMongo(); diff --git a/src/common/status/StatusService.ts b/src/common/status/StatusService.ts index bcd818f5..78139a19 100644 --- a/src/common/status/StatusService.ts +++ b/src/common/status/StatusService.ts @@ -1,6 +1,5 @@ -/* eslint-disable import/no-import-module-exports */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import nodeconfig from "config"; -import { parseEther } from "viem/utils"; import { IEVMAccount } from "../../relayer/account"; import { IRelayerManager } from "../../relayer/relayer-manager"; import { ICacheService } from "../cache"; @@ -12,8 +11,11 @@ import { IStatusService } from "./interface/IStatusService"; import { logger } from "../logger"; import { StatusServiceParamsType } from "./types"; import { GasPriceService } from "../gas-price"; -import { BundlerSimulationService } from "../simulation"; -import { formatHrtimeSeconds } from "../utils/formatting"; +import { + BundlerSimulationService, + BundlerSimulationServiceV07, +} from "../simulation"; +import { formatHrtimeSeconds } from "../utils/timing"; const filenameLogger = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -66,6 +68,10 @@ export class StatusService implements IStatusService { [chainId: number]: BundlerSimulationService; }; + bundlerSimulationServiceMapV07: { + [chainId: number]: BundlerSimulationServiceV07; + }; + constructor(params: StatusServiceParamsType) { const { cacheService, @@ -74,6 +80,7 @@ export class StatusService implements IStatusService { dbInstance, gasPriceServiceMap, bundlerSimulationServiceMap, + bundlerSimulationServiceMapV07, } = params; this.cacheService = cacheService; this.networkServiceMap = networkServiceMap; @@ -81,6 +88,7 @@ export class StatusService implements IStatusService { this.dbInstance = dbInstance; this.gasPriceServiceMap = gasPriceServiceMap; this.bundlerSimulationServiceMap = bundlerSimulationServiceMap; + this.bundlerSimulationServiceMapV07 = bundlerSimulationServiceMapV07; } async checkAllChains(): Promise { @@ -172,16 +180,30 @@ export class StatusService implements IStatusService { promises.push( new Promise((resolve, reject) => { - this.checkRelayers(chainId) + this.checkMasterAccount(chainId) .then((res) => { errors = errors.concat(res.errors); - latencies.relayers = res.durationSeconds; + latencies.masterAccount = res.durationSeconds; resolve(res); }) .catch((err) => reject(err)); }), ); + if (nodeconfig.get("health.checkRelayers")) { + promises.push( + new Promise((resolve, reject) => { + this.checkRelayers(chainId) + .then((res) => { + errors = errors.concat(res.errors); + latencies.relayers = res.durationSeconds; + resolve(res); + }) + .catch((err) => reject(err)); + }), + ); + } + await Promise.all(promises); } catch (err: any) { log.error(`Error in checkChain: ${customJSONStringify(err)}`); @@ -240,17 +262,14 @@ export class StatusService implements IStatusService { }); } - // checkRelayers (tries to) check if the relayers can actually relay transactions - async checkRelayers(chainId: number): Promise { + async checkMasterAccount(chainId: number): Promise { return statusCheck(async () => { - // eslint-disable-next-line @typescript-eslint/dot-notation const relayerManager = this.evmRelayerManagerMap["RM1"][chainId]; if (!relayerManager) { throw new Error( `Relayers are temporarily unavailable for chainId: ${chainId}`, ); } - // A basic sanity check: this will be 0 if and only if it was never funded. // Even if it was funded and then drained, the balance will be non-zero. const masterAccount = relayerManager.ownerAccountDetails.getPublicKey(); @@ -258,7 +277,19 @@ export class StatusService implements IStatusService { await this.networkServiceMap[chainId].getBalance(masterAccount); if (masterAccountBalance === 0n) { - throw new Error(`Relayer for chainId: ${chainId} is not funded`); + throw new Error(`Master account for chainId: ${chainId} is not funded`); + } + }); + } + + // checkRelayers (tries to) check if the relayers can actually relay transactions + async checkRelayers(chainId: number): Promise { + return statusCheck(async () => { + const relayerManager = this.evmRelayerManagerMap["RM1"][chainId]; + if (!relayerManager) { + throw new Error( + `Relayers are temporarily unavailable for chainId: ${chainId}`, + ); } // The following checks if the relayers are low on balance and returns an error only if the master account is also low on balance @@ -267,17 +298,11 @@ export class StatusService implements IStatusService { relayerManager.relayerQueue.get(address) || relayerManager.transactionProcessingRelayerMap[address]; - // this value is in ETH and we need to convert it to wei. - // This is confusing because the fundingBalanceThreshold is already parsed to wei - const fundingRelayerAmount = parseEther( - relayerManager.fundingRelayerAmount.toString(), - ); - - if ( - relayer.balance < relayerManager.fundingBalanceThreshold && - masterAccountBalance < fundingRelayerAmount - ) { - throw new Error(`Relayers for chainId: ${chainId} have low balance`); + const epsilonWei = 10; // a tiny amount greater than zero + if (relayer.balance < epsilonWei) { + throw new Error( + `Relayer with address: ${relayer.address} for chainId: ${chainId} is not funded`, + ); } } }); @@ -322,29 +347,20 @@ export class StatusService implements IStatusService { // - relayer queues sizes // - bundler version information (we need to fetch this from git somehow) // - RPC provider information (without leaking keys, basically just the provider name) - async info(): Promise { + async info(chainId: number): Promise { const statusInfo: StatusInfo = {}; - // eslint-disable-next-line @typescript-eslint/dot-notation const relayerManagers = this.evmRelayerManagerMap["RM1"]; if (!relayerManagers) { throw new Error(`Relayers are temporarily unavailable`); } - for (const [chainId, relayerManager] of Object.entries(relayerManagers)) { - const chainIdInt = parseInt(chainId, 10); - statusInfo[chainIdInt] = { - relayers: [], - }; + const relayerManager = relayerManagers[chainId]; + const relayers = Object.values(relayerManager.relayerMap); - for (const address of Object.keys(relayerManager.relayerMap)) { - const relayer = - relayerManager.relayerQueue.get(address) || - relayerManager.transactionProcessingRelayerMap[address]; - - statusInfo[chainIdInt].relayers.push(relayer); - } - } + statusInfo[chainId] = { + relayers: await Promise.all(relayers.map((relayer) => relayer.getInfo())), + }; return statusInfo; } diff --git a/src/common/status/interface/IStatusService.ts b/src/common/status/interface/IStatusService.ts index c1e73f43..0f0ac3f8 100644 --- a/src/common/status/interface/IStatusService.ts +++ b/src/common/status/interface/IStatusService.ts @@ -3,5 +3,5 @@ import { ChainStatus, StatusInfo } from "../../types"; export interface IStatusService { checkChain(chainId: number): Promise; checkAllChains(): Promise; - info(): Promise; + info(chainId: number): Promise; } diff --git a/src/common/status/types/index.ts b/src/common/status/types/index.ts index 734f982a..12a0ce22 100644 --- a/src/common/status/types/index.ts +++ b/src/common/status/types/index.ts @@ -4,7 +4,10 @@ import { ICacheService } from "../../cache"; import { IDBService } from "../../db"; import { GasPriceService } from "../../gas-price"; import { EVMNetworkService } from "../../network"; -import { BundlerSimulationService } from "../../simulation"; +import { + BundlerSimulationService, + BundlerSimulationServiceV07, +} from "../../simulation"; import { EVMRawTransactionType } from "../../types"; export type StatusServiceParamsType = { @@ -22,6 +25,9 @@ export type StatusServiceParamsType = { bundlerSimulationServiceMap: { [chainId: number]: BundlerSimulationService; }; + bundlerSimulationServiceMapV07: { + [chainId: number]: BundlerSimulationServiceV07; + }; }; export type RedisStatusResponseType = { diff --git a/src/common/types/index.ts b/src/common/types/index.ts index c87c6ed7..b6eca08e 100644 --- a/src/common/types/index.ts +++ b/src/common/types/index.ts @@ -1,43 +1,14 @@ -import { ENTRY_POINT_ABI } from "entry-point-gas-estimations"; +/* eslint-disable @typescript-eslint/no-explicit-any */ import { GetContractReturnType, Hex } from "viem"; +import { ENTRY_POINT_V07_ABI } from "../entrypoint-v7/abiv7"; +import { EVMAccountInfo } from "../../relayer/account"; +import { ENTRYPOINT_V6_ABI } from "@biconomy/gas-estimations"; export enum TransactionType { FUNDING = "FUNDING", BUNDLER = "BUNDLER", } -export enum TransactionMethodType { - BUNDLER = "eth_sendUserOperation", -} - -export enum EthMethodType { - ESTIMATE_USER_OPERATION_GAS = "eth_estimateUserOperationGas", - GET_USER_OPERATION_BY_HASH = "eth_getUserOperationByHash", - GET_USER_OPERATION_RECEIPT = "eth_getUserOperationReceipt", - SUPPORTED_ENTRY_POINTS = "eth_supportedEntryPoints", - CHAIN_ID = "eth_chainId", - GAS_AND_GAS_PRICES = "eth_getUserOpGasFields", - GET_USER_OPERATIONS_BY_API_KEY = "eth_getUserOperationsByApiKey", - GET_TRANSACTION_COUNT = "eth_getTransactionCount", - GET_BALANCE = "eth_getBalance", - GAS_PRICE = "eth_gasPrice", - FEE_HISTORY = "eth_feeHistory", - MAX_PRIORITY_FEE_PER_GAS = "eth_maxPriorityFeePerGas", - ESTIMATE_GAS = "eth_estimateGas", - ETH_CALL = "eth_call", - GET_TRANSACTION_RECEIPT = "eth_getTransactionReceipt", - SEND_RAW_TRANSACTION = "eth_sendRawTransaction", -} - -export enum AlchemyMethodType { - SIMULATE_EXECUTION = "alchemy_simulateExecution", -} - -export enum BiconomyMethodType { - GET_GAS_FEE_VALUES = "biconomy_getGasFeeValues", - GET_USER_OPERATION_STATUS = "biconomy_getUserOperationStatus", -} - export enum UserOperationStateEnum { BUNDLER_MEMPOOL = "BUNDLER_MEMPOOL", SUBMITTED = "SUBMITTED", @@ -81,7 +52,7 @@ export type EVMRawTransactionType = { type: string; }; -export type BundlerTransactionMessageType = { +export type SendUserOperation = { type: string; to: string; data: string; @@ -89,7 +60,7 @@ export type BundlerTransactionMessageType = { chainId: number; value: string; transactionId: string; - userOp?: UserOperationType; + userOp?: UserOperationType | UserOperationStruct; walletAddress?: string; }; @@ -123,6 +94,24 @@ export type UserOperationType = { signature: `0x${string}`; }; +export type UserOperationStruct = { + sender: Hex; + nonce: bigint; + factory: Hex; + factoryData: Hex; + callData: Hex; + callGasLimit: bigint; + verificationGasLimit: bigint; + preVerificationGas: bigint; + maxFeePerGas: bigint; + maxPriorityFeePerGas: bigint; + paymaster: Hex; + paymasterVerificationGasLimit: bigint; + paymasterPostOpGasLimit: bigint; + paymasterData: Hex; + signature: Hex; +}; + export type SymbolMapByChainIdType = { [key: number]: { [key: string]: string; @@ -136,6 +125,13 @@ export type EntryPointMapType = { }>; }; +export type EntryPointV07MapType = { + [chainId: number]: Array<{ + address: string; + entryPointContract: EntryPointV07ContractType; + }>; +}; + export type FeeSupportedToken = { address: string; symbol: string; @@ -203,7 +199,11 @@ export type UpdateRequestDataType = { }; export type EntryPointContractType = GetContractReturnType< - typeof ENTRY_POINT_ABI + typeof ENTRYPOINT_V6_ABI +>; + +export type EntryPointV07ContractType = GetContractReturnType< + typeof ENTRY_POINT_V07_ABI >; export type StateOverrideSetType = { @@ -228,8 +228,10 @@ export type EVMRelayerMetaDataType = { nonce: number; pendingCount: number; balance: number; + chainNonce?: number; + flashbotsNonce?: number; }; export type StatusInfo = { - [chainId: number]: { relayers: Array }; + [chainId: number]: { relayers: Array }; }; diff --git a/src/common/utils/axios-calls.ts b/src/common/utils/axios-calls.ts index 439a0b26..4843e482 100644 --- a/src/common/utils/axios-calls.ts +++ b/src/common/utils/axios-calls.ts @@ -1,4 +1,4 @@ -/* eslint-disable import/no-import-module-exports */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import axios from "axios"; import { logger } from "../logger"; import { customJSONStringify } from "./custom-json-stringifier"; @@ -7,11 +7,6 @@ const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -export const axiosGetCall = async (url: string, _data?: any) => { - const { data } = await axios.get(url, _data); - return data; -}; - export const axiosPostCall = async (url: string, _data?: any) => { try { log.info( diff --git a/src/common/utils/big-int-converter.ts b/src/common/utils/big-int-converter.ts index b8ebd26d..192d3a14 100644 --- a/src/common/utils/big-int-converter.ts +++ b/src/common/utils/big-int-converter.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ export const convertBigIntToString = (args: any): any => { if (typeof args === "bigint") { return args.toString(); diff --git a/src/common/utils/custom-json-stringifier.ts b/src/common/utils/custom-json-stringifier.ts index 307fc7dc..031893fb 100644 --- a/src/common/utils/custom-json-stringifier.ts +++ b/src/common/utils/custom-json-stringifier.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const customJSONStringify = (object: any) => JSON.stringify(object, (key, value) => typeof value === "bigint" ? value.toString() : value, diff --git a/src/common/utils/formatting.ts b/src/common/utils/formatting.ts deleted file mode 100644 index aadb6c45..00000000 --- a/src/common/utils/formatting.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Formats the process.hrtime in seconds. - * @param hrtime - The hrtime to be formatted. - * @returns The formatted hrtime in seconds. - */ -export const formatHrtimeSeconds = (hrtime: [number, number]) => { - const nanoseconds = hrtime[0] * 1e9 + hrtime[1]; - return nanoseconds / 1e9; -}; diff --git a/src/common/utils/id.ts b/src/common/utils/id.ts new file mode 100644 index 00000000..60d6dd8f --- /dev/null +++ b/src/common/utils/id.ts @@ -0,0 +1,30 @@ +import crypto from "crypto"; + +const randomInteger = (min: number, max: number): number => + Math.floor(Math.random() * (max - min + 1)) + min; + +export const generateTransactionId = (data: string) => { + const hashData = `0x${crypto + .createHash("sha256") + .update(data + Date.now() + randomInteger(1, 100000)) + .digest("hex")}`; + return hashData; +}; + +/** + * Generates a unique transaction id used for tracing transactions. + * It's useful because tx hashes can change due to replacements transactions but this ID stays the same. + * @returns A unique transaction id + */ +export const uniqueTransactonId = () => { + return generateTransactionId(Date.now().toString()); +}; + +/** + * Generates a unique request id used for tracing requests. + * Based on the current timestamp + random number + * @returns A unique request id + */ +export const uniqueRequestId = () => { + return Date.now() + randomInteger(1, 100000); +}; diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index c47a7067..c0e3459b 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -1,7 +1,7 @@ export * from "./axios-calls"; export * from "./cache-utils"; -export * from "./tx-id-generator"; +export * from "./id"; export * from "./parse-error"; -export * from "./user-op-utils"; +export * from "./userOperation"; export * from "./custom-json-stringifier"; export * from "./big-int-converter"; diff --git a/src/common/utils/parse-error.ts b/src/common/utils/parse-error.ts index 2a926f8a..6f37e00c 100644 --- a/src/common/utils/parse-error.ts +++ b/src/common/utils/parse-error.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { serializeError } from "serialize-error"; import { customJSONStringify } from "./custom-json-stringifier"; @@ -7,14 +8,14 @@ export const parseError = (error: any): string => { try { errorMessage = serializeError(error)?.message || customJSONStringify(error); - } catch (err) { + } catch { // ignore errorMessage = customJSONStringify(error); } } else { try { errorMessage = customJSONStringify(error); - } catch (err) { + } catch { // ignore errorMessage = error; } @@ -24,7 +25,7 @@ export const parseError = (error: any): string => { return errorMessage .toString() .replace(/\b(https?):\/\/[-\w+&@#/%?=~|!:,.;]*[-\w+&@#/%=~|]/g, "URL"); - } catch (err2) { + } catch { // ignore errorMessage = error; } diff --git a/src/common/utils/rpc-error.ts b/src/common/utils/rpc-error.ts index dd59e6e2..8f6a0cb1 100644 --- a/src/common/utils/rpc-error.ts +++ b/src/common/utils/rpc-error.ts @@ -2,6 +2,7 @@ export default class RpcError extends Error { constructor( msg: string, readonly code?: number, + // eslint-disable-next-line @typescript-eslint/no-explicit-any readonly data: any = undefined, ) { super(msg); diff --git a/src/common/utils/timing.test.ts b/src/common/utils/timing.test.ts new file mode 100644 index 00000000..cd116c1c --- /dev/null +++ b/src/common/utils/timing.test.ts @@ -0,0 +1,24 @@ +import { formatHrtimeSeconds, measureTime } from "./timing"; + +describe("timing", () => { + describe("formatHrtimeSeconds", () => { + it("should format the hrtime in seconds", () => { + const result = formatHrtimeSeconds([1, 500000000]); + expect(result).toBe(1.5); + }); + }); + + describe("measureTime", () => { + it("should measure the time it takes to execute a function", async () => { + const result = await measureTime(async () => { + return new Promise((resolve) => { + setTimeout(() => { + resolve("done"); + }, 1000); + }); + }); + expect(result[0]).toBe("done"); + expect(result[1]).toBeGreaterThan(1); + }); + }); +}); diff --git a/src/common/utils/timing.ts b/src/common/utils/timing.ts new file mode 100644 index 00000000..f9505325 --- /dev/null +++ b/src/common/utils/timing.ts @@ -0,0 +1,41 @@ +import pino from "pino"; + +/** + * Formats the process.hrtime in seconds. + * @param hrtime - The hrtime to be formatted. + * @returns The formatted hrtime in seconds. + */ +export const formatHrtimeSeconds = (hrtime: [number, number]) => { + const nanoseconds = hrtime[0] * 1e9 + hrtime[1]; + return nanoseconds / 1e9; +}; + +/** + * A helper function to measure the time it takes to execute a function + * @param fn - The function to be executed + * @returns The time it took to execute the function in seconds + */ +export const measureTime = async ( + fn: () => Promise, +): Promise<[T, number]> => { + const start = process.hrtime(); + const result = await fn(); + const end = process.hrtime(start); + return [result, formatHrtimeSeconds(end)]; +}; + +/** + * A helper function to log the time it takes to execute a function, given a pino logger + * @param fn - The function to be executed + * @param message - The message to be logged + * @returns The time it took to execute the function in seconds + */ +export const logMeasureTime = async ( + log: pino.Logger, + message: string, + fn: () => Promise, +): Promise<[T, number]> => { + const [result, time] = await measureTime(fn); + log.info({ durationSeconds: time }, message); + return [result, time]; +}; diff --git a/src/common/utils/tx-id-generator.ts b/src/common/utils/tx-id-generator.ts deleted file mode 100644 index 84e4bf79..00000000 --- a/src/common/utils/tx-id-generator.ts +++ /dev/null @@ -1,12 +0,0 @@ -import crypto from "crypto"; - -const randomInteger = (min: number, max: number): number => - Math.floor(Math.random() * (max - min + 1)) + min; - -export const generateTransactionId = (data: string) => { - const hashData = `0x${crypto - .createHash("sha256") - .update(data + Date.now() + randomInteger(1, 1000)) - .digest("hex")}`; - return hashData; -}; diff --git a/src/common/utils/user-op-utils.ts b/src/common/utils/userOperation.ts similarity index 94% rename from src/common/utils/user-op-utils.ts rename to src/common/utils/userOperation.ts index e8685d8c..ae460032 100644 --- a/src/common/utils/user-op-utils.ts +++ b/src/common/utils/userOperation.ts @@ -1,6 +1,4 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable no-continue */ -/* eslint-disable no-await-in-loop */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { Log, PublicClient, @@ -13,7 +11,12 @@ import { toHex, } from "viem"; import { logger } from "../logger"; -import { EntryPointContractType, StakeInfo, UserOperationType } from "../types"; +import { + EntryPointContractType, + EntryPointV07ContractType, + StakeInfo, + UserOperationType, +} from "../types"; import { parseError } from "./parse-error"; import { customJSONStringify } from "./custom-json-stringifier"; @@ -31,6 +34,19 @@ export const getPaymasterFromPaymasterAndData = ( return paymasterAddress; }; +export const getPaymasterFromPaymasterAndDataV7 = ( + paymasterAndData: string, +): string => { + if (!paymasterAndData) { + return ""; + } + const paymasterAddress = `${paymasterAndData.substring(0, 42)}`; + log.info( + `paymasterAddress: ${paymasterAddress} for paymasterAndData: ${paymasterAndData}`, + ); + return paymasterAddress; +}; + const filterLogs = ( userOperationEventLogFromReceipt: Log, transactionLogs: Log[], @@ -62,7 +78,7 @@ const filterLogs = ( * @param {number} chainId * @param {string} userOpHash * @param {TransactionReceipt} receipt - * @param {EntryPointContractType} entryPointContract + * @param {EntryPointContractType | EntryPointV07ContractType} entryPointContract * @param {bigint} fromBlock * @param {PublicClient} provider */ @@ -70,7 +86,7 @@ export const getUserOperationReceiptForFailedTransaction = async ( chainId: number, userOpHash: string, receipt: TransactionReceipt, - entryPointContract: EntryPointContractType, + entryPointContract: EntryPointContractType | EntryPointV07ContractType, fromBlock: bigint, provider: PublicClient, ): Promise => { @@ -199,8 +215,7 @@ export const getUserOperationReceiptForSuccessfulTransaction = async ( chainId: number, userOpHash: string, receipt: TransactionReceipt, - entryPointContract: EntryPointContractType, - // eslint-disable-next-line consistent-return + entryPointContract: EntryPointContractType | EntryPointV07ContractType, ): Promise<{ actualGasCost: number; actualGasUsed: number; diff --git a/src/config/CONFIG.md b/src/config/CONFIG.md index f347ce23..45e52cfd 100644 --- a/src/config/CONFIG.md +++ b/src/config/CONFIG.md @@ -81,6 +81,7 @@ Also you could take a look at [config/default.json](../../config/default.json) a ## Configuring Redis There are two supported Redis setups: + - **Redis Cluster:** used on production, configured through the `redisCluster` config object - **Single Redis instance:** used locally, configured through `dataSources.redisUrl` diff --git a/src/config/index.ts b/src/config/index.ts index 697e881c..16d6ce1c 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import crypto from "crypto-js"; import fs, { existsSync } from "fs"; import _, { isNumber } from "lodash"; @@ -45,7 +44,6 @@ export function mergeWithDecryptedConfig( export class Config implements IConfig { config: ConfigType; - // eslint-disable-next-line class-methods-use-this decryptConfig(): string { const encryptedEnvPath = process.env.BUNDLER_CONFIG_PATH || "./config.json.enc"; @@ -122,7 +120,9 @@ export class Config implements IConfig { this.config.supportedNetworks = [chainId]; } - this.validate(); + if (nodeconfig.get("config.validate")) { + this.validate(); + } log.info("Config loaded successfully"); } catch (error) { diff --git a/src/config/interface/IConfig.ts b/src/config/interface/IConfig.ts index b93af2f6..f9ce7438 100644 --- a/src/config/interface/IConfig.ts +++ b/src/config/interface/IConfig.ts @@ -1,8 +1,10 @@ +import { Hex } from "viem"; import { TransactionType } from "../../common/types"; enum RpcProviderType { PUBLIC = "public", PRIVATE = "private", + MEV_PROTECTED = "mev-protected", } interface RpcProvider { @@ -79,6 +81,12 @@ type EntryPointDataConfigType = { }; }; +type EntryPointV07DataConfigType = { + [address: `0x${string}`]: { + supportedChainIds: Array; + }; +}; + type PaymasterDashboardBackendConfigType = { dappDataUrl: string; }; @@ -111,6 +119,8 @@ export type ConfigType = { EIP1559SupportedNetworks: Array; // map of entrypoint addresses -> supported chain Ids entryPointData: EntryPointDataConfigType; + // map of entrypoint addresses -> supported chain Ids + entryPointV07Data: EntryPointV07DataConfigType; // array of chain Ids for supported L2 networks l2Networks: Array; // array of chain Ids for networks that are part of the Linea ecosystem @@ -129,6 +139,10 @@ export type ConfigType = { optimismNetworks: Array; // array of chain Ids for networks that are part of the Optimism ecosystem - they have a different gas price oracle than Optimism morphNetworks: Array; + // array of chain Ids for networks that are part of the Esi ecosystem - they have different gas limits than other networks + seiNetworks: Array; + // array of chain Ids for networks that are part of the Kakarot ecosystem - they have a 7m gas limit + kakarotNetworks: Array; paymasterDashboardBackendConfig: PaymasterDashboardBackendConfigType; // array of chain Ids for networks that are part of the Polygon zkEVM ecosystem polygonZKEvmNetworks: Array; @@ -148,11 +162,25 @@ export type ConfigType = { // array of chain Ids for networks that are supported by the Bundler supportedNetworks: Array; supportedTransactionType: ChainIdSupportedTransactionType; + // array of chain Ids for networks that are supported by the Bundler with EPv7 + supportedNetworksV07: Array; // array of chain Ids for networks that are TEST networks testnetNetworks: Array; // Transaction error messages transaction: TransactionConfigType; zeroAddress: `0x${string}`; + // hardcode override some gas values for a specific chain + gasOverrides: Record< + number, + { + baseFeePerGas: number; + preVerificationGas: number; + } + >; + // disable maxFeePerGas & maxPriorityFeePerGas validation for specific chain ids + disableFeeValidation: Array; + // a list of addresses that are allowed to access the admin endpoints (based on signature) + adminAddresses: Array; }; export interface IConfig { diff --git a/src/config/static-config.json b/src/config/static-config.json deleted file mode 100644 index 240d9f56..00000000 --- a/src/config/static-config.json +++ /dev/null @@ -1,969 +0,0 @@ -{ - "socketService": { - "wssUrl": "wss://sdk-ws.prod.biconomy.io/connection/websocket", - "httpUrl": "https://sdk-ws.prod.biconomy.io/api" - }, - "supportedNetworks": [ - 1, 80001, 137, 97, 56, 1442, 1101, 42161, 42170, 420, 10, 43113, 43114, - 84531, 8453, 59140, 59144, 5000, 5001, 204, 5611, 88888, 592, 88882, 81, - 1115, 1116, 169, 3441005, 91715, 9980, 421614, 11155111, 84532, 168587773, - 80085, 81457, 534351, 534352, 56400, 7000, 11155420, 80002 - ], - "EIP1559SupportedNetworks": [ - 1, 80001, 137, 42161, 420, 10, 43114, 43113, 84531, 8453, 59140, 59144, 204, - 5611, 421614, 11155111, 84532, 168587773, 80085, 81457, 42170, 169, 56400, - 11155420, 80002 - ], - "testnetNetworks": [ - 5, 80001, 97, 1442, 421613, 420, 43113, 84531, 59140, 5001, 5611, 81, 88882, - 59144, 421614, 11155111, 1115, 3441005, 84532, 168587773, 80085, 534351, - 56400, 11155420, 80002 - ], - "nonRM2SupportedNetworks": [ - 97, 1442, 1101, 42170, 420, 10, 43113, 43114, 84531, 8453, 59140, 59144, - 5000, 5001, 204, 5611, 88888, 592, 88882, 81, 1115, 1116, 169, 3441005, - 91715, 9980, 421614, 11155111, 84532, 168587773, 80085, 81457, 534351, - 534352, 56400, 7000, 11155420, 80002 - ], - "supportedTransactionType": { - "1": ["AA", "SCW", "BUNDLER"], - "5": ["AA", "SCW", "BUNDLER"], - "137": ["AA", "SCW", "BUNDLER"], - "80001": ["AA", "SCW", "BUNDLER"], - "1442": ["AA", "BUNDLER"], - "1101": ["AA", "BUNDLER"], - "421613": ["AA", "BUNDLER"], - "42161": ["AA", "SCW", "BUNDLER"], - "42170": ["AA", "BUNDLER"], - "97": ["AA", "BUNDLER"], - "56": ["AA", "SCW", "BUNDLER"], - "10": ["AA", "BUNDLER"], - "420": ["AA", "BUNDLER"], - "43113": ["AA", "BUNDLER"], - "43114": ["AA", "BUNDLER"], - "84531": ["AA", "BUNDLER"], - "8453": ["AA", "BUNDLER"], - "59140": ["AA", "BUNDLER"], - "59144": ["AA", "BUNDLER"], - "1287": ["AA", "BUNDLER"], - "1284": ["AA", "BUNDLER"], - "5000": ["AA", "BUNDLER"], - "5001": ["AA", "BUNDLER"], - "204": ["AA", "BUNDLER"], - "5611": ["AA", "BUNDLER"], - "592": ["AA", "BUNDLER"], - "88888": ["AA", "BUNDLER"], - "81": ["AA", "BUNDLER"], - "88882": ["AA", "BUNDLER"], - "1115": ["AA", "BUNDLER"], - "1116": ["AA", "BUNDLER"], - "169": ["AA", "BUNDLER"], - "3441005": ["AA", "BUNDLER"], - "91715": ["AA", "BUNDLER"], - "9980": ["AA", "BUNDLER"], - "7116": ["AA", "BUNDLER"], - "421614": ["AA", "BUNDLER"], - "11155111": ["AA", "BUNDLER"], - "168587773": ["AA", "BUNDLER"], - "84532": ["AA", "BUNDLER"], - "80085": ["AA", "BUNDLER"], - "81457": ["AA", "BUNDLER"], - "534351": ["AA", "BUNDLER"], - "534352": ["AA", "BUNDLER"], - "56400": ["AA", "BUNDLER"], - "7000": ["AA", "BUNDLER"], - "11155420": ["AA", "BUNDLER"], - "80002": ["AA", "BUNDLER"] - }, - "chains": { - "currency": { - "5": "ETH", - "137": "MATIC", - "80001": "MATIC", - "97": "BNB", - "56": "BNB", - "1": "ETH", - "1442": "ETH", - "1101": "ETH", - "421613": "ETH", - "42161": "ETH", - "42170": "ETH", - "420": "ETH", - "10": "ETH", - "43113": "AVAX", - "43114": "AVAX", - "84531": "ETH", - "8453": "ETH", - "59140": "ETH", - "59144": "ETH", - "5000": "MNT", - "5001": "MNT", - "204": "BNB", - "5611": "BNB", - "1287": "ETH", - "1284": "ETH", - "592": "ASTR", - "88888": "CHZ", - "81": "ASTR", - "88882": "CHZ", - "1115": "CORE", - "1116": "CORE", - "169": "ETH", - "3441005": "ETH", - "91715": "tcBNB", - "9980": "BNB", - "7116": "ETH", - "421614": "ETH", - "11155111": "ETH", - "80085": "BERA", - "84532": "ETH", - "168587773": "ETH", - "81457": "ETH", - "534351": "ETH", - "534352": "ETH", - "56400": "ZERO", - "7000": "ZETA", - "11155420": "ETH", - "80002": "MATIC" - }, - "decimal": { - "5": 18, - "137": 18, - "1": 18, - "80001": 18, - "97": 18, - "56": 18, - "1442": 18, - "1101": 18, - "421613": 18, - "42161": 18, - "42170": 18, - "420": 18, - "10": 18, - "43113": 18, - "43114": 18, - "84531": 18, - "8453": 18, - "59140": 18, - "59144": 18, - "5000": 18, - "5001": 18, - "204": 18, - "5611": 18, - "1287": 18, - "1284": 18, - "592": 18, - "88888": 18, - "81": 18, - "88882": 18, - "1115": 18, - "1116": 18, - "169": 18, - "3441005": 18, - "91715": 18, - "9980": 18, - "7116": 18, - "421614": 18, - "11155111": 18, - "80085": 18, - "84532": 18, - "168587773": 18, - "81457": 18, - "534351": 18, - "534352": 18, - "56400": 18, - "7000": 18, - "11155420": 18, - "80002": 18 - }, - "retryTransactionInterval": { - "5": 220000, - "137": 150000, - "80001": 120000, - "1": 220000, - "97": 150000, - "56": 150000, - "1442": 220000, - "1101": 220000, - "421613": 220000, - "42161": 220000, - "42170": 220000, - "420": 220000, - "10": 220000, - "43113": 150000, - "43114": 150000, - "84531": 220000, - "8453": 220000, - "59140": 220000, - "59144": 220000, - "1287": 220000, - "1284": 220000, - "5000": 220000, - "5001": 220000, - "204": 220000, - "5611": 220000, - "592": 220000, - "88888": 220000, - "81": 220000, - "88882": 220000, - "1115": 220000, - "1116": 220000, - "169": 220000, - "3441005": 220000, - "91715": 220000, - "9980": 220000, - "7116": 220000, - "421614": 220000, - "11155111": 220000, - "80085": 220000, - "84532": 220000, - "168587773": 220000, - "81457": 220000, - "534351": 220000, - "534352": 220000, - "56400": 220000, - "7000": 220000, - "11155420": 220000, - "80002": 120000 - }, - "updateFrequencyInSeconds": { - "5": 60, - "137": 10, - "1": 60, - "80001": 60, - "97": 60, - "56": 60, - "1442": 60, - "1101": 60, - "421613": 60, - "42161": 60, - "42170": 60, - "420": 60, - "10": 60, - "43113": 60, - "43114": 60, - "84531": 60, - "8453": 60, - "59140": 60, - "59144": 60, - "5000": 86400, - "5001": 60, - "204": 60, - "5611": 60, - "1287": 60, - "1284": 60, - "592": 60, - "88888": 60, - "81": 60, - "88882": 60, - "1115": 60, - "1116": 60, - "169": 60, - "3441005": 60, - "91715": 60, - "9980": 60, - "7116": 60, - "421614": 60, - "11155111": 60, - "80085": 60, - "84532": 60, - "168587773": 3600, - "81457": 60, - "534351": 60, - "534352": 60, - "56400": 60, - "7000": 60, - "11155420": 60, - "80002": 60 - } - }, - "defaultGasOverheads": { - "fixed": 21000, - "perUserOp": 18300, - "perUserOpWord": 4, - "zeroByte": 4, - "nonZeroByte": 16, - "bundleSize": 1, - "sigSize": 65 - }, - "relayerManagers": [ - { - "name": "RM1", - "gasLimitMap": { - "0": 21000, - "1": 710000, - "2": 1700000 - }, - "minRelayerCount": { - "5": 3, - "137": 200, - "1": 2, - "80001": 20, - "97": 2, - "56": 20, - "1442": 2, - "1101": 2, - "421613": 2, - "42161": 50, - "42170": 3, - "420": 2, - "10": 30, - "43113": 2, - "43114": 100, - "84531": 2, - "8453": 50, - "59140": 2, - "59144": 2, - "1287": 2, - "1284": 2, - "5000": 30, - "5001": 2, - "204": 2, - "5611": 2, - "592": 2, - "88888": 2, - "81": 1, - "88882": 1, - "1115": 1, - "1116": 15, - "169": 1, - "3441005": 1, - "91715": 1, - "9980": 1, - "7116": 40, - "421614": 5, - "11155111": 10, - "80085": 2, - "168587773": 20, - "84532": 2, - "81457": 20, - "534351": 2, - "534352": 2, - "56400": 100, - "7000": 2, - "11155420": 2, - "80002": 2 - }, - "maxRelayerCount": { - "5": 10, - "137": 400, - "1": 2, - "80001": 40, - "97": 7, - "56": 30, - "1442": 10, - "1101": 10, - "421613": 10, - "42161": 100, - "42170": 10, - "420": 10, - "10": 45, - "43113": 10, - "43114": 80, - "84531": 10, - "8453": 60, - "59140": 10, - "59144": 10, - "1287": 10, - "1284": 10, - "5000": 45, - "5001": 10, - "204": 10, - "5611": 10, - "592": 10, - "88888": 10, - "81": 5, - "88882": 5, - "1115": 5, - "1116": 20, - "169": 5, - "3441005": 5, - "91715": 5, - "9980": 5, - "7116": 100, - "421614": 20, - "11155111": 20, - "80085": 7, - "84532": 7, - "168587773": 30, - "81457": 30, - "534351": 5, - "534352": 5, - "56400": 120, - "7000": 5, - "11155420": 5, - "80002": 5 - }, - "inactiveRelayerCountThreshold": { - "5": 5, - "137": 150, - "80001": 15, - "97": 5, - "1": 2, - "56": 2, - "1442": 5, - "1101": 5, - "421613": 5, - "42161": 40, - "42170": 5, - "420": 5, - "10": 25, - "43113": 5, - "43114": 8, - "84531": 5, - "8453": 55, - "59140": 5, - "59144": 5, - "1287": 5, - "1284": 5, - "5000": 25, - "5001": 5, - "204": 5, - "5611": 5, - "592": 5, - "88888": 5, - "81": 5, - "88882": 5, - "1115": 5, - "1116": 5, - "169": 5, - "3441005": 5, - "91715": 5, - "9980": 5, - "7116": 60, - "421614": 15, - "11155111": 15, - "80085": 5, - "168587773": 15, - "84532": 5, - "81457": 15, - "534351": 5, - "534352": 5, - "56400": 80, - "7000": 3, - "11155420": 3, - "80002": 3 - }, - "pendingTransactionCountThreshold": { - "5": 15, - "137": 15, - "80001": 15, - "97": 15, - "1": 15, - "56": 15, - "1442": 15, - "1101": 15, - "421613": 15, - "42161": 15, - "42170": 15, - "420": 15, - "10": 15, - "43113": 15, - "43114": 15, - "84531": 15, - "8453": 15, - "59140": 15, - "59144": 15, - "1287": 15, - "1284": 15, - "5000": 15, - "5001": 15, - "204": 15, - "5611": 15, - "592": 15, - "88888": 15, - "81": 15, - "88882": 15, - "1115": 15, - "1116": 15, - "169": 15, - "3441005": 15, - "91715": 15, - "9980": 15, - "7116": 15, - "421614": 15, - "11155111": 15, - "80085": 15, - "168587773": 15, - "84532": 15, - "81457": 15, - "534351": 15, - "534352": 15, - "56400": 15, - "7000": 15, - "11155420": 15, - "80002": 15 - }, - "fundingRelayerAmount": { - "5": 0.001, - "137": 5, - "80001": 5, - "97": 0.1, - "1": 1, - "56": 0.15, - "1442": 0.2, - "1101": 0.2, - "421613": 0.2, - "42161": 0.01, - "42170": 0.1, - "420": 0.5, - "10": 0.02, - "43113": 1, - "43114": 0.5, - "84531": 0.5, - "8453": 0.001, - "59140": 0.1, - "59144": 0.1, - "1287": 1, - "1284": 1, - "5000": 2, - "5001": 2, - "204": 0.01, - "5611": 0.01, - "592": 10, - "88888": 10, - "81": 5, - "88882": 5, - "1115": 0.4, - "1116": 0.2, - "169": 0.002, - "3441005": 0.002, - "91715": 0.05, - "9980": 0.05, - "7116": 0.01, - "421614": 0.01, - "11155111": 0.1, - "80085": 0.01, - "168587773": 0.5, - "84532": 0.015, - "81457": 0.02, - "534351": 0.03, - "534352": 0.03, - "56400": 0.5, - "7000": 5, - "11155420": 0.01, - "80002": 1 - }, - "fundingBalanceThreshold": { - "5": 0.03, - "137": 3, - "80001": 3, - "97": 0.05, - "1": 0.5, - "56": 0.003, - "1442": 0.01, - "1101": 0.01, - "421613": 0.01, - "42161": 0.002, - "42170": 0.01, - "420": 0.2, - "10": 0.005, - "43113": 0.1, - "43114": 0.2, - "84531": 0.1, - "8453": 0.0008, - "59140": 0.01, - "59144": 0.01, - "1287": 0.1, - "1284": 0.1, - "5000": 1, - "5001": 1, - "204": 0.005, - "5611": 0.005, - "592": 5, - "88888": 5, - "81": 3, - "88882": 3, - "1115": 0.1, - "1116": 6, - "169": 0.001, - "3441005": 0.001, - "91715": 0.02, - "9980": 0.02, - "7116": 0.005, - "421614": 0.005, - "11155111": 0.05, - "80085": 0.005, - "168587773": 0.2, - "84532": 0.0075, - "81457": 0.01, - "534351": 0.015, - "534352": 0.015, - "56400": 0.2, - "7000": 2, - "11155420": 0.005, - "80002": 0.5 - }, - "newRelayerInstanceCount": { - "5": 3, - "137": 10, - "80001": 5, - "97": 1, - "1": 1, - "56": 1, - "1442": 2, - "1101": 2, - "421613": 2, - "42161": 1, - "42170": 2, - "420": 2, - "10": 2, - "43113": 2, - "43114": 2, - "84531": 2, - "8453": 2, - "59140": 2, - "59144": 2, - "1287": 2, - "1284": 2, - "5000": 2, - "5001": 2, - "204": 2, - "5611": 2, - "592": 2, - "88888": 2, - "81": 2, - "88882": 2, - "1115": 2, - "1116": 2, - "169": 2, - "3441005": 2, - "91715": 2, - "9980": 2, - "7116": 2, - "421614": 2, - "11155111": 2, - "80085": 2, - "168587773": 2, - "84532": 2, - "81457": 2, - "534351": 2, - "534352": 2, - "56400": 2, - "7000": 2, - "11155420": 2, - "80002": 2 - } - }, - { - "name": "RM2", - "gasLimitMap": { - "0": 21000, - "1": 710000, - "2": 1700000 - }, - "minRelayerCount": { - "5": 3, - "137": 5, - "1": 2, - "80001": 5, - "56": 2, - "42161": 2 - }, - "maxRelayerCount": { - "5": 10, - "137": 15, - "1": 2, - "80001": 15, - "56": 7, - "42161": 10 - }, - "inactiveRelayerCountThreshold": { - "5": 5, - "137": 5, - "80001": 5, - "1": 2, - "56": 5, - "42161": 5 - }, - "pendingTransactionCountThreshold": { - "5": 15, - "137": 15, - "80001": 15, - "1": 15, - "56": 15, - "42161": 15 - }, - "fundingRelayerAmount": { - "5": 0.001, - "137": 10, - "80001": 0.1, - "1": 1, - "56": 0.2, - "42161": 0.2 - }, - "fundingBalanceThreshold": { - "5": 0.03, - "137": 3, - "80001": 0.01, - "1": 0.5, - "56": 0.05, - "42161": 0.01 - }, - "newRelayerInstanceCount": { - "5": 3, - "137": 5, - "80001": 5, - "1": 1, - "56": 1, - "42161": 2 - } - } - ], - "transaction": { - "rpcResponseErrorMessages": { - "ALREADY_KNOWN": ["already known"], - "REPLACEMENT_TRANSACTION_UNDERPRICED": [ - "replacement fee too low", - "replacement_underpriced", - "replacement transaction underpriced" - ], - "TRANSACTION_UNDERPRICED": ["transaction underpriced"], - "INSUFFICIENT_FUNDS": [ - "insufficient funds for gas", - "transaction would cause overdraft", - "insufficient funds for transfer", - "insufficient funds for gas * price + value" - ], - "NONCE_TOO_LOW": [ - "nonce too low", - "nonce has already been used", - "lower than the current nonce of the account" - ], - "MAX_PRIORITY_FEE_HIGHER_THAN_MAX_FEE": [ - "max priority fee per gas higher than max fee per gas", - "cannot be higher than the fee cap" - ], - "RPC_FAILURE": [ - "error sending relay", - "unable to complete request at this time", - "timeout", - "we can't execute this request", - "rate limit exceeded", - "sender is over rate limit", - "the request timed out", - "the request took too long to respond", - "fetch failed", - "http request failed" - ], - "INTRINSIC_GAS_TOO_LOW": ["intrinsic gas too low"], - "MAX_FEE_PER_GAS_LESS_THAN_BLOCK_BASE_FEE": [ - "max fee per gas less than block base fee" - ] - } - }, - "feeOption": { - "supportedFeeTokens": { - "137": ["MATIC", "USDC", "USDT", "DAI", "WETH"], - "1": ["ETH", "USDC", "USDT", "DAI", "WETH"], - "56": ["BNB"], - "42161": ["ETH"] - }, - "offset": { - "137": { - "USDC": 1000000, - "USDT": 1000000, - "WETH": 1, - "DAI": 1, - "MATIC": 1, - "BICO": 1 - }, - "1": { - "USDC": 1000000, - "USDT": 1000000, - "WETH": 1, - "DAI": 1, - "MATIC": 1, - "BICO": 1 - }, - "42161": { - "USDC": 1000000, - "USDT": 1000000, - "WETH": 1, - "DAI": 1, - "MATIC": 1, - "BICO": 1 - }, - "56": { - "BND": 1, - "USDT": 1 - } - }, - "similarTokens": { - "1": ["ETH", "WETH"], - "137": ["MATIC", "WMATIC"], - "56": ["BNB", "WBNB"], - "42161": ["ETH", "WETH"] - }, - "nativeChainIds": { - "WETH": 1, - "WMATIC": 137 - }, - "logoUrl": { - "1": { - "ETH": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/eth.png", - "WETH": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/eth.png", - "MATIC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/matic.png", - "WMATIC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/matic.png", - "USDC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/usdc.png", - "USDT": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/usdt.png", - "DAI": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/dai.png" - }, - "137": { - "ETH": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/eth.png", - "WETH": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/eth.png", - "MATIC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/matic.png", - "WMATIC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/matic.png", - "USDC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/usdc.png", - "USDT": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/usdt.png", - "DAI": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/dai.png", - "BICO": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/bico.png" - }, - "56": { - "BNB": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/bnb.png", - "USDT": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/usdt.png" - }, - "42161": { - "ETH": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/eth.png", - "WETH": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/eth.png", - "MATIC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/matic.png", - "WMATIC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/matic.png", - "USDC": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/usdc.png", - "USDT": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/usdt.png", - "XDAI": "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/xdai.png" - } - }, - "tokenContractAddress": { - "1": { - "ETH": "0x0000000000000000000000000000000000000000", - "USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "DAI": "0x6B175474E89094C44Da98b954EedeAC495271d0F" - }, - "137": { - "MATIC": "0x0000000000000000000000000000000000000000", - "USDC": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", - "USDT": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", - "WETH": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", - "DAI": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", - "BICO": "0x91c89A94567980f0e9723b487b0beD586eE96aa7" - }, - "56": { - "BNB": "0x0000000000000000000000000000000000000000", - "USDT": "0xbf22b04E250A5921ab4dC0d4ceD6E391459e92D4" - }, - "42161": { - "ETH": "0x0000000000000000000000000000000000000000", - "USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "DAI": "0x6B175474E89094C44Da98b954EedeAC495271d0F" - } - }, - "decimals": { - "1": { - "ETH": 18, - "USDC": 6, - "USDT": 6, - "WETH": 18, - "DAI": 18 - }, - "137": { - "MATIC": 18, - "USDC": 6, - "USDT": 6, - "WETH": 18, - "DAI": 18, - "BICO": 18 - }, - "56": { - "BNB": 18, - "USDT": 18 - }, - "42161": { - "ETH": 18, - "USDC": 6, - "USDT": 6, - "WETH": 18, - "DAI": 18 - } - }, - "feeTokenTransferGas": { - "1": { - "ETH": 7300, - "USDC": 22975, - "USDT": 22975, - "WETH": 22975, - "DAI": 22975 - }, - "137": { - "MATIC": 7300, - "USDC": 22975, - "USDT": 22975, - "WETH": 22975, - "DAI": 22975, - "BICO": 22975 - }, - "56": { - "BNB": 7300, - "USDT": 22975 - }, - "42161": { - "ETH": 7300, - "USDC": 22975, - "USDT": 22975, - "WETH": 22975, - "DAI": 22975 - } - }, - "refundReceiver": { - "137": "0xfC614d949CcdC9EC2Ffc2b17eA0824c5A3beC69e", - "1": "0xfC614d949CcdC9EC2Ffc2b17eA0824c5A3beC69e", - "56": "0xfC614d949CcdC9EC2Ffc2b17eA0824c5A3beC69e", - "42161": "0xfC614d949CcdC9EC2Ffc2b17eA0824c5A3beC69e" - } - }, - "tokenPrice": { - "networkSymbols": { - "ETH": [ - 1, 3, 4, 42, 5, 421613, 42161, 10, 1442, 1101, 420, 10, 84531, 8453, - 59140, 59144, 5000, 5001, 5611, 204, 84532, 168587773 - ], - "BNB": [97, 56], - "MATIC": [8995, 80001, 15001, 16110, 137, 80002], - "BERA": [80085] - }, - "symbolMapByChainId": { - "1": { - "0x6B175474E89094C44Da98b954EedeAC495271d0F": "DAI" - }, - "137": { - "0x6B175474E89094C44Da98b954EedeAC495271d0F": "DAI" - }, - "56": { - "0x6B175474E89094C44Da98b954EedeAC495271d0F": "DAI" - }, - "42161": { - "0x6B175474E89094C44Da98b954EedeAC495271d0F": "DAI" - } - } - }, - "entryPointData": { - "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789": { - "supportedChainIds": [ - 1, 5, 80001, 137, 97, 56, 1442, 1101, 421613, 42161, 420, 10, 43114, - 84531, 8453, 59140, 59144, 592, 81, 204, 5611, 5000, 5001, 169, 3441005, - 1115, 1116, 91715, 9980, 421614, 11155111, 84532, 168587773, 43113, - 80085, 81457, 42170, 534351, 534352, 56400, 7000, 11155420, 80002 - ] - }, - "0x00000061fefce24a79343c27127435286bb7a4e1": { - "supportedChainIds": [88888, 88882] - } - }, - "aaDashboardBackend": { - "url": "https://paymaster-dashboard-backend-v2.staging.biconomy.io/api/v2/bundlers/auth" - }, - "zeroAddress": "0x0000000000000000000000000000000000000000" -} diff --git a/src/decrypt-config.ts b/src/decrypt-config.ts index 8db437bf..f2658aa3 100644 --- a/src/decrypt-config.ts +++ b/src/decrypt-config.ts @@ -7,7 +7,7 @@ const AES_PADDING = crypto.pad.Pkcs7; const AES_MODE = crypto.mode.CBC; function decryptConfig(): string { - // const encryptedEnvPath = './config.json.enc'; + // const encryptedEnvPaPth = './config.json.enc'; const encryptedEnvPath = process.argv[2]; // Taking the second command line argument as configName const passphrase = process.env.BUNDLER_CONFIG_PASSPHRASE; if (!passphrase) { @@ -45,6 +45,7 @@ function decryptConfig(): string { }); plaintext = encryptedBytes.toString(crypto.enc.Utf8); } catch (e) { + console.error(e); console.log("Incorrect password for decryption"); process.exit(); } diff --git a/src/encrypt-config.ts b/src/encrypt-config.ts index 9ee5bc56..bc6a75b9 100644 --- a/src/encrypt-config.ts +++ b/src/encrypt-config.ts @@ -1,5 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable no-console */ import crypto from "crypto-js"; import { promises, existsSync } from "fs"; diff --git a/src/relayer/account/EVMAccount.ts b/src/relayer/account/EVMAccount.ts index 0ce57bee..cf5dd87f 100644 --- a/src/relayer/account/EVMAccount.ts +++ b/src/relayer/account/EVMAccount.ts @@ -1,28 +1,92 @@ -/* eslint-disable no-param-reassign */ -/* eslint-disable @typescript-eslint/return-await */ import { PrivateKeyAccount, privateKeyToAccount } from "viem/accounts"; -import { createWalletClient, WalletClient, http } from "viem"; +import { + createWalletClient, + WalletClient, + http, + Hex, + Address, + formatEther, +} from "viem"; import { EVMRawTransactionType } from "../../common/types"; import { IEVMAccount } from "./interface/IEVMAccount"; +import { logger } from "../../common/logger"; +import { hideRpcUrlApiKey } from "../../common/network/utils"; +import { INetworkService } from "../../common/network"; +import { INonceManager } from "../nonce-manager"; export class EVMAccount implements IEVMAccount { + public rpcUrl: string; + + public address: Address; + + public chainId: number; + private account: PrivateKeyAccount; private publicKey: string; private walletClient: WalletClient; + private nonceManager: INonceManager; + + private networkService: INetworkService; + constructor( accountPublicKey: string, accountPrivateKey: string, rpcUrl: string, + chainId: number, + nonceManager: INonceManager, + networkService: INetworkService, ) { + this.rpcUrl = rpcUrl; this.account = privateKeyToAccount(`0x${accountPrivateKey}`); this.walletClient = createWalletClient({ - transport: http(rpcUrl), + transport: http(this.rpcUrl), account: privateKeyToAccount(`0x${accountPrivateKey}`), }); this.publicKey = accountPublicKey; + this.address = this.account.address; + this.chainId = chainId; + this.nonceManager = nonceManager; + this.networkService = networkService; + } + + public async getNonces(): Promise { + const promises = [ + this.nonceManager.getNonce(this), + this.networkService.getNetworkNonce(this), + ]; + + if (this.networkService.mevProtectedRpcUrl) { + promises.push(this.networkService.getNonce(this)); + } + + const results = await Promise.all(promises); + + const chain = + typeof results[1] === "string" ? parseInt(results[1], 16) : results[1]; + + return { + nonceManager: results[0], + chain, + flashbots: results.length > 2 ? results[2] : undefined, + }; + } + + public async getInfo(): Promise { + const [balanceWei, nonces] = await Promise.all([ + this.networkService.getBalance(this.address), + this.getNonces(), + ]); + + return { + chainId: this.chainId, + address: this.address, + rpcUrl: hideRpcUrlApiKey(this.rpcUrl), + balanceEth: formatEther(balanceWei), + nonces, + }; } getPublicKey(): string { @@ -34,47 +98,93 @@ export class EVMAccount implements IEVMAccount { } signTransaction(rawTransaction: EVMRawTransactionType): Promise { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return this.account.signTransaction(rawTransaction as any); } async sendTransaction( rawTransaction: EVMRawTransactionType, ): Promise { - let sendTransactionParameters; + const sendTransactionParameters = + this.createSendTransactionParams(rawTransaction); + + const txHash = await this.walletClient.sendTransaction( + sendTransactionParameters, + ); + + logger.info( + { txHash, rpcUrl: hideRpcUrlApiKey(this.rpcUrl) }, + `Transaction sent.`, + ); + + return txHash; + } + + private createSendTransactionParams( + rawTransaction: EVMRawTransactionType, + ): SendEip1559TransactionParameters | SendLegacyTransactionParameters { + const baseParams: BaseSendTransactionParameters = { + account: this.account, + to: rawTransaction.to as Hex, + value: BigInt(rawTransaction.value), + data: rawTransaction.data as Hex, + nonce: Number(rawTransaction.nonce), + chain: null, + gas: BigInt(rawTransaction.gasLimit), + }; + if (rawTransaction.type === "eip1559") { - const transactionType = "eip1559"; - sendTransactionParameters = { - account: this.account, - to: rawTransaction.to as `0x${string}`, - value: BigInt(rawTransaction.value), - data: rawTransaction.data as `0x${string}`, - nonce: Number(rawTransaction.nonce), - chain: null, - type: transactionType as unknown as "eip1559", - gas: BigInt(rawTransaction.gasLimit), - maxFeePerGas: BigInt( - rawTransaction.maxFeePerGas ? rawTransaction.maxFeePerGas : 0, - ), - maxPriorityFeePerGas: BigInt( - rawTransaction.maxPriorityFeePerGas - ? rawTransaction.maxPriorityFeePerGas - : 0, - ), + return { + ...baseParams, + type: "eip1559", + maxFeePerGas: BigInt(rawTransaction.maxFeePerGas || 0), + maxPriorityFeePerGas: BigInt(rawTransaction.maxPriorityFeePerGas || 0), }; } else { - const transactionType = "legacy"; - sendTransactionParameters = { - account: this.account, - to: rawTransaction.to as `0x${string}`, - value: BigInt(rawTransaction.value), - data: rawTransaction.data as `0x${string}`, - nonce: Number(rawTransaction.nonce), - chain: null, - type: transactionType as unknown as "legacy", + return { + ...baseParams, + type: "legacy", gas: BigInt(rawTransaction.gasLimit), gasPrice: BigInt(rawTransaction.gasPrice ? rawTransaction.gasPrice : 0), }; } - return await this.walletClient.sendTransaction(sendTransactionParameters); } } + +interface Nonces { + nonceManager: number; + chain: number; + flashbots?: number; +} + +export interface EVMAccountInfo { + chainId: number; + address: string; + rpcUrl: string; + balanceEth: string; // in ether + nonces: Nonces; +} + +interface BaseSendTransactionParameters { + account: PrivateKeyAccount; + to: Hex; + value: bigint; + data: Hex; + nonce: number; + chain: null; + gas: bigint; +} + +interface SendEip1559TransactionParameters + extends BaseSendTransactionParameters { + type: "eip1559"; + gas: bigint; + maxFeePerGas: bigint; + maxPriorityFeePerGas: bigint; +} + +interface SendLegacyTransactionParameters + extends BaseSendTransactionParameters { + type: "legacy"; + gasPrice: bigint; +} diff --git a/src/relayer/account/interface/IEVMAccount.ts b/src/relayer/account/interface/IEVMAccount.ts index 7be62eca..1242432a 100644 --- a/src/relayer/account/interface/IEVMAccount.ts +++ b/src/relayer/account/interface/IEVMAccount.ts @@ -1,9 +1,14 @@ +import { Address } from "viem"; import { EVMRawTransactionType } from "../../../common/types"; import { IAccount } from "./IAccount"; +import { EVMAccountInfo } from "../EVMAccount"; export interface IEVMAccount extends IAccount { + rpcUrl: string; + address: Address; getPublicKey(): string; signMessage(message: string): Promise; signTransaction(rawTransaction: EVMRawTransactionType): Promise; sendTransaction(rawTransaction: EVMRawTransactionType): Promise; + getInfo(): Promise; } diff --git a/src/relayer/consumer/BundlerConsumer.ts b/src/relayer/consumer/BundlerConsumer.ts index 9bab994d..c8da65d6 100644 --- a/src/relayer/consumer/BundlerConsumer.ts +++ b/src/relayer/consumer/BundlerConsumer.ts @@ -1,29 +1,30 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable @typescript-eslint/return-await */ import { ConsumeMessage } from "amqplib"; import { encodeFunctionData } from "viem"; -import { ENTRY_POINT_ABI } from "entry-point-gas-estimations"; import { ICacheService } from "../../common/cache"; import { logger } from "../../common/logger"; import { IQueue } from "../../common/queue"; import { - BundlerTransactionMessageType, + SendUserOperation, EntryPointMapType, EVMRawTransactionType, TransactionType, } from "../../common/types"; import { - customJSONStringify, getFailedTransactionRetryCountKey, getRetryTransactionCountKey, - parseError, } from "../../common/utils"; import { IEVMAccount } from "../account"; import { IRelayerManager } from "../relayer-manager"; import { ITransactionService } from "../transaction-service"; import { ITransactionConsumer } from "./interface/ITransactionConsumer"; import { BundlerConsumerParamsType } from "./types"; -import { STATUSES } from "../../server/api/shared/middleware"; +import { STATUSES } from "../../server/api/shared/statuses"; +import { ENTRY_POINT_V07_ABI } from "../../common/entrypoint-v7/abiv7"; +import { + isUserOpV06, + packUserOperation, +} from "../../common/entrypoint-v7/PackedUserOperation"; +import { ENTRYPOINT_V6_ABI } from "@biconomy/gas-estimations"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -33,7 +34,7 @@ export class BundlerConsumer { private transactionType: TransactionType = TransactionType.BUNDLER; - private queue: IQueue; + private queue: IQueue; chainId: number; @@ -56,51 +57,58 @@ export class BundlerConsumer this.cacheService = cacheService; } - onMessageReceived = async ( - msg?: ConsumeMessage, - // eslint-disable-next-line consistent-return - ): Promise => { + onMessageReceived = async (msg?: ConsumeMessage): Promise => { if (msg) { const transactionDataReceivedFromQueue = JSON.parse( msg.content.toString(), ); - log.info( - `onMessage received in ${this.transactionType}: ${customJSONStringify( - transactionDataReceivedFromQueue, - )} for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } on chainId: ${this.chainId}`, - ); - this.queue.ack(msg); - // eslint-disable-next-line consistent-return + const topLog = log.child({ + msg: transactionDataReceivedFromQueue, + chainId: this.chainId, + }); + + topLog.info(`BundlerConsumer.onMessageReceived`); + + try { + await this.queue.ack(msg); + } catch (err) { + topLog.error( + { err }, + `BundlerConsumer.onMessageReceived:: Error while acknowledging message`, + ); + } + const sendTransactionWithRetry = async (): Promise => { // get active relayer const activeRelayer = await this.relayerManager.getActiveRelayer(); - log.info( - `Active relayer for ${ - this.transactionType - } is ${activeRelayer?.getPublicKey()} for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } on chainId: ${this.chainId}`, - ); if (activeRelayer) { const { userOp } = transactionDataReceivedFromQueue; - log.info( - `Setting active relayer: ${activeRelayer?.getPublicKey()} as beneficiary for userOp: ${customJSONStringify( - userOp, - )} for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } on chainId: ${this.chainId}`, - ); - - const data = encodeFunctionData({ - abi: ENTRY_POINT_ABI, - functionName: "handleOps", - args: [[userOp], activeRelayer.getPublicKey() as `0x${string}`], + const _log = topLog.child({ + activeRelayer: activeRelayer.address, }); + + _log.info("Aquired active relayer for UserOperation"); + + const isV06 = isUserOpV06(userOp); + let data; + if (isV06) { + data = encodeFunctionData({ + abi: ENTRYPOINT_V6_ABI, + functionName: "handleOps", + args: [[userOp], activeRelayer.getPublicKey() as `0x${string}`], + }); + } else { + const packed = packUserOperation(userOp); + + data = encodeFunctionData({ + abi: ENTRY_POINT_V07_ABI, + functionName: "handleOps", + args: [[packed], activeRelayer.getPublicKey() as `0x${string}`], + }); + } transactionDataReceivedFromQueue.data = data; await this.cacheService.set( @@ -121,9 +129,8 @@ export class BundlerConsumer try { // call transaction service - log.info( - `Calling transactionService to sendTransaction for transactionId: ${transactionDataReceivedFromQueue.transactionId} on chainId: ${this.chainId}`, - ); + _log.info(`Calling transactionService.sendTransaction`); + const transactionServiceResponse = await this.transactionService.sendTransaction( transactionDataReceivedFromQueue, @@ -131,88 +138,65 @@ export class BundlerConsumer this.transactionType, this.relayerManager.name, ); - log.info( - `Response from transaction service for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } for ${ - this.transactionType - } after sending transaction on chainId: ${ - this.chainId - }: ${customJSONStringify(transactionServiceResponse)}`, + + _log.info( + { + response: transactionServiceResponse, + }, + `transactionService responded`, ); - if ( + + const isBundlerLowOnFunds = transactionServiceResponse.state === "failed" && - transactionServiceResponse.code === STATUSES.FUND_BUNDLER - ) { + transactionServiceResponse.code === STATUSES.FUND_BUNDLER; + + if (isBundlerLowOnFunds) { log.info( - `Bundler: ${activeRelayer.getPublicKey()} could not execute transaction due to low fund for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } on chainId: ${this.chainId}. Sending bundler for funding`, + `Relayer could not execute transaction due to low funds. Sending bundler for funding`, ); this.relayerManager.fundAndAddRelayerToActiveQueue( activeRelayer.getPublicKey(), ); return await sendTransactionWithRetry(); } - log.info( - `Adding relayer: ${activeRelayer.getPublicKey()} back to active relayer queue for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } for transactionType: ${this.transactionType} on chainId: ${ - this.chainId - }`, - ); + + _log.info(`Adding relayer back to active relayer queue`); + await this.relayerManager.addActiveRelayer( activeRelayer.getPublicKey(), ); - log.info( - `Added relayer: ${activeRelayer.getPublicKey()} back to active relayer queue for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } for transactionType: ${this.transactionType} on chainId: ${ - this.chainId - }`, - ); + + _log.info(`Added relayer back to active relayer queue`); + if (transactionServiceResponse.state === "success") { - log.info( - `Transaction sent successfully for transactionId: ${transactionDataReceivedFromQueue.transactionId} for ${this.transactionType} on chain ${this.chainId}`, - ); + _log.info(`Transaction sent successfully`); } else { - log.error( - `Transaction failed with error: ${ - transactionServiceResponse?.error || "unknown error" - } for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } for ${this.transactionType} on chain ${this.chainId}`, + _log.error( + { err: transactionServiceResponse?.error || "unknown error" }, + `Transaction failed`, ); } + await this.cacheService.delete( getFailedTransactionRetryCountKey( transactionDataReceivedFromQueue.transactionId, this.chainId, ), ); - } catch (error: any) { - log.error( - `Error in transaction service for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } for transactionType: ${this.transactionType} on chainId: ${ - this.chainId - } with error: ${customJSONStringify(parseError(error))}`, + } catch (err: unknown) { + _log.error( + { err }, + `Error in BundlerConsumer.sendTransactionWithRetry`, ); - log.info( - `Adding relayer: ${activeRelayer.getPublicKey()} back to active relayer queue for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } for transactionType: ${this.transactionType} on chainId: ${ - this.chainId - }`, - ); - this.relayerManager.addActiveRelayer(activeRelayer.getPublicKey()); - log.info( - `Added relayer: ${activeRelayer.getPublicKey()} back to active relayer queue for transactionId: ${ - transactionDataReceivedFromQueue.transactionId - } for transactionType: ${this.transactionType} on chainId: ${ - this.chainId - }`, + + _log.info(`Adding relayer back to active relayer queue`); + + await this.relayerManager.addActiveRelayer( + activeRelayer.getPublicKey(), ); + + _log.info(`Added relayer back to active relayer queue`); + await this.cacheService.delete( getFailedTransactionRetryCountKey( transactionDataReceivedFromQueue.transactionId, @@ -222,15 +206,11 @@ export class BundlerConsumer } } else { this.queue.publish(JSON.parse(msg.content.toString())); - log.info( - `No active relayer for transactionId: ${transactionDataReceivedFromQueue.transactionId} for transactionType: ${this.transactionType} on chainId: ${this.chainId}`, - ); + topLog.info(`No active relayer for user operation`); } }; await sendTransactionWithRetry(); } - log.info( - `No msg received from queue for transactionType: ${this.transactionType} on chainId: ${this.chainId}`, - ); + log.info({ chainId: this.chainId }, `Empty msg received from queue`); }; } diff --git a/src/relayer/consumer/types/index.ts b/src/relayer/consumer/types/index.ts index 9ab77d76..ff4516d4 100644 --- a/src/relayer/consumer/types/index.ts +++ b/src/relayer/consumer/types/index.ts @@ -1,8 +1,9 @@ import { ICacheService } from "../../../common/cache"; import { IQueue } from "../../../common/queue"; import { - BundlerTransactionMessageType, + SendUserOperation, EntryPointMapType, + EntryPointV07MapType, EVMRawTransactionType, } from "../../../common/types"; import { IEVMAccount } from "../../account"; @@ -10,12 +11,13 @@ import { IRelayerManager } from "../../relayer-manager"; import { ITransactionService } from "../../transaction-service"; export type BundlerConsumerParamsType = { - queue: IQueue; + queue: IQueue; relayerManager: IRelayerManager; transactionService: ITransactionService; cacheService: ICacheService; options: { chainId: number; entryPointMap: EntryPointMapType; + entryPointMapV07: EntryPointV07MapType; }; }; diff --git a/src/relayer/nonce-manager/EVMNonceManager.ts b/src/relayer/nonce-manager/EVMNonceManager.ts index 0ac0b1a5..40a21892 100644 --- a/src/relayer/nonce-manager/EVMNonceManager.ts +++ b/src/relayer/nonce-manager/EVMNonceManager.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import NodeCache from "node-cache"; import { ICacheService } from "../../common/cache"; import { logger } from "../../common/logger"; @@ -37,35 +36,38 @@ export class EVMNonceManager this.usedNonceTracker = new NodeCache(); } - async getNonce(address: string): Promise { + async getNonce(relayer: IEVMAccount): Promise { + const _log = log.child({ + relayerAddress: relayer.address, + chainId: this.chainId, + }); + let nonce: number | undefined; try { - nonce = this.pendingNonceTracker.get(address.toLowerCase()); - log.info( - `Nonce from pendingNonceTracker for account: ${address} on chainId: ${this.chainId} is ${nonce}`, + nonce = this.pendingNonceTracker.get(relayer.address.toLowerCase()); + _log.debug( + `Nonce from pendingNonceTracker for account: ${relayer.address} on chainId: ${this.chainId} is ${nonce}`, ); if (typeof nonce === "number") { - if (nonce === this.usedNonceTracker.get(address.toLowerCase())) { - log.info( - `Nonce ${nonce} for address ${address} is already used on chainId: ${this.chainId}. So clearing nonce and getting nonce from network`, + if ( + nonce === this.usedNonceTracker.get(relayer.address.toLowerCase()) + ) { + _log.warn( + `Nonce ${nonce} for address ${relayer.address} is already used. So clearing nonce and getting nonce from network`, ); - nonce = await this.getAndSetNonceFromNetwork(address); + nonce = await this.getAndSetNonceFromNetwork(relayer); } } else { - nonce = await this.getAndSetNonceFromNetwork(address); + nonce = await this.getAndSetNonceFromNetwork(relayer); } return nonce; } catch (error) { - log.error( - `Error in getting nonce for address: ${address} on chainId: ${ - this.chainId - } with error: ${parseError(error)}`, - ); - log.info( - `Fetching nonce from network for address: ${address} on chainId: ${this.chainId}`, + _log.error( + `Error in getting nonce for address: ${relayer} with error: ${parseError(error)}`, ); - return await this.getAndSetNonceFromNetwork(address); + _log.info(`Fetching nonce from network for address: ${relayer.address}`); + return await this.getAndSetNonceFromNetwork(relayer); } } @@ -81,12 +83,27 @@ export class EVMNonceManager return true; } - async getAndSetNonceFromNetwork(address: string): Promise { - const nonceFromNetwork = await this.networkService.getNonce(address); - log.info( - `Nonce from network for account: ${address} on chainId: ${this.chainId} is ${nonceFromNetwork}`, + async getAndSetNonceFromNetwork( + account: IEVMAccount, + pending?: boolean, + ): Promise { + const _log = log.child({ + address: account.address, + chainId: this.chainId, + }); + + const nonceFromNetwork = await this.networkService.getNonce( + account, + pending, + ); + + _log.debug( + `Nonce from network for account: ${account.address} is ${nonceFromNetwork}`, + ); + this.pendingNonceTracker.set( + account.address.toLowerCase(), + nonceFromNetwork, ); - this.pendingNonceTracker.set(address.toLowerCase(), nonceFromNetwork); return nonceFromNetwork; } } diff --git a/src/relayer/nonce-manager/interface/INonceManager.ts b/src/relayer/nonce-manager/interface/INonceManager.ts index da0e3fa6..248f1720 100644 --- a/src/relayer/nonce-manager/interface/INonceManager.ts +++ b/src/relayer/nonce-manager/interface/INonceManager.ts @@ -1,15 +1,16 @@ import { ICacheService } from "../../../common/cache"; import { INetworkService } from "../../../common/network"; +import { IEVMAccount } from "../../account"; export interface INonceManager { chainId: number; networkService: INetworkService; cacheService: ICacheService; - getNonce(address: string, pendingCount?: boolean): Promise; + getNonce(relayer: IEVMAccount, pendingCount?: boolean): Promise; getAndSetNonceFromNetwork( - address: string, - pendingCount: boolean, + account: IEVMAccount, + pending: boolean, ): Promise; markUsed(address: string, nonce: number): Promise; incrementNonce(address: string): Promise; diff --git a/src/relayer/relayer-manager/EVMRelayerManager.ts b/src/relayer/relayer-manager/EVMRelayerManager.ts index 84b1fbe8..f8f6ece1 100644 --- a/src/relayer/relayer-manager/EVMRelayerManager.ts +++ b/src/relayer/relayer-manager/EVMRelayerManager.ts @@ -1,5 +1,4 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable no-await-in-loop */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { Mutex } from "async-mutex"; import { privateToPublic, @@ -7,13 +6,11 @@ import { toChecksumAddress, } from "ethereumjs-util"; import hdkey from "hdkey"; -import { parseEther, toHex } from "viem"; +import { formatEther, parseEther, toHex } from "viem"; import { ICacheService } from "../../common/cache"; import { IGasPriceService } from "../../common/gas-price"; import { logger } from "../../common/logger"; import { INetworkService } from "../../common/network"; -import { getPendingTransactionIncreasingMessage } from "../../common/notification"; -import { INotificationManager } from "../../common/notification/interface"; import { EVMRawTransactionType, EVMRelayerMetaDataType, @@ -96,8 +93,6 @@ export class EVMRelayerManager gasPriceService: IGasPriceService; - notificationManager: INotificationManager; - constructor( evmRelayerManagerServiceParams: EVMRelayerManagerServiceParamsType, ) { @@ -109,7 +104,6 @@ export class EVMRelayerManager nonceManager, relayerQueue, transactionService, - notificationManager, } = evmRelayerManagerServiceParams; this.chainId = options.chainId; this.name = options.name; @@ -130,23 +124,6 @@ export class EVMRelayerManager this.transactionService = transactionService; this.nonceManager = nonceManager; this.cacheService = cacheService; - this.notificationManager = notificationManager; - } - - private async sendPendingTransactionIncreasingSlackNotification( - relayerAddress: string, - pendingCount: number, - ) { - const pendingTransactionIncreasingMessage = - getPendingTransactionIncreasingMessage( - relayerAddress, - this.chainId, - pendingCount, - ); - const slackNotifyObject = this.notificationManager.getSlackNotifyObject( - pendingTransactionIncreasingMessage, - ); - await this.notificationManager.sendSlackNotification(slackNotifyObject); } /** @@ -351,9 +328,14 @@ export class EVMRelayerManager async createRelayers( numberOfRelayers: number = this.minRelayerCount, ): Promise<`0x${string}`[]> { - log.info(`Waiting for lock to create relayers on chainId: ${this.chainId}`); + const _log = log.child({ + chainId: this.chainId, + }); + + _log.debug(`Waiting for lock to create relayers...`); const release = await createRelayerMutex.acquire(); - log.info(`Received lock to create relayers on chainId ${this.chainId}`); + _log.debug(`Received lock to create relayers!`); + const relayersMasterSeed = this.relayerSeed; const relayers: IEVMAccount[] = []; const relayersAddressList: `0x${string}`[] = []; @@ -372,15 +354,21 @@ export class EVMRelayerManager const nodePath = `${nodePathRoot + nodePathIndex}/`; const ethNodePath: any = ethRoot.derive(nodePath + relayerIndex); const privateKey = ethNodePath._privateKey.toString("hex"); + const ethPubkey = privateToPublic(ethNodePath.privateKey); const ethAddr = publicToAddress(ethPubkey).toString("hex"); + const ethAddress = toChecksumAddress(`0x${ethAddr}`); const address = ethAddress.toLowerCase(); + const relayer = new EVMAccount( address, privateKey, - this.networkService.rpcUrl, + this.networkService.mevProtectedRpcUrl || this.networkService.rpcUrl, + this.chainId, + this.nonceManager, + this.networkService, ); this.relayerMap[address] = relayer; relayers.push(relayer); @@ -391,16 +379,18 @@ export class EVMRelayerManager .getPublicKey() .toLowerCase() as `0x${string}`; try { - log.info( - `Creating relayer ${relayerAddress} on chainId: ${this.chainId}`, - ); + _log.info(`Creating relayer with address=${relayerAddress}`); + const balance = Number( toHex(await this.networkService.getBalance(relayerAddress)), ); - const nonce = await this.nonceManager.getNonce(relayerAddress); - log.info( - `Balance of relayer ${relayerAddress} is ${balance} and nonce is ${nonce} on chainId: ${this.chainId} with threshold ${this.fundingBalanceThreshold}`, + + const nonce = await this.nonceManager.getNonce(relayer); + + _log.info( + `Balance of relayer with address=${relayerAddress} is ${formatEther(BigInt(balance))} and nonce is ${BigInt(nonce)}`, ); + this.relayerQueue.push({ address: relayer.getPublicKey().toLowerCase(), pendingCount: 0, @@ -409,24 +399,17 @@ export class EVMRelayerManager }); relayersAddressList.push(relayerAddress); } catch (error) { - log.error(parseError(error)); - log.info( - `Error while getting balance and nonce for relayer ${relayerAddress} on chainId: ${this.chainId}`, + _log.error( + `Error while getting balance and nonce for relayer with address=${relayerAddress}: ${parseError(error)}`, ); } } } catch (error) { - log.error( - `failed to create relayers ${customJSONStringify(error)} on chainId: ${ - this.chainId - }`, - ); + _log.error(`failed to create relayers: ${customJSONStringify(error)}`); } release(); - log.info( - `Lock released after creating relayers on chainId: ${this.chainId}`, - ); + _log.debug(`Lock released after creating relayers`); return relayersAddressList; } @@ -437,24 +420,26 @@ export class EVMRelayerManager */ hasBalanceBelowThreshold(relayerAddress: string): boolean { const address = relayerAddress.toLowerCase(); + const _log = log.child({ + relayerAddress, + chainId: this.chainId, + }); + const relayerData = this.relayerQueue .list() .find((relayer) => relayer.address === address); + if (relayerData) { const relayerBalance = BigInt(relayerData.balance); - log.info( - `Relayer ${address} balance is ${relayerBalance} on chainId: ${this.chainId}`, - ); + _log.info(`Relayer ${address} balance is ${formatEther(relayerBalance)}`); if (relayerBalance <= this.fundingBalanceThreshold) { - log.info( - `Relayer ${address} balance ${relayerBalance} is below threshold of ${this.fundingBalanceThreshold} on chainId: ${this.chainId}`, + _log.info( + `Relayer balance ${formatEther(relayerBalance)} is below threshold of ${formatEther(this.fundingBalanceThreshold)}`, ); return true; } } else { - log.error( - `Relayer ${address} not found in relayer queue on chainId: ${this.chainId}`, - ); + _log.error(`Relayer ${address} not found in relayer queue`); } return false; } diff --git a/src/relayer/relayer-manager/types/index.ts b/src/relayer/relayer-manager/types/index.ts index a73824cb..1c77fd19 100644 --- a/src/relayer/relayer-manager/types/index.ts +++ b/src/relayer/relayer-manager/types/index.ts @@ -1,7 +1,6 @@ import { ICacheService } from "../../../common/cache"; import { IGasPriceService } from "../../../common/gas-price"; import { INetworkService } from "../../../common/network"; -import { INotificationManager } from "../../../common/notification/interface"; import { EVMRawTransactionType, EVMRelayerMetaDataType, @@ -18,7 +17,6 @@ export type EVMRelayerManagerServiceParamsType = { transactionService: ITransactionService; nonceManager: INonceManager; relayerQueue: IRelayerQueue; - notificationManager: INotificationManager; options: { name: string; chainId: number; diff --git a/src/relayer/relayer-queue/EVMRelayerQueue.ts b/src/relayer/relayer-queue/EVMRelayerQueue.ts index 10cfafb5..00a518dc 100644 --- a/src/relayer/relayer-queue/EVMRelayerQueue.ts +++ b/src/relayer/relayer-queue/EVMRelayerQueue.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import { Mutex } from "async-mutex"; import { IRelayerQueue } from "./interface/IRelayerQueue"; // import { SortEVMRelayerByBalance } from './strategy'; @@ -62,10 +61,7 @@ export class EVMRelayerQueue implements IRelayerQueue { async push(item: EVMRelayerMetaDataType): Promise { await pushMutex.runExclusive(() => { this.items.push(item); - // this.items = SortEVMRelayerByBalance.performAlgorithm( - // this.items, - // ); - log.info(`Relayer queue after push: ${customJSONStringify(this.items)}`); + log.debug(`Relayer queue after push: ${customJSONStringify(this.items)}`); }); } diff --git a/src/relayer/retry-transaction-service/EVMRetryTransactionService.ts b/src/relayer/retry-transaction-service/EVMRetryTransactionService.ts index 426a0941..015d60b9 100644 --- a/src/relayer/retry-transaction-service/EVMRetryTransactionService.ts +++ b/src/relayer/retry-transaction-service/EVMRetryTransactionService.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import { ConsumeMessage } from "amqplib"; import { logger } from "../../common/logger"; import { INetworkService } from "../../common/network"; @@ -10,18 +9,14 @@ import { IRelayerManager } from "../relayer-manager"; import { ITransactionService } from "../transaction-service/interface/ITransactionService"; import { IRetryTransactionService } from "./interface/IRetryTransactionService"; import { EVMRetryTransactionServiceParamsType } from "./types"; -import { INotificationManager } from "../../common/notification/interface"; -import { getAccountUndefinedNotificationMessage } from "../../common/notification"; -import { - customJSONStringify, - getTransactionMinedKey, - parseError, -} from "../../common/utils"; +import { getTransactionMinedKey } from "../../common/utils"; import { ICacheService } from "../../common/cache"; +import { FlashbotsTxStatus } from "../../common/network/FlashbotsClient"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); + export class EVMRetryTransactionService implements IRetryTransactionService { @@ -33,8 +28,6 @@ export class EVMRetryTransactionService queue: IQueue; - notificationManager: INotificationManager; - cacheService: ICacheService; EVMRelayerManagerMap: { @@ -51,30 +44,36 @@ export class EVMRetryTransactionService transactionService, networkService, retryTransactionQueue, - notificationManager, cacheService, } = evmRetryTransactionServiceParams; this.chainId = options.chainId; this.EVMRelayerManagerMap = options.EVMRelayerManagerMap; this.transactionService = transactionService; this.networkService = networkService; - this.notificationManager = notificationManager; this.cacheService = cacheService; this.queue = retryTransactionQueue; } onMessageReceived = async (msg?: ConsumeMessage) => { if (msg) { - log.info( - `Message received from retry transaction queue on chainId: ${ - this.chainId - }: ${customJSONStringify(msg.content.toString())}`, - ); - this.queue.ack(msg); const transactionDataReceivedFromRetryQueue = JSON.parse( msg.content.toString(), ); + log.info( + { chainId: this.chainId, msg: transactionDataReceivedFromRetryQueue }, + `Message received from retry transaction queue`, + ); + + try { + await this.queue.ack(msg); + } catch (err) { + log.error( + { err, chainId: this.chainId }, + `Error while acknowledging message`, + ); + } + const { transactionHash, transactionId, @@ -83,6 +82,13 @@ export class EVMRetryTransactionService relayerManagerName, } = transactionDataReceivedFromRetryQueue; + const _log = log.child({ + chainId: this.chainId, + transactionHash, + transactionId, + relayerAddress, + }); + let account: IEVMAccount; if (transactionType === TransactionType.FUNDING) { account = @@ -99,58 +105,54 @@ export class EVMRetryTransactionService ); if (isTransactionMined === "1") { - log.info( - `isTransactionMined: ${isTransactionMined} for transactionHash: ${transactionHash} and transactionId: ${transactionId} on chainId: ${this.chainId} hence not making RPC call to get receipt`, + _log.info( + `Transaction mined, hence not making RPC call to get receipt`, ); await this.cacheService.delete(getTransactionMinedKey(transactionId)); return; } - log.info( - `Checking transaction status of transactionHash: ${transactionHash} with transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.info(`Checking transaction status`); + let transactionReceipt = null; try { transactionReceipt = await this.networkService.getTransactionReceipt(transactionHash); - log.info( - `Transaction receipt for transactionHash: ${transactionHash} with transactionId: ${transactionId} on chainId: ${ - this.chainId - } is ${customJSONStringify(transactionReceipt)}`, - ); + _log.info({ transactionReceipt }, `Transaction receipt received`); } catch (error) { - log.error( - `Error in fetching transaction receipt from network for transactionHash: ${transactionHash} with transactionId: ${transactionId} on chainId: ${ - this.chainId - } is ${parseError(error)}`, - ); + log.error({ error }, `Error in fetching transaction receipt`); } if (transactionReceipt) { - log.info( - `Transaction receipt receivied for transactionHash: ${transactionHash} and transactionId: ${transactionId} on chainId: ${this.chainId}. Hence not retrying the transaction.`, + _log.info( + `Transaction receipt receivied. Not retrying the transaction.`, ); } else { - log.info( - `Transaction receipt not receivied for transactionHash: ${transactionHash} and transactionId: ${transactionId} on chainId: ${this.chainId}. Hence retrying the transaction.`, + _log.info( + `Transaction receipt not receivied. Retrying the transaction.`, ); + if ( + this.networkService.supportsFlashbots && + this.networkService.flashbots != null + ) { + try { + const flashbotsStatus = + await this.networkService.flashbots?.waitForTransaction( + transactionHash, + ); + + if (flashbotsStatus.status === FlashbotsTxStatus.FAILED) { + _log.error(`Flashbots transaction failed`); + } + } catch (error) { + _log.error({ error }, `Error in fetching flashbots status`); + return; + } + } + if (!account) { - log.error( - `Account not found for transactionHash: ${transactionHash} and transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - // TODO: send slack notification - const message = getAccountUndefinedNotificationMessage( - transactionId, - relayerAddress, - transactionType, - relayerManagerName, - ); - const slackNotifyObject = - this.notificationManager.getSlackNotifyObject(message); - await this.notificationManager.sendSlackNotification( - slackNotifyObject, - ); + _log.error(`Account not found`); } else { await this.transactionService.retryTransaction( transactionDataReceivedFromRetryQueue, @@ -161,8 +163,9 @@ export class EVMRetryTransactionService } } } else { - log.info( - `Message not received on retry transaction queue on chainId: ${this.chainId}`, + log.warn( + { chainId: this.chainId }, + `Empty message received on retry transaction queue`, ); } }; diff --git a/src/relayer/retry-transaction-service/types/index.ts b/src/relayer/retry-transaction-service/types/index.ts index fbbce24a..20f837a8 100644 --- a/src/relayer/retry-transaction-service/types/index.ts +++ b/src/relayer/retry-transaction-service/types/index.ts @@ -1,6 +1,5 @@ import { ICacheService } from "../../../common/cache"; import { INetworkService } from "../../../common/network"; -import { INotificationManager } from "../../../common/notification/interface"; import { IQueue } from "../../../common/queue"; import { RetryTransactionQueueData } from "../../../common/queue/types"; import { EVMRawTransactionType } from "../../../common/types"; @@ -12,7 +11,6 @@ export type EVMRetryTransactionServiceParamsType = { transactionService: ITransactionService; networkService: INetworkService; retryTransactionQueue: IQueue; - notificationManager: INotificationManager; cacheService: ICacheService; options: { chainId: number; diff --git a/src/relayer/transaction-listener/EVMTransactionListener.ts b/src/relayer/transaction-listener/EVMTransactionListener.ts index ee06edfa..43b357ca 100644 --- a/src/relayer/transaction-listener/EVMTransactionListener.ts +++ b/src/relayer/transaction-listener/EVMTransactionListener.ts @@ -1,11 +1,10 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable no-await-in-loop */ import { TransactionReceipt, decodeErrorResult, toHex } from "viem"; import { ICacheService } from "../../common/cache"; import { ITransactionDAO, IUserOperationDAO, IUserOperationStateDAO, + IUserOperationV07DAO, } from "../../common/db"; import { IQueue } from "../../common/interface"; import { logger } from "../../common/logger"; @@ -14,6 +13,8 @@ import { RetryTransactionQueueData } from "../../common/queue/types"; import { EntryPointContractType, EntryPointMapType, + EntryPointV07ContractType, + EntryPointV07MapType, EVMRawTransactionType, TransactionStatus, TransactionType, @@ -38,6 +39,8 @@ import { OnTransactionSuccessParamsType, } from "./types"; import { config } from "../../config"; +import { FlashbotsTxStatus } from "../../common/network/FlashbotsClient"; +import pino from "pino"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -56,10 +59,14 @@ export class EVMTransactionListener userOperationDao: IUserOperationDAO; + userOperationDaoV07: IUserOperationV07DAO; + userOperationStateDao: IUserOperationStateDAO; entryPointMap: EntryPointMapType; + entryPointV07Map: EntryPointV07MapType; + cacheService: ICacheService; constructor(evmTransactionListenerParams: EVMTransactionListenerParamsType) { @@ -69,15 +76,18 @@ export class EVMTransactionListener retryTransactionQueue, transactionDao, userOperationDao, + userOperationDaoV07, userOperationStateDao, cacheService, } = evmTransactionListenerParams; this.chainId = options.chainId; this.entryPointMap = options.entryPointMap; + this.entryPointV07Map = options.entryPointMapV07; this.networkService = networkService; this.retryTransactionQueue = retryTransactionQueue; this.transactionDao = transactionDao; this.userOperationDao = userOperationDao; + this.userOperationDaoV07 = userOperationDaoV07; this.userOperationStateDao = userOperationStateDao; this.cacheService = cacheService; } @@ -97,104 +107,55 @@ export class EVMTransactionListener previousTransactionHash, } = notifyTransactionListenerParams; - // if no transactionExecutionResponse then it means transactions was not published onc hain - // update transaction and user op collection - if (!transactionHash) { - log.error(`transactionExecutionResponse is null for transactionId: ${transactionId} for bundler: ${relayerAddress} hence - updating transaction and userOp data`); - try { - if (transactionType === TransactionType.BUNDLER) { - await this.transactionDao.updateByTransactionIdAndTransactionHash( - this.chainId, - transactionId, - "null", - { - resubmitted: true, - status: TransactionStatus.DROPPED, - updationTime: Date.now(), - }, - ); - this.updateUserOpDataForFailureInTransactionExecution(transactionId); - } - } catch (dataSavingError) { - log.error( - `Error in updating transaction and userOp data for transactionId: ${transactionId} with error: ${parseError( - dataSavingError, - )}`, - ); - } + const _log = log.child({ + transactionId, + transactionHash, + relayerAddress, + transactionType, + walletAddress, + relayerManagerName, + previousTransactionHash, + }); - log.error( - `transactionExecutionResponse is null for transactionId: ${transactionId} for bundler: ${relayerAddress}`, + // if no transaction hash, means transaction was not published on chain e.g it was dropped + if (!transactionHash) { + await this.handleTransactionDropped( + _log, + transactionId, + relayerAddress, + transactionType, ); + return false; } + // if no previous transaction hash, means transaction is still pending if (!previousTransactionHash) { - log.info( - `Not a replacement transaction, updating data for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - - await this.transactionDao.updateByTransactionId( - this.chainId, + await this.handleTransactionPending( + _log, transactionId, - { - transactionHash, - rawTransaction: convertBigIntToString(rawTransaction), - relayerAddress, - gasPrice: rawTransaction.gasPrice?.toString(), - status: TransactionStatus.PENDING, - updationTime: Date.now(), - }, - ); - log.info( - `Data updated for transactionId: ${transactionId} on chainId: ${this.chainId}`, + transactionHash, + rawTransaction, + relayerAddress, ); } else { - log.info( - `A replacement transaction, updating data for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - await this.transactionDao.updateByTransactionIdAndTransactionHash( - this.chainId, + // otherwise it means it was replaced by a transaction with a higher fee + await this.handleReplacementTransaction( + _log, transactionId, transactionHash, - { - resubmitted: true, - status: TransactionStatus.DROPPED, - updationTime: Date.now(), - }, - ); - log.info( - `Replacement transaction data updated for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - log.info( - `A replacement transaction encountered, saving new transaction data for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - await this.transactionDao.save(this.chainId, { - transactionId, transactionType, - transactionHash, previousTransactionHash, - status: TransactionStatus.PENDING, - rawTransaction: convertBigIntToString(rawTransaction), - chainId: this.chainId, - gasPrice: rawTransaction.gasPrice?.toString(), + rawTransaction, relayerAddress, walletAddress, metaData, - resubmitted: false, - creationTime: Date.now(), - updationTime: Date.now(), - }); - log.info( - `Saved new data for the replaced transaction with transactionId: ${transactionId} on chainId: ${this.chainId} with new transactionHash: ${transactionHash}`, ); } // retry txn service will check for receipt - log.info( - `Publishing transaction data of transactionId: ${transactionId} to retry transaction queue on chainId ${this.chainId}`, - ); + _log.info(`Publishing transaction data to retry transaction queue`); + try { await this.publishToRetryTransactionQueue({ relayerAddress, @@ -207,12 +168,9 @@ export class EVMTransactionListener relayerManagerName, }); } catch (publishToRetryTransactionQueueError) { - log.error( - `publishToRetryTransactionQueueError: ${parseError( - publishToRetryTransactionQueueError, - )} while publishing to retry transaction queue for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, + _log.error( + { publishToRetryTransactionQueueError }, + `Error while publishing to retry transaction queue`, ); } @@ -222,12 +180,9 @@ export class EVMTransactionListener } catch (waitForTransactionError) { // timeout error // do nothing for now just log - log.error( - `Error in waitForTransaction: ${parseError( - waitForTransactionError, - )} on hash: ${transactionHash} for transactionId: ${transactionId} on chainId ${ - this.chainId - }`, + _log.error( + { waitForTransactionError }, + `Error while waiting for transaction`, ); return false; } @@ -235,6 +190,128 @@ export class EVMTransactionListener return true; } + private async handleReplacementTransaction( + _log: pino.Logger, + transactionId: string, + transactionHash: string, + transactionType: TransactionType, + previousTransactionHash: string | undefined, + rawTransaction: EVMRawTransactionType, + relayerAddress: string, + walletAddress: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + metaData: any, + ) { + _log.info( + `A replacement transaction encountered, updating previous transaction to ${TransactionStatus.DROPPED}`, + ); + + await this.transactionDao.updateByTransactionIdAndTransactionHash( + this.chainId, + transactionId, + transactionHash, + { + resubmitted: true, + status: TransactionStatus.DROPPED, + updationTime: Date.now(), + }, + ); + + _log.info(`Previous transaction updated to ${TransactionStatus.DROPPED}`); + + _log.info(`Saving new (replacement) transaction data`); + + await this.transactionDao.save(this.chainId, { + transactionId, + transactionType, + transactionHash, + previousTransactionHash, + status: TransactionStatus.PENDING, + rawTransaction: convertBigIntToString(rawTransaction), + chainId: this.chainId, + gasPrice: rawTransaction.gasPrice?.toString(), + relayerAddress, + walletAddress, + metaData, + resubmitted: false, + creationTime: Date.now(), + updationTime: Date.now(), + }); + + _log.info( + `Saved new replacement transaction new transactionHash: ${transactionHash}`, + ); + } + + private async handleTransactionPending( + _log: pino.Logger, + transactionId: string, + transactionHash: string | undefined, + rawTransaction: EVMRawTransactionType, + relayerAddress: string, + ) { + _log.info( + `Not a replacement transaction, updating transaction data to ${TransactionStatus.PENDING}`, + ); + + await this.transactionDao.updateByTransactionId( + this.chainId, + transactionId, + { + transactionHash, + rawTransaction: convertBigIntToString(rawTransaction), + relayerAddress, + gasPrice: rawTransaction.gasPrice?.toString(), + status: TransactionStatus.PENDING, + updationTime: Date.now(), + }, + ); + + _log.info(`Transaction data updated`); + } + + private async handleTransactionDropped( + _log: pino.Logger, + transactionId: string, + relayerAddress: string, + transactionType: TransactionType, + ) { + _log.error( + { transactionId, relayerAddress }, + `transactionExecutionResponse is null hence updating transaction and userOp data`, + ); + + try { + if (transactionType === TransactionType.BUNDLER) { + _log.warn( + `Updating Transaction status to ${TransactionStatus.DROPPED}`, + ); + + await this.transactionDao.updateByTransactionIdAndTransactionHash( + this.chainId, + transactionId, + "null", + { + resubmitted: true, + status: TransactionStatus.DROPPED, + updationTime: Date.now(), + }, + ); + + await this.updateUserOpDataForFailureInTransactionExecution( + transactionId, + ); + + _log.info(`transactionExecutionResponse is null`); + } + } catch (dataSavingError) { + _log.error( + { dataSavingError }, + `Error in updating transaction and userOp data`, + ); + } + } + async publishToRetryTransactionQueue( data: RetryTransactionQueueData, ): Promise { @@ -242,6 +319,7 @@ export class EVMTransactionListener return true; } + // TODO: Refactor this private async onTransactionSuccess( onTransactionSuccessParams: OnTransactionSuccessParamsType, ) { @@ -251,45 +329,132 @@ export class EVMTransactionListener transactionId, transactionType, } = onTransactionSuccessParams; + + const _log = log.child({ + transactionId, + transactionHash, + }); + if (!transactionReceipt) { - log.error( - `Transaction receipt not found for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); + _log.error(`Transaction receipt not found `); return; } + let tryForV07 = false; + if (transactionHash) { try { - log.info( - `transactionType: ${transactionType} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); if (transactionType === TransactionType.BUNDLER) { - log.info( - `Getting userOps for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.info(`Getting userOps for transactionId: ${transactionId}`); + const userOps = await this.userOperationDao.getUserOpsByTransactionId( this.chainId, transactionId, ); - log.info( - `userOps: ${customJSONStringify( - userOps, - )} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + if (!userOps.length) { log.info( `No user op found for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); + tryForV07 = true; + } else { + for (let i = 0; i < userOps.length; i += 1) { + const { userOpHash, entryPoint } = userOps[i]; + + const entryPointContracts = this.entryPointMap[this.chainId]; + + const entryPointContract = entryPointContracts.find( + (contract) => + contract.address.toLowerCase() === entryPoint.toLowerCase(), + )?.entryPointContract; + + if (entryPointContract) { + const userOpReceipt = + await getUserOperationReceiptForSuccessfulTransaction( + this.chainId, + userOpHash, + transactionReceipt, + entryPointContract, + ); + + if (!userOpReceipt) { + _log.info( + `userOpReceipt not found for userOpHash: ${userOpHash}`, + ); + return; + } + + const { success, actualGasCost, actualGasUsed, reason, logs } = + userOpReceipt; + + const newUserOpData = convertBigIntToString({ + transactionHash, + receipt: convertBigIntToString(transactionReceipt), + blockNumber: Number(transactionReceipt.blockNumber), + blockHash: transactionReceipt.blockHash, + status: TransactionStatus.SUCCESS, + success: success.toString(), + actualGasCost, + actualGasUsed, + reason, + logs: convertBigIntToString(logs), + }); + + _log.info({ newUserOpData }, `Updating userOp data`); + + await this.userOperationDao.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + this.chainId, + transactionId, + userOpHash, + newUserOpData, + ); + + _log.info(`userOp data updated`); + + if (transactionType === TransactionType.BUNDLER) { + _log.info( + `Updating UserOperation state to: ${UserOperationStateEnum.CONFIRMED}`, + ); + + await this.userOperationStateDao.updateState(this.chainId, { + transactionId, + message: "Transaction confirmed", + state: UserOperationStateEnum.CONFIRMED, + }); + + log.info( + `Updated UserOperation state to: ${UserOperationStateEnum.CONFIRMED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + } + } else { + _log.error( + `entryPoint: ${entryPoint} not found in entry point map`, + ); + } + } + } + } + + if (transactionType === TransactionType.BUNDLER && tryForV07) { + _log.info(`V0.7 Getting userOps for transactionId: ${transactionId}`); + + const userOps = + await this.userOperationDaoV07.getUserOpsByTransactionId( + this.chainId, + transactionId, + ); + + if (!userOps.length) { + _log.info( + `V0.7 No user op found for transactionId: ${transactionId}`, + ); return; } - for ( - let userOpIndex = 0; - userOpIndex < userOps.length; - userOpIndex += 1 - ) { - const { userOpHash, entryPoint } = userOps[userOpIndex]; - const entryPointContracts = this.entryPointMap[this.chainId]; + for (let i = 0; i < userOps.length; i += 1) { + const { userOpHash, entryPoint } = userOps[i]; + + const entryPointContracts = this.entryPointV07Map[this.chainId]; const entryPointContract = entryPointContracts.find( (contract) => @@ -304,72 +469,394 @@ export class EVMTransactionListener transactionReceipt, entryPointContract, ); + if (!userOpReceipt) { + _log.info(`V0.7 userOpReceipt not found`); + return; + } + + const { success, actualGasCost, actualGasUsed, reason, logs } = + userOpReceipt; + + const newData = convertBigIntToString({ + transactionHash, + receipt: convertBigIntToString(transactionReceipt), + blockNumber: Number(transactionReceipt.blockNumber), + blockHash: transactionReceipt.blockHash, + status: TransactionStatus.SUCCESS, + success: success.toString(), + actualGasCost, + actualGasUsed, + reason, + logs: convertBigIntToString(logs), + }); + + _log.info({ newData }, `Updating userOp data`); + + await this.userOperationDaoV07.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + this.chainId, + transactionId, + userOpHash, + newData, + ); + + _log.info( + `v0.7 userOp data updated for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + + if (transactionType === TransactionType.BUNDLER) { + _log.info( + `v0.7 updating state to: ${UserOperationStateEnum.CONFIRMED}`, + ); + + await this.userOperationStateDao.updateState(this.chainId, { + transactionId, + message: "Transaction confirmed", + state: UserOperationStateEnum.CONFIRMED, + }); + + _log.info( + `v0.7 updated state to: ${UserOperationStateEnum.CONFIRMED}`, + ); + } + } else { + _log.info( + `v0.7 entryPoint: ${entryPoint} not found in entry point map`, + ); + } + } + } + } catch (error) { + _log.error({ error }, `Error while saving userOp data to database`); + } + + // Save transaction data to DB + try { + _log.info(`Saving transaction data to database`); + + let transactionFee = 0; + let transactionFeeInUSD = 0; + let transactionFeeCurrency = ""; + if ( + !transactionReceipt.gasUsed && + !transactionReceipt.effectiveGasPrice + ) { + _log.warn( + `gasUsed or effectiveGasPrice field not found in transaction receipt`, + ); + } else { + transactionFee = Number( + transactionReceipt.gasUsed * transactionReceipt.effectiveGasPrice, + ); + transactionFeeCurrency = config.chains.currency[this.chainId]; + const coinsRateObj = await this.cacheService.get(getTokenPriceKey()); + if (!coinsRateObj) { + _log.info("Coins Rate Obj not fetched from cache"); + } else { + transactionFeeInUSD = JSON.parse(coinsRateObj)[this.chainId] || 0; + } + } + + await this.transactionDao.updateByTransactionIdAndTransactionHash( + this.chainId, + transactionId, + transactionHash, + { + receipt: convertBigIntToString(transactionReceipt), + transactionFee, + transactionFeeInUSD, + transactionFeeCurrency, + status: TransactionStatus.SUCCESS, + updationTime: Date.now(), + }, + ); + } catch (err) { + _log.error({ err }, `Error while saving transaction data to database`); + } + } else { + _log.error(`No transactionExecutionResponse found`); + } + } + + private async onTransactionFailure( + onTransactionFailureParams: OnTransactionFailureParamsType, + ) { + const { + transactionHash, + transactionId, + transactionReceipt, + transactionType, + } = onTransactionFailureParams; + + const _log = log.child({ + transactionId, + transactionHash, + }); + + if (!transactionReceipt) { + _log.error( + `Transaction receipt not found for transactionId: ${transactionId} on chainId ${this.chainId}`, + ); + return; + } + + let tryForV07 = false; + if (transactionHash) { + try { + if (transactionType === TransactionType.BUNDLER) { + _log.info( + `Getting userOps for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + + const userOps = await this.userOperationDao.getUserOpsByTransactionId( + this.chainId, + transactionId, + ); + + if (!userOps.length) { + _log.info(`No user ops found`); + tryForV07 = true; + } + for (let i = 0; i < userOps.length; i += 1) { + const { userOpHash, entryPoint } = userOps[i]; + + const entryPointContracts = this.entryPointMap[this.chainId]; + + const entryPointContract = entryPointContracts.find( + (contract) => + contract.address.toLowerCase() === entryPoint.toLowerCase(), + )?.entryPointContract; + + if (entryPointContract) { + const latestBlock = + await this.networkService.getLatestBlockNumber(); + + _log.info({ latestBlock }, `Fetched latest block`); + + let fromBlock = latestBlock - BigInt(1000); + if (config.astarNetworks.includes(this.chainId)) { + fromBlock += BigInt(501); + } + log.info( - `userOpReceipt: ${customJSONStringify( - userOpReceipt, - )} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, + `fromBlock: ${fromBlock} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); + + const userOpReceipt = + await getUserOperationReceiptForFailedTransaction( + this.chainId, + userOpHash, + transactionReceipt, + entryPointContract, + fromBlock, + this.networkService.provider, + ); + if (!userOpReceipt) { log.info( `userOpReceipt not fetched for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); + log.info( + `Updating userOp data: ${customJSONStringify( + convertBigIntToString({ + transactionHash, + receipt: convertBigIntToString(transactionReceipt), + blockNumber: Number(transactionReceipt.blockNumber), + blockHash: transactionReceipt.blockHash, + status: TransactionStatus.FAILED, + success: "false", + actualGasCost: 0, + actualGasUsed: 0, + reason: null, + logs: null, + }), + )} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${ + this.chainId + }`, + ); + + await this.userOperationDao.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + this.chainId, + transactionId, + userOpHash, + convertBigIntToString({ + transactionHash, + receipt: convertBigIntToString(transactionReceipt), + blockNumber: Number(transactionReceipt.blockNumber), + blockHash: transactionReceipt.blockHash, + status: TransactionStatus.FAILED, + success: "false", + actualGasCost: 0, + actualGasUsed: 0, + reason: "null", + logs: null, + }), + ); + log.info( + `userOp data updated for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + + if (transactionType === TransactionType.BUNDLER) { + log.info( + `updating state to: ${UserOperationStateEnum.FAILED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + await this.userOperationStateDao.updateState(this.chainId, { + transactionId, + state: UserOperationStateEnum.FAILED, + message: await this.getTransactionFailureMessage( + transactionReceipt, + entryPointContract, + ), + }); + log.info( + `updated state to: ${UserOperationStateEnum.FAILED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + } return; } - const { success, actualGasCost, actualGasUsed, reason, logs } = - userOpReceipt; + const { + success, + actualGasCost, + actualGasUsed, + reason, + logs, + frontRunnedTransactionReceipt, + } = userOpReceipt; + + log.info( + `Updating transaction data for a front runned transaction for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + log.info( + `Updating userOp data: ${customJSONStringify( + convertBigIntToString({ + receipt: convertBigIntToString( + frontRunnedTransactionReceipt, + ), + transactionHash: ( + frontRunnedTransactionReceipt as TransactionReceipt + ).transactionHash, + blockNumber: Number( + (frontRunnedTransactionReceipt as TransactionReceipt) + .blockNumber, + ), + blockHash: ( + frontRunnedTransactionReceipt as TransactionReceipt + ).blockHash, + status: TransactionStatus.SUCCESS, + success, + actualGasCost, + actualGasUsed, + reason, + logs: convertBigIntToString(logs), + }), + )} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${ + this.chainId + }`, + ); + + await this.userOperationDao.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + this.chainId, + transactionId, + userOpHash, + convertBigIntToString({ + receipt: convertBigIntToString(frontRunnedTransactionReceipt), + transactionHash: ( + frontRunnedTransactionReceipt as TransactionReceipt + ).transactionHash, + blockNumber: Number( + (frontRunnedTransactionReceipt as TransactionReceipt) + .blockNumber, + ), + blockHash: ( + frontRunnedTransactionReceipt as TransactionReceipt + ).blockHash, + status: TransactionStatus.SUCCESS, + success, + actualGasCost, + actualGasUsed, + reason, + logs: convertBigIntToString(logs), + }), + ); + + log.info( + `userOp data updated for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + ); + let frontRunnedTransactionFee = 0; + let frontRunnedTransactionFeeInUSD = 0; + let frontRunnedTransactionFeeCurrency = ""; + if ( + !frontRunnedTransactionReceipt.gasUsed && + !frontRunnedTransactionReceipt.effectiveGasPrice + ) { + log.info( + `gasUsed or effectiveGasPrice field not found in ${customJSONStringify( + frontRunnedTransactionReceipt, + )}`, + ); + } else { + frontRunnedTransactionFee = 0; + try { + frontRunnedTransactionFee = Number( + frontRunnedTransactionReceipt.gasUsed * + frontRunnedTransactionReceipt.effectiveGasPrice, + ); + } catch (err) { + log.error( + `Error in calculating front ran transaction fee, defaulting to ${frontRunnedTransactionFee}`, + { + err, + transactionId, + chainId: this.chainId, + gasUsed: frontRunnedTransactionReceipt.gasUsed, + effectiveGasPrice: + frontRunnedTransactionReceipt.effectiveGasPrice, + }, + ); + } + + frontRunnedTransactionFeeCurrency = + config.chains.currency[this.chainId]; + const coinsRateObj = + await this.cacheService.get(getTokenPriceKey()); + if (!coinsRateObj) { + log.info("Coins Rate Obj not fetched from cache"); + } else { + frontRunnedTransactionFeeInUSD = + JSON.parse(coinsRateObj)[this.chainId]; + } + } - log.info( - `Updating userOp data: ${customJSONStringify( - convertBigIntToString({ - transactionHash, - receipt: convertBigIntToString(transactionReceipt), - blockNumber: Number(transactionReceipt.blockNumber), - blockHash: transactionReceipt.blockHash, - status: TransactionStatus.SUCCESS, - success: success.toString(), - actualGasCost, - actualGasUsed, - reason, - logs: convertBigIntToString(logs), - }), - )} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); - await this.userOperationDao.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + await this.transactionDao.updateByTransactionIdAndTransactionHashForFrontRunnedTransaction( this.chainId, transactionId, - userOpHash, - convertBigIntToString({ - transactionHash, - receipt: convertBigIntToString(transactionReceipt), - blockNumber: Number(transactionReceipt.blockNumber), - blockHash: transactionReceipt.blockHash, - status: TransactionStatus.SUCCESS, - success: success.toString(), - actualGasCost, - actualGasUsed, - reason, - logs: convertBigIntToString(logs), - }), - ); - log.info( - `userOp data updated for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + transactionHash, + { + frontRunnedTransactionHash: + frontRunnedTransactionReceipt.hash, + frontRunnedReceipt: convertBigIntToString( + frontRunnedTransactionReceipt, + ), + frontRunnedTransactionFee, + frontRunnedTransactionFeeInUSD, + frontRunnedTransactionFeeCurrency, + status: TransactionStatus.FAILED, + updationTime: Date.now(), + }, ); if (transactionType === TransactionType.BUNDLER) { log.info( - `updating state to: ${UserOperationStateEnum.CONFIRMED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `updating state to: ${UserOperationStateEnum.CONFIRMED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId} for a front runned transaction`, ); await this.userOperationStateDao.updateState(this.chainId, { transactionId, - message: "Transaction confirmed", + transactionHash: frontRunnedTransactionReceipt.hash, + message: + "Transaction was front runned, check new transaction hash in receipt", state: UserOperationStateEnum.CONFIRMED, }); log.info( - `updated state to: ${UserOperationStateEnum.CONFIRMED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `updated state state to: ${UserOperationStateEnum.CONFIRMED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId} for a front runned transaction`, ); } } else { @@ -379,107 +866,19 @@ export class EVMTransactionListener } } } - } catch (error) { - log.error( - `Error in saving userOp data in database for transactionId: ${transactionId} on chainId ${ - this.chainId - } with error: ${parseError(error)}`, - ); - } - - try { - log.info( - `Saving transaction data in database for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); - let transactionFee = 0; - let transactionFeeInUSD = 0; - let transactionFeeCurrency = ""; - if ( - !transactionReceipt.gasUsed && - !transactionReceipt.effectiveGasPrice - ) { - log.info( - `gasUsed or effectiveGasPrice field not found in ${customJSONStringify( - transactionReceipt, - )}`, - ); - } else { - transactionFee = Number( - transactionReceipt.gasUsed * transactionReceipt.effectiveGasPrice, - ); - transactionFeeCurrency = config.chains.currency[this.chainId]; - const coinsRateObj = await this.cacheService.get(getTokenPriceKey()); - if (!coinsRateObj) { - log.info("Coins Rate Obj not fetched from cache"); - } else { - transactionFeeInUSD = JSON.parse(coinsRateObj)[this.chainId] || 0; - } - } - await this.transactionDao.updateByTransactionIdAndTransactionHash( - this.chainId, - transactionId, - transactionHash, - { - receipt: convertBigIntToString(transactionReceipt), - transactionFee, - transactionFeeInUSD, - transactionFeeCurrency, - status: TransactionStatus.SUCCESS, - updationTime: Date.now(), - }, - ); - } catch (error) { - log.error( - `Error in saving transaction data in database for transactionId: ${transactionId} on chainId ${ - this.chainId - } with error: ${parseError(error)}`, - ); - } - } else { - log.error( - `No transactionExecutionResponse found for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); - } - } - - private async onTransactionFailure( - onTransactionFailureParams: OnTransactionFailureParamsType, - ) { - const { - transactionHash, - transactionId, - transactionReceipt, - transactionType, - } = onTransactionFailureParams; - if (!transactionReceipt) { - log.error( - `Transaction receipt not found for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); - return; - } - - if (transactionHash) { - try { - log.info( - `transactionType: ${transactionType} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - if (transactionType === TransactionType.BUNDLER) { + if (transactionType === TransactionType.BUNDLER && tryForV07) { log.info( - `Getting userOps for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `V0.7 Getting userOps for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); - const userOps = await this.userOperationDao.getUserOpsByTransactionId( - this.chainId, - transactionId, - ); - log.info( - `userOps: ${customJSONStringify( - userOps, - )} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + const userOps = + await this.userOperationDaoV07.getUserOpsByTransactionId( + this.chainId, + transactionId, + ); if (!userOps.length) { log.info( - `No user op found for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `V0.7 No user op found for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); return; } @@ -490,7 +889,7 @@ export class EVMTransactionListener ) { const { userOpHash, entryPoint } = userOps[userOpIndex]; - const entryPointContracts = this.entryPointMap[this.chainId]; + const entryPointContracts = this.entryPointV07Map[this.chainId]; const entryPointContract = entryPointContracts.find( (contract) => @@ -499,16 +898,16 @@ export class EVMTransactionListener if (entryPointContract) { const latestBlock = - await this.networkService.getLatesBlockNumber(); + await this.networkService.getLatestBlockNumber(); log.info( - `latestBlock: ${latestBlock} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `v0.7 latestBlock: ${latestBlock} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); let fromBlock = latestBlock - BigInt(1000); if (config.astarNetworks.includes(this.chainId)) { fromBlock += BigInt(501); } log.info( - `fromBlock: ${fromBlock} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `v0.7 fromBlock: ${fromBlock} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); const userOpReceipt = await getUserOperationReceiptForFailedTransaction( @@ -519,20 +918,13 @@ export class EVMTransactionListener fromBlock, this.networkService.provider, ); - log.info( - `userOpReceipt: ${customJSONStringify( - userOpReceipt, - )} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); if (!userOpReceipt) { log.info( - `userOpReceipt not fetched for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `v0.7 userOpReceipt not fetched for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); log.info( - `Updating userOp data: ${customJSONStringify( + `v0.7 Updating userOp data: ${customJSONStringify( convertBigIntToString({ transactionHash, receipt: convertBigIntToString(transactionReceipt), @@ -550,7 +942,7 @@ export class EVMTransactionListener }`, ); - await this.userOperationDao.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + await this.userOperationDaoV07.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( this.chainId, transactionId, userOpHash, @@ -568,12 +960,12 @@ export class EVMTransactionListener }), ); log.info( - `userOp data updated for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `v0.7 userOp data updated for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); if (transactionType === TransactionType.BUNDLER) { log.info( - `updating state to: ${UserOperationStateEnum.FAILED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `v0.7 updating state to: ${UserOperationStateEnum.FAILED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); await this.userOperationStateDao.updateState(this.chainId, { transactionId, @@ -584,7 +976,7 @@ export class EVMTransactionListener ), }); log.info( - `updated state to: ${UserOperationStateEnum.FAILED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + `v0.7 updated state to: ${UserOperationStateEnum.FAILED} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); } return; @@ -601,35 +993,8 @@ export class EVMTransactionListener log.info( `Updating transaction data for a front runned transaction for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); - log.info( - `Updating userOp data: ${customJSONStringify( - convertBigIntToString({ - receipt: convertBigIntToString( - frontRunnedTransactionReceipt, - ), - transactionHash: ( - frontRunnedTransactionReceipt as TransactionReceipt - ).transactionHash, - blockNumber: Number( - (frontRunnedTransactionReceipt as TransactionReceipt) - .blockNumber, - ), - blockHash: ( - frontRunnedTransactionReceipt as TransactionReceipt - ).blockHash, - status: TransactionStatus.SUCCESS, - success, - actualGasCost, - actualGasUsed, - reason, - logs: convertBigIntToString(logs), - }), - )} for userOpHash: ${userOpHash} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); - await this.userOperationDao.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( + await this.userOperationDaoV07.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( this.chainId, transactionId, userOpHash, @@ -680,6 +1045,7 @@ export class EVMTransactionListener log.error( `Error in calculating front ran transaction fee, defaulting to ${frontRunnedTransactionFee}`, { + err, transactionId, chainId: this.chainId, gasUsed: frontRunnedTransactionReceipt.gasUsed, @@ -807,24 +1173,26 @@ export class EVMTransactionListener private async updateUserOpDataForFailureInTransactionExecution( transactionId: string, ): Promise { + const _log = log.child({ + transactionId, + chainId: this.chainId, + }); + const userOps = await this.userOperationDao.getUserOpsByTransactionId( this.chainId, transactionId, ); - log.info( - `userOps: ${customJSONStringify( - userOps, - )} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - if (!userOps.length) { - log.info( - `No user op found for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + + if (userOps.length === 0) { + _log.info(`No user op found for transactionId: ${transactionId}`); return; } - for (let userOpIndex = 0; userOpIndex < userOps.length; userOpIndex += 1) { - const { userOpHash } = userOps[userOpIndex]; + _log.info({ numUserOps: userOps.length }, "Got user ops by transaction ID"); + + for (let i = 0; i < userOps.length; i++) { + const { userOpHash } = userOps[i]; + await this.userOperationDao.updateUserOpDataToDatabaseByTransactionIdAndUserOpHash( this.chainId, transactionId, @@ -842,6 +1210,11 @@ export class EVMTransactionListener logs: null, }, ); + + _log.info( + { userOpHash }, + `Updated user op status to ${TransactionStatus.DROPPED}`, + ); } } @@ -867,6 +1240,12 @@ export class EVMTransactionListener ); try { + if (this.networkService.mevProtectedRpcUrl) { + return this.waitForFlashbotsTransaction( + notifyTransactionListenerParams, + ); + } + const transactionReceipt = await this.networkService.waitForTransaction( transactionHash, transactionId, @@ -939,9 +1318,94 @@ export class EVMTransactionListener } } + async waitForFlashbotsTransaction( + notifyTransactionListenerParams: NotifyTransactionListenerParamsType, + ) { + if ( + !this.networkService.supportsFlashbots || + !this.networkService.flashbots + ) { + throw new Error( + `Flashbots service not available for chainId: ${this.networkService.chainId}`, + ); + } + + const { transactionHash, transactionId, relayerAddress } = + notifyTransactionListenerParams; + + const _log = log.child({ + chainId: this.chainId, + transactionHash, + transactionId, + relayerAddress, + }); + + if (!transactionHash) { + return; + } + + try { + _log.info(`Start waiting for flashbots transaction`); + + const response = + await this.networkService.flashbots.waitForTransaction(transactionHash); + + _log.info( + { transactionHash, transactionId, chainId: this.chainId }, + `Flashbots response: ${customJSONStringify(response)}`, + ); + + await this.cacheService.delete( + getRetryTransactionCountKey(transactionId, this.chainId), + ); + + await this.cacheService.set(getTransactionMinedKey(transactionId), "1"); + + if (response.status === FlashbotsTxStatus.INCLUDED) { + _log.info( + { chainId: this.chainId, transactionHash, transactionId }, + `Transaction included`, + ); + + // get the recept and call onTransactionSuccess + const transactionReceipt = + await this.networkService.getTransactionReceipt(transactionHash); + + if (transactionReceipt) { + notifyTransactionListenerParams.transactionReceipt = + transactionReceipt; + } + + await this.onTransactionSuccess(notifyTransactionListenerParams); + } else { + _log.info( + { chainId: this.chainId, transactionHash, transactionId }, + `Transaction not included`, + ); + + const transactionReceipt = + await this.networkService.getTransactionReceipt(transactionHash); + + if (transactionReceipt) { + notifyTransactionListenerParams.transactionReceipt = + transactionReceipt; + } + + await this.onTransactionFailure(notifyTransactionListenerParams); + } + } catch (error) { + _log.error( + { + err: error, + }, + `Error in waitForFlashbotsTransaction`, + ); + } + } + async getTransactionFailureMessage( receipt: TransactionReceipt, - entryPointContract: EntryPointContractType, + entryPointContract: EntryPointContractType | EntryPointV07ContractType, ): Promise { try { const getTransactionResponse = await this.networkService.getTransaction( @@ -984,6 +1448,7 @@ export class EVMTransactionListener } return "Unable to parse transaction failure reason, please check transaction hash on explorer"; } catch (error) { + log.error(error); return "Unable to parse transaction failure reason, please check transaction hash on explorer"; } } diff --git a/src/relayer/transaction-listener/types/index.ts b/src/relayer/transaction-listener/types/index.ts index 3498bb57..8d762298 100644 --- a/src/relayer/transaction-listener/types/index.ts +++ b/src/relayer/transaction-listener/types/index.ts @@ -4,12 +4,14 @@ import { ITransactionDAO, IUserOperationDAO, IUserOperationStateDAO, + IUserOperationV07DAO, } from "../../../common/db"; import { IQueue } from "../../../common/interface"; import { INetworkService } from "../../../common/network"; import { RetryTransactionQueueData } from "../../../common/queue/types"; import { EntryPointMapType, + EntryPointV07MapType, EVMRawTransactionType, TransactionType, } from "../../../common/types"; @@ -20,11 +22,13 @@ export type EVMTransactionListenerParamsType = { retryTransactionQueue: IQueue; transactionDao: ITransactionDAO; userOperationDao: IUserOperationDAO; + userOperationDaoV07: IUserOperationV07DAO; userOperationStateDao: IUserOperationStateDAO; cacheService: ICacheService; options: { chainId: number; entryPointMap: EntryPointMapType; + entryPointMapV07: EntryPointV07MapType; }; }; @@ -37,6 +41,7 @@ export type NotifyTransactionListenerParamsType = { previousTransactionHash?: string; rawTransaction: EVMRawTransactionType; walletAddress: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any metaData?: any; relayerManagerName: string; error?: string; diff --git a/src/relayer/transaction-service/EVMTransactionService.ts b/src/relayer/transaction-service/EVMTransactionService.ts index ad9681fa..9c4bfbc1 100644 --- a/src/relayer/transaction-service/EVMTransactionService.ts +++ b/src/relayer/transaction-service/EVMTransactionService.ts @@ -1,17 +1,10 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable no-else-return */ import { Mutex } from "async-mutex"; -import { toHex } from "viem"; +import { Hex, TransactionReceipt, decodeFunctionData, toHex } from "viem"; +import { estimateGas } from "viem/linea"; import { ICacheService } from "../../common/cache"; import { IGasPriceService } from "../../common/gas-price"; import { logger } from "../../common/logger"; import { INetworkService } from "../../common/network"; -import { - getMaxRetryCountNotificationMessage, - getTransactionErrorNotificationMessage, - getRelayerFundingNotificationMessage, -} from "../../common/notification"; -import { INotificationManager } from "../../common/notification/interface"; import { EVMRawTransactionType, NetworkBasedGasPriceType, @@ -25,7 +18,7 @@ import { customJSONStringify, } from "../../common/utils"; import { config } from "../../config"; -import { STATUSES } from "../../server/api/shared/middleware"; +import { STATUSES } from "../../server/api/shared/statuses"; import { IEVMAccount } from "../account"; import { INonceManager } from "../nonce-manager"; import { ITransactionListener } from "../transaction-listener"; @@ -42,6 +35,9 @@ import { TransactionDataType, } from "./types"; import { IUserOperationStateDAO } from "../../common/db"; +import { GasPriceType } from "../../common/gas-price/types"; +import { BLOCKCHAINS } from "../../common/constants"; +import { ENTRYPOINT_V6_ABI } from "@biconomy/gas-estimations"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -50,7 +46,7 @@ const log = logger.child({ export class EVMTransactionService implements ITransactionService { - chainId: number; + readonly chainId: number; networkService: INetworkService; @@ -62,8 +58,6 @@ export class EVMTransactionService cacheService: ICacheService; - notificationManager: INotificationManager; - userOperationStateDao: IUserOperationStateDAO; addressMutex: { @@ -78,7 +72,6 @@ export class EVMTransactionService nonceManager, gasPriceService, cacheService, - notificationManager, userOperationStateDao, } = evmTransactionServiceParams; this.chainId = options.chainId; @@ -87,7 +80,6 @@ export class EVMTransactionService this.nonceManager = nonceManager; this.gasPriceService = gasPriceService; this.cacheService = cacheService; - this.notificationManager = notificationManager; this.userOperationStateDao = userOperationStateDao; } @@ -126,23 +118,20 @@ export class EVMTransactionService const maxRetryCount = 5; + const _log = log.child({ + transactionId, + relayerAddress, + chainId: this.chainId, + maxRetryCount, + retryTransactionCount, + }); + if (retryTransactionCount > maxRetryCount) { - try { - // send slack notification - await this.sendMaxRetryCountExceededSlackNotification( - transactionData.transactionId, - account, - transactionType, - this.chainId, - ); - } catch (error) { - log.error(`Error in sending slack notification: ${parseError(error)}`); - } return { state: "failed", code: STATUSES.NOT_FOUND, error: - "Max retry count exceeded. Use end point to get transaction status", // todo add end point + "Max retry count exceeded. Use end point to get transaction status", transactionId, ...{ isTransactionRelayed: false, @@ -151,22 +140,13 @@ export class EVMTransactionService }; } - log.info( - `Transaction request received for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); + _log.info(`Transaction request received`); const addressMutex = this.getMutex(account.getPublicKey()); - log.info( - `Taking lock on address: ${account.getPublicKey()} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); + + _log.info(`Taking lock on relayer`); const release = await addressMutex.acquire(); - log.info( - `Lock taken on address: ${account.getPublicKey()} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); + _log.info(`Lock taken on relayer`); try { // create transaction @@ -180,13 +160,13 @@ export class EVMTransactionService account, transactionId, }); - log.info( - `Raw transaction for transactionId: ${transactionId} is ${customJSONStringify( - rawTransaction, - )} on chainId ${this.chainId}`, + + _log.info( + { rawTransaction }, + `Calling EVMTransactionService.executeTransation`, ); - const transactionExecutionResponse = await this.executeTransaction( + const executeTransactionResponse = await this.executeTransaction( { rawTransaction, account, @@ -194,52 +174,39 @@ export class EVMTransactionService transactionId, ); - log.info( - `Transaction execution response for transactionId ${ - transactionData.transactionId - }: ${customJSONStringify(transactionExecutionResponse)} on chainId ${ - this.chainId - }`, + _log.info( + { + executeTransactionResponse, + }, + `EVMTransactionService.executeTransation responded`, ); if (transactionType === TransactionType.BUNDLER) { - log.info( - `Setting: ${UserOperationStateEnum.SUBMITTED} for transactionId: ${transactionId} on chainId ${this.chainId}`, + _log.info( + `Updating UserOperation state to: ${UserOperationStateEnum.SUBMITTED}`, ); + this.userOperationStateDao.updateState(this.chainId, { transactionId, - transactionHash: transactionExecutionResponse.hash, + transactionHash: executeTransactionResponse.hash, state: UserOperationStateEnum.SUBMITTED, }); } - log.info( - `Incrementing nonce for account: ${relayerAddress} for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); + _log.info(`Incrementing relayer nonce`); await this.nonceManager.incrementNonce(relayerAddress); - log.info( - `Incremented nonce for account: ${relayerAddress} for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); + _log.info(`Incremented relayer nonce`); // release lock once transaction is sent and nonce is incremented - log.info( - `Releasing lock on address: ${account.getPublicKey()} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); + _log.info(`Releasing lock on relayer`); release(); - log.info( - `Lock released on address: ${account.getPublicKey()} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); + _log.info(`Lock released on relayer`); + + _log.info(`Calling transactionListener.--y`); - log.info( - `Notifying transaction listener for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); const transactionListenerNotifyResponse = await this.transactionListener.notify({ - transactionHash: transactionExecutionResponse.hash, + transactionHash: executeTransactionResponse.hash, transactionId: transactionId as string, relayerAddress, transactionType, @@ -251,11 +218,7 @@ export class EVMTransactionService }); if (transactionType === TransactionType.FUNDING) { - await this.sendRelayerFundingSlackNotification( - relayerAddress, - this.chainId, - transactionExecutionResponse.hash, - ); + _log.info(`Funding relayer`); } if (transactionListenerNotifyResponse) { @@ -272,18 +235,12 @@ export class EVMTransactionService transactionId, }; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - log.info( - `Releasing lock on address: ${account.getPublicKey()} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); + _log.info(`Releasing lock on relayer`); release(); - log.info( - `Lock released on address: ${account.getPublicKey()} for transactionId: ${transactionId} on chainId: ${ - this.chainId - }`, - ); + _log.info(`Lock released on relayer`); + if ( error.message && error.message === "Bundler balance too low. Send bundler for funding" @@ -299,23 +256,18 @@ export class EVMTransactionService }, }; } + if (transactionType === TransactionType.BUNDLER) { - log.info( - `Setting: ${UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL} for transactionId: ${transactionId} on chainId ${this.chainId}`, + _log.info( + `Updating UserOperation state to ${UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL}`, ); this.userOperationStateDao.updateState(this.chainId, { transactionId, state: UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL, }); } - log.info( - `Error while sending transaction: ${error} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - await this.sendTransactionFailedSlackNotification( - transactionId, - this.chainId, - parseError(error), - ); + + _log.info({ err: error }, `Error while sending transaction`); return { state: "failed", code: STATUSES.INTERNAL_SERVER_ERROR, @@ -334,6 +286,9 @@ export class EVMTransactionService account: IEVMAccount, transactionType: TransactionType, ): Promise { + // TODO: Make it general and EIP 1559 specific and get bump up from config + const bumpingPercentage = 50; + const { transactionHash, transactionId, @@ -342,10 +297,21 @@ export class EVMTransactionService metaData, relayerManagerName, } = retryTransactionData; + + const _log = log.child({ + transactionId, + transactionHash, + chainId: this.chainId, + walletAddress, + relayerManagerName, + bumpingPercentage, + }); + try { await this.cacheService.increment( getRetryTransactionCountKey(transactionId, this.chainId), ); + let pastGasPrice: NetworkBasedGasPriceType = rawTransaction.gasPrice as bigint; if (!pastGasPrice) { @@ -354,39 +320,23 @@ export class EVMTransactionService maxPriorityFeePerGas: rawTransaction.maxPriorityFeePerGas as bigint, }; } - // Make it general and EIP 1559 specific and get bump up from config - const bumpedUpGasPrice = this.gasPriceService.getBumpedUpGasPrice( + + const bumpedUpGasPrice = await this.gasPriceService.getBumpedUpGasPrice( pastGasPrice, 50, ); - log.info( - `Bumped up gas price for transactionId: ${transactionId} is ${bumpedUpGasPrice} on chainId ${this.chainId}`, - ); + + _log.info({ bumpedUpGasPrice }, `Bumped up gas price`); if (typeof bumpedUpGasPrice === "bigint") { rawTransaction.gasPrice = bumpedUpGasPrice as bigint; - log.info( - `Bumped up gas price for transactionId: ${transactionId} is ${bumpedUpGasPrice} on chainId ${this.chainId}`, - ); } else if (typeof bumpedUpGasPrice === "object") { rawTransaction.maxFeePerGas = bumpedUpGasPrice.maxFeePerGas; rawTransaction.maxPriorityFeePerGas = bumpedUpGasPrice.maxPriorityFeePerGas; - log.info( - `Bumped up gas price for transactionId: ${transactionId} is ${customJSONStringify( - bumpedUpGasPrice, - )} on chainId ${this.chainId}`, - ); } - log.info( - `Executing retry transaction for transactionId: ${transactionId}`, - ); - log.info( - `Raw transaction for retrying transactionId: ${transactionId} is ${customJSONStringify( - rawTransaction, - )} on chainId ${this.chainId}`, - ); + _log.info(`Retry executeTransaction`); const retryTransactionExecutionResponse = await this.executeTransaction( { @@ -395,9 +345,15 @@ export class EVMTransactionService }, transactionId, ); + + _log.info( + { retryTransactionExecutionResponse }, + `Retry executeTransaction responded`, + ); + if (transactionType === TransactionType.BUNDLER) { - log.info( - `Setting: ${UserOperationStateEnum.SUBMITTED} for transactionId: ${transactionId} for resubmitted transaction on chainId ${this.chainId}`, + _log.info( + `Updating UserOperation state to ${UserOperationStateEnum.SUBMITTED}`, ); this.userOperationStateDao.updateState(this.chainId, { transactionId, @@ -406,9 +362,8 @@ export class EVMTransactionService }); } - log.info( - `Notifying transaction listener for transactionId: ${transactionId} on chainId ${this.chainId}`, - ); + _log.info(`Notifying transaction listener`); + const transactionListenerNotifyResponse = await this.transactionListener.notify({ transactionHash: retryTransactionExecutionResponse.hash, @@ -422,13 +377,10 @@ export class EVMTransactionService relayerManagerName, }); - if (transactionType === TransactionType.FUNDING) { - await this.sendRelayerFundingSlackNotification( - account.getPublicKey(), - this.chainId, - transactionHash as string, - ); - } + _log.info( + { transactionListenerNotifyResponse }, + `Transaction listener responded`, + ); if (transactionListenerNotifyResponse) { return { @@ -445,23 +397,16 @@ export class EVMTransactionService }; } } catch (error) { - log.error( - `Error while retrying transaction: ${parseError( - error, - )} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.error({ err: error }, `Error while retrying transaction`); log.info( - `Setting: ${UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL} for transactionId: ${transactionId} for resubmitted transaction on chainId ${this.chainId}`, + `Updating UserOperation state to ${UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL}`, ); + this.userOperationStateDao.updateState(this.chainId, { transactionId, state: UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL, }); - await this.sendTransactionFailedSlackNotification( - transactionId, - this.chainId, - parseError(error), - ); + return { state: "failed", code: STATUSES.INTERNAL_SERVER_ERROR, @@ -483,10 +428,21 @@ export class EVMTransactionService createTransactionParams; const relayerAddress = account.getPublicKey(); - const nonce = await this.nonceManager.getNonce(relayerAddress); - log.info( - `Nonce for relayerAddress ${relayerAddress} is ${nonce} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + const _log = log.child({ + from, + to, + value, + data, + gasLimit, + speed, + relayerAddress, + transactionId, + chainId: this.chainId, + }); + + const nonce = await this.nonceManager.getNonce(account); + _log.info({ nonce }, `Got current nonce`); + const response = { from, to, @@ -496,13 +452,21 @@ export class EVMTransactionService chainId: this.chainId, nonce, }; + + const isLinea = [ + BLOCKCHAINS.LINEA_MAINNET, + BLOCKCHAINS.LINEA_TESTNET, + ].includes(this.chainId); + + // Create a 'special' transaction for linea, see method docstring + if (isLinea) { + return this.createLineaTransaction(from, data, to, response); + } + const gasPrice = await this.gasPriceService.getGasPrice(speed); + _log.info({ gasPrice }, `getGasPrice responded`); + if (typeof gasPrice !== "bigint") { - log.info( - `Gas price being used to send transaction by relayer: ${relayerAddress} is: ${customJSONStringify( - gasPrice, - )} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); const { maxPriorityFeePerGas, maxFeePerGas } = gasPrice; return { ...response, @@ -511,12 +475,67 @@ export class EVMTransactionService maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), }; } - log.info( - `Gas price being used to send transaction by relayer: ${relayerAddress} is: ${gasPrice} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + return { ...response, type: "legacy", gasPrice: BigInt(gasPrice) }; } + // Linea has a custom linea_estimateGas RPC endpoint that we need to call for every transaction + // and we can't rely on our cached values and standard EVM logic. See: https://docs.linea.build/developers/reference/api/linea-estimategas + private async createLineaTransaction( + from: string, + data: `0x${string}`, + to: string, + response: { + from: string; + to: `0x${string}`; + value: bigint; + gasLimit: `0x${string}`; + data: `0x${string}`; + chainId: number; + nonce: number; + }, + ) { + // Note that this estimateGas is imported from viem/linea, it's not standard + const estimateGasResponse = await estimateGas( + this.networkService.provider, + { + account: from as Hex, + data: data as Hex, + to: to as Hex, + }, + ); + + // fixed overhead to cover edge cases, EP uses only 5000 but I added 10x for Linea based on my tests + let gasLimitOverhead = 50000n; + + // Now we need the verificationGasLimit and callGasLimit from the user op. + const decodedData = decodeFunctionData({ abi: ENTRYPOINT_V6_ABI, data }); + const firstArg = decodedData.args[0]; + + if (Array.isArray(firstArg) && firstArg.length > 0) { + const userOp = firstArg[0]; + + // This is how the EP contract checks if there's enough gas: + // gasleft() < userOp.callGasLimit + userOp.verificationGasLimit + 5000 + gasLimitOverhead += + BigInt(userOp.verificationGasLimit) + BigInt(userOp.callGasLimit); + } + + const newGasLimit = estimateGasResponse.gasLimit + gasLimitOverhead; + response.gasLimit = `0x${newGasLimit.toString(16)}`; + + return { + ...response, + type: "eip1559", + maxFeePerGas: + BigInt(estimateGasResponse.baseFeePerGas) + + BigInt(estimateGasResponse.priorityFeePerGas), + maxPriorityFeePerGas: BigInt( + (estimateGasResponse.priorityFeePerGas * 3n) / 2n, + ), + }; + } + async executeTransaction( executeTransactionParams: ExecuteTransactionParamsType, transactionId: string, @@ -525,18 +544,27 @@ export class EVMTransactionService retryExecuteTransactionParams: ExecuteTransactionParamsType, ): Promise => { const { rawTransaction, account } = retryExecuteTransactionParams; + // TODO: Extract this to config + const maxFailedTransactionCount = 5; + + const _log = log.child({ + transactionId, + chainId: this.chainId, + relayer: account.address, + maxFailedTransactionCount, + }); try { - log.info( - `Getting failed transaction retry count for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.info(`Getting failed transaction retry count`); const failedTransactionRetryCount = parseInt( await this.cacheService.get( getFailedTransactionRetryCountKey(transactionId, this.chainId), ), 10, ); - - const maxFailedTransactionCount = 5; + _log.info( + { failedTransactionRetryCount }, + `Got failed transaction retry count`, + ); if (failedTransactionRetryCount > maxFailedTransactionCount) { throw new Error( @@ -544,193 +572,265 @@ export class EVMTransactionService ); } - log.info( - `Sending transaction to network: ${customJSONStringify( - rawTransaction, - )} for bundler address: ${ - rawTransaction.from - } for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.info(`Calling networkService.sendTransaction`); + const sendTransactionResponse = await this.networkService.sendTransaction(rawTransaction, account); - log.info( - `Send transaction response: ${customJSONStringify( - sendTransactionResponse, - )} for bundler address: ${ - rawTransaction.from - } for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + + _log.info({ sendTransactionResponse }, `Network service responded`); + if (sendTransactionResponse instanceof Error) { - log.info( - `Transaction execution failed and checking for retry for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + _log.error(`Transaction execution failed and checking for retry`); throw sendTransactionResponse; } + this.nonceManager.markUsed( account.getPublicKey(), rawTransaction.nonce, ); + return { ...rawTransaction, hash: sendTransactionResponse, }; - } catch (error: any) { + } catch (error: unknown) { await this.cacheService.increment( getFailedTransactionRetryCountKey(transactionId, this.chainId), 1, ); - const errInString = parseError(error).toLowerCase(); - log.error( - `Error while executing transaction: ${errInString} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + const errorString = parseError(error).toLowerCase(); + + _log.error( + `Error while executing transaction: ${errorString} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, ); - const { - ALREADY_KNOWN, - REPLACEMENT_TRANSACTION_UNDERPRICED, - TRANSACTION_UNDERPRICED, - INSUFFICIENT_FUNDS, - NONCE_TOO_LOW, - MAX_PRIORITY_FEE_HIGHER_THAN_MAX_FEE, - RPC_FAILURE, - INTRINSIC_GAS_TOO_LOW, - MAX_FEE_PER_GAS_LESS_THAN_BLOCK_BASE_FEE, - } = config.transaction.rpcResponseErrorMessages; - - if (NONCE_TOO_LOW.some((str) => errInString.indexOf(str) > -1)) { - log.info( - `Nonce too low error for for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - const correctNonce = await this.handleNonceTooLow(rawTransaction); - log.info( - `Correct nonce to be used: ${correctNonce} for for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); + + if (isNonceTooLow(errorString)) { + _log.warn({ errorString }, `Error: Nonce too low`); + + const correctNonce = await this.handleNonceTooLow(account); + + _log.info({ correctNonce }, `Correct nonce is: ${correctNonce}`); rawTransaction.nonce = correctNonce; return await retryExecuteTransaction({ rawTransaction, account }); - } else if ( - REPLACEMENT_TRANSACTION_UNDERPRICED.some( - (str) => errInString.indexOf(str) > -1, - ) || - TRANSACTION_UNDERPRICED.some((str) => errInString.indexOf(str) > -1) - ) { - log.info( - `Replacement transaction underpriced or transaction underpriced error for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + } else if (isReplacementTransactionUnderpriced(errorString)) { + _log.warn( + { errorString }, + `Error: Replacement transaction underpriced or transaction underpriced`, ); + const bumpedUpGasPrice = await this.handleReplacementFeeTooLow(rawTransaction); if (typeof bumpedUpGasPrice !== "bigint") { - log.info( - `rawTransaction.maxFeePerGas ${rawTransaction.maxFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} before bumping up`, - ); - log.info( - `rawTransaction.maxPriorityFeePerGas ${rawTransaction.maxPriorityFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} before bumping up`, + _log.info( + { + maxFeePerGas: rawTransaction.maxFeePerGas, + maxPriorityFeePeGas: rawTransaction.maxPriorityFeePerGas, + }, + `Fees per gas before bumping up`, ); + rawTransaction.maxFeePerGas = bumpedUpGasPrice.maxFeePerGas; rawTransaction.maxPriorityFeePerGas = bumpedUpGasPrice.maxPriorityFeePerGas; - log.info( - `rawTransaction.maxFeePerGas ${rawTransaction.maxFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} after bumping up`, - ); - log.info( - `rawTransaction.maxPriorityFeePerGas ${rawTransaction.maxPriorityFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} after bumping up`, + + _log.info( + { + maxFeePerGas: rawTransaction.maxFeePerGas, + maxPriorityFeePeGas: rawTransaction.maxPriorityFeePerGas, + }, + `Fees per gas after bumping up`, ); } else { - log.info( - `rawTransaction.gasPrice ${rawTransaction.gasPrice} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} before bumping up`, + _log.info( + { gasPrice: rawTransaction.gasPrice }, + `Gas price before bumping up`, ); + rawTransaction.gasPrice = bumpedUpGasPrice; + log.info( - `increasing gas price for the resubmit transaction ${rawTransaction.gasPrice} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} after bumping up`, + { gasPrice: rawTransaction.gasPrice }, + `Gas price after bumping up`, ); } return await retryExecuteTransaction({ rawTransaction, account }); - } else if (ALREADY_KNOWN.some((str) => errInString.indexOf(str) > -1)) { - log.info( - `Already known transaction hash with same payload and nonce for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}. Not doing anything`, - ); + } else if (isAlreadyKnown(errorString)) { // https://github.com/ethereum/go-ethereum/blob/25733a4aadba3b60a9766f1e6ac9c787588ba678/core/txpool/errors.go#L22 // https://docs.alchemy.com/reference/error-reference - } else if ( - INSUFFICIENT_FUNDS.some((str) => errInString.indexOf(str) > -1) - ) { - log.info( - `Bundler address: ${rawTransaction.from} has insufficient funds for transactionId: ${transactionId} on chainId: ${this.chainId}`, + _log.warn( + { errorString }, + `Error: Already known transaction hash with same payload and nonce. Not doing anything`, + ); + } else if (isInsufficientFundsError(errorString)) { + _log.warn( + { errorString }, + `Relayer: ${rawTransaction.from} has insufficient funds for transaction`, ); throw new Error("Bundler balance too low. Send bundler for funding"); - } else if ( - MAX_PRIORITY_FEE_HIGHER_THAN_MAX_FEE.some( - (str) => errInString.indexOf(str) > -1, - ) - ) { + } else if (isMaxPriorityFeeHigherThanMaxFee(errorString)) { + _log.warn( + { errorString }, + `Error: MaxPriorityFeePerGas higher than MaxFeePerGas`, + ); + const bumpedUpGasPrice = await this.handleReplacementFeeTooLow(rawTransaction); if (typeof bumpedUpGasPrice !== "bigint") { - log.info( + _log.info( `rawTransaction.maxFeePerGas ${rawTransaction.maxFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} before bumping up`, ); rawTransaction.maxFeePerGas = bumpedUpGasPrice.maxFeePerGas; - log.info( + _log.info( `rawTransaction.maxFeePerGas ${rawTransaction.maxFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} after bumping up`, ); } return await retryExecuteTransaction({ rawTransaction, account }); - } else if (RPC_FAILURE.some((str) => errInString.indexOf(str) > -1)) { - return await retryExecuteTransaction({ rawTransaction, account }); - } else if ( - INTRINSIC_GAS_TOO_LOW.some((str) => errInString.indexOf(str) > -1) - ) { - const bumpedUpGasLimit = await this.handleGasTooLow(rawTransaction); - - log.info( - `rawTransaction.gasLimit ${rawTransaction.gasLimit} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} before bumping up`, + } else if (isRpcFailure(errorString)) { + _log.warn( + { errorString }, + `Error: RPC failure. Retrying transaction.`, ); - rawTransaction.gasLimit = bumpedUpGasLimit; - log.info( - `increasing gas limit for the resubmit transaction ${rawTransaction.gasPrice} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + return retryExecuteTransaction({ rawTransaction, account }); + } else if (isIntrinsicGasTooLow(errorString)) { + _log.warn( + { errorString }, + `Error: Intrinsic gas too low. Retrying transaction.`, ); - log.info( - `rawTransaction.gasLimit ${rawTransaction.gasLimit} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} after bumping up`, + + const bumpedUpGasLimit = await this.handleGasTooLow(rawTransaction); + _log.info( + { bumpedUpGasLimit }, + `Bumped up gas limits, retrying transaction`, ); + return await retryExecuteTransaction({ rawTransaction, account }); - } else if ( - MAX_FEE_PER_GAS_LESS_THAN_BLOCK_BASE_FEE.some( - (str) => errInString.indexOf(str) > -1, - ) - ) { + } else if (isMaxFeeBelowBlockBaseFee(errorString)) { + _log.warn( + { errorString }, + `Error: MaxFeePerGas below block base fee. Retrying transaction.`, + ); + const bumpedUpGasPrice = await this.handleReplacementFeeTooLow(rawTransaction); + _log.info( + { bumpedUpGasPrice }, + `Bumped up gas price, retrying transaction`, + ); + if (typeof bumpedUpGasPrice !== "bigint") { - log.info( - `rawTransaction.maxFeePerGas ${rawTransaction.maxFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} before bumping up`, - ); rawTransaction.maxFeePerGas = bumpedUpGasPrice.maxFeePerGas; - log.info( - `increasing gas price for the resubmit transaction ${rawTransaction.gasPrice} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, - ); - log.info( - `rawTransaction.maxFeePerGas ${rawTransaction.maxFeePerGas} for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId} after bumping up`, - ); } return await retryExecuteTransaction({ rawTransaction, account }); } else { - log.info( - `Error: ${errInString} not handled. Transaction not being retried for bundler address: ${rawTransaction.from} for transactionId: ${transactionId} on chainId: ${this.chainId}`, + _log.error( + { + errorString, + errorRaw: error, + }, + `Critical: don't know how to handle error. Transaction not being retried`, ); - throw new Error(errInString); + throw new Error(errorString); } - throw new Error(errInString); + + throw new Error(errorString); } }; - const response = await retryExecuteTransaction(executeTransactionParams); - return response; + return retryExecuteTransaction(executeTransactionParams); } - private async handleNonceTooLow(rawTransaction: EVMRawTransactionType) { + /** + * cancelTransaction cancels the pending transaction for the given account & nonce + * @param account IEVMAccount + * @param nonce number + * @returns Receipt or error + */ + async cancelTransaction( + account: IEVMAccount, + nonce: number, + ): Promise { + const relayerAddress = account.getPublicKey(); + log.info( + `Cancel transaction request received for relayerAddress: ${relayerAddress} for nonce=${nonce} on chainId: ${this.chainId}`, + ); + + const addressMutex = this.getMutex(account.getPublicKey()); + log.info( + `Taking lock on address: ${account.getPublicKey()} to cancel transaction for relayerAddress: ${relayerAddress} for nonce: ${nonce} on chainId: ${ + this.chainId + }`, + ); + const release = await addressMutex.acquire(); + + try { + const gasPrice = await this.gasPriceService.getGasPrice( + GasPriceType.FAST, + ); + let gasParams: + | { type: "legacy"; gasPrice: bigint } + | { + type: "eip1559"; + maxFeePerGas: bigint; + maxPriorityFeePerGas: bigint; + }; + if (typeof gasPrice !== "bigint") { + log.info( + `Gas price being used to cancel transaction by relayer: ${relayerAddress} for nonce: ${nonce} is: ${customJSONStringify( + gasPrice, + )} on chainId: ${this.chainId}`, + ); + const { maxPriorityFeePerGas, maxFeePerGas } = gasPrice; + gasParams = { + type: "eip1559", + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }; + } else { + gasParams = { type: "legacy", gasPrice: BigInt(gasPrice) }; + } + + const rawCancelTransaction: EVMRawTransactionType = { + from: relayerAddress, + to: relayerAddress as `0x${string}`, + value: BigInt(0), + data: "0x", + nonce, + gasLimit: toHex(21000), + chainId: this.chainId, + ...gasParams, + }; + + const cancelTransactionResponse = + await this.networkService.sendTransaction( + rawCancelTransaction, + account, + ); + + log.info( + `Cancel transaction txHash: ${cancelTransactionResponse} for relayerAddress=${relayerAddress} and nonce: ${nonce} on chainId: ${this.chainId}`, + ); + + const receipt = await this.networkService.waitForTransaction( + cancelTransactionResponse as string, + "ADMIN_CANCEL", + ); + + release(); + + return receipt; + } catch (err: unknown) { + release(); + throw err; + } + } + + private async handleNonceTooLow(account: IEVMAccount) { const correctNonce = await this.nonceManager.getAndSetNonceFromNetwork( - rawTransaction.from, + account, true, ); return correctNonce; @@ -745,76 +845,66 @@ export class EVMTransactionService maxFeePerGas: rawTransaction.maxFeePerGas, maxPriorityFeePerGas: rawTransaction.maxPriorityFeePerGas, }; - const bumpedUpGasPrice = this.gasPriceService.getBumpedUpGasPrice( + const bumpedUpGasPrice = await this.gasPriceService.getBumpedUpGasPrice( pastGasPrice as NetworkBasedGasPriceType, 50, ); return bumpedUpGasPrice; } - // eslint-disable-next-line class-methods-use-this private async handleGasTooLow(rawTransaction: EVMRawTransactionType) { return toHex(Number(rawTransaction.gasLimit) * 2); } +} - private async sendMaxRetryCountExceededSlackNotification( - transactionId: string, - account: IEVMAccount, - transactionType: TransactionType, - chainId: number, - ) { - const maxRetryCountNotificationMessage = - getMaxRetryCountNotificationMessage( - transactionId, - account, - transactionType, - chainId, - ); - const slackNotifyObject = this.notificationManager.getSlackNotifyObject( - maxRetryCountNotificationMessage, - ); - await this.notificationManager.sendSlackNotification(slackNotifyObject); - } +function isMaxFeeBelowBlockBaseFee(errorString: string) { + return MAX_FEE_PER_GAS_LESS_THAN_BLOCK_BASE_FEE.some( + (str) => errorString.indexOf(str) > -1, + ); +} - private async sendTransactionFailedSlackNotification( - transactionId: string, - chainId: number, - error: string, - ) { - try { - const message = getTransactionErrorNotificationMessage( - transactionId, - chainId, - error, - ); - const slackNotifyObject = - this.notificationManager.getSlackNotifyObject(message); - await this.notificationManager.sendSlackNotification(slackNotifyObject); - } catch (errorInSlack) { - log.error( - `Error in sending slack notification: ${parseError(errorInSlack)}`, - ); - } - } +function isIntrinsicGasTooLow(errorString: string) { + return INTRINSIC_GAS_TOO_LOW.some((str) => errorString.indexOf(str) > -1); +} - private async sendRelayerFundingSlackNotification( - relayerAddress: string, - chainId: number, - transactionHash: string, - ) { - try { - const message = getRelayerFundingNotificationMessage( - relayerAddress, - chainId, - transactionHash, - ); - const slackNotifyObject = - this.notificationManager.getSlackNotifyObject(message); - await this.notificationManager.sendSlackNotification(slackNotifyObject); - } catch (errorInSlack) { - log.error( - `Error in sending slack notification: ${parseError(errorInSlack)}`, - ); - } - } +function isRpcFailure(errorString: string) { + return RPC_FAILURE.some((str) => errorString.indexOf(str) > -1); +} + +function isMaxPriorityFeeHigherThanMaxFee(errorString: string) { + return MAX_PRIORITY_FEE_HIGHER_THAN_MAX_FEE.some( + (str) => errorString.indexOf(str) > -1, + ); +} + +function isInsufficientFundsError(errorString: string) { + return INSUFFICIENT_FUNDS.some((str) => errorString.indexOf(str) > -1); +} + +function isAlreadyKnown(errorString: string) { + return ALREADY_KNOWN.some((str) => errorString.indexOf(str) > -1); +} + +function isReplacementTransactionUnderpriced(errInString: string) { + return ( + REPLACEMENT_TRANSACTION_UNDERPRICED.some( + (str) => errInString.indexOf(str) > -1, + ) || TRANSACTION_UNDERPRICED.some((str) => errInString.indexOf(str) > -1) + ); +} + +function isNonceTooLow(errInString: string) { + return NONCE_TOO_LOW.some((str) => errInString.indexOf(str) > -1); } + +const { + ALREADY_KNOWN, + REPLACEMENT_TRANSACTION_UNDERPRICED, + TRANSACTION_UNDERPRICED, + INSUFFICIENT_FUNDS, + NONCE_TOO_LOW, + MAX_PRIORITY_FEE_HIGHER_THAN_MAX_FEE, + RPC_FAILURE, + INTRINSIC_GAS_TOO_LOW, + MAX_FEE_PER_GAS_LESS_THAN_BLOCK_BASE_FEE, +} = config.transaction.rpcResponseErrorMessages; diff --git a/src/relayer/transaction-service/types/index.ts b/src/relayer/transaction-service/types/index.ts index 7fd07cb3..3546c3ec 100644 --- a/src/relayer/transaction-service/types/index.ts +++ b/src/relayer/transaction-service/types/index.ts @@ -4,7 +4,6 @@ import { IGasPriceService } from "../../../common/gas-price"; import { GasPriceType } from "../../../common/gas-price/types"; import { INetworkService } from "../../../common/network"; import { RetryTransactionQueueData } from "../../../common/queue/types"; -import { INotificationManager } from "../../../common/notification/interface"; import { AccessListItem, EVMRawTransactionType } from "../../../common/types"; import { IEVMAccount } from "../../account"; import { INonceManager } from "../../nonce-manager"; @@ -17,7 +16,6 @@ export type EVMTransactionServiceParamsType = { gasPriceService: IGasPriceService; transactionDao: ITransactionDAO; cacheService: ICacheService; - notificationManager: INotificationManager; userOperationStateDao: IUserOperationStateDAO; options: { chainId: number; diff --git a/src/server/api/admin/cancel/handler.ts b/src/server/api/admin/cancel/handler.ts new file mode 100644 index 00000000..1db8895c --- /dev/null +++ b/src/server/api/admin/cancel/handler.ts @@ -0,0 +1,86 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { + EVMRelayerManagerMap, + networkServiceMap, + transactionServiceMap, +} from "../../../../common/service-manager"; +import { customJSONStringify } from "../../../../common/utils"; +import { config } from "../../../../config"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +/** + * cancelTransaction is used when one of the relayers gets stuck (forever) and the transaction is not mined. + * @returns Cancelation transaction receipt or 500 error + */ +export const cancelTransaction = async (req: Request, res: Response) => { + try { + const { adminAddress, relayerAddress, nonce, chainId } = req.body; + log.info(`Received /cancel request: ${customJSONStringify(req.body)}`); + + if (!adminAddress) { + return res.status(400).json({ + error: "Admin address is required", + }); + } + + if (!config.adminAddresses.includes(adminAddress)) { + return res.status(401).json({ + error: "Unauthorized (invalid admin address)", + }); + } + + // get the value of the Authentication header (signature) + const signature = req.headers.authorization; + if (!signature) { + return res.status(401).json({ + error: "Unauthorized (empty Authorization header)", + }); + } + + const networkService = networkServiceMap[chainId]; + if (!networkService) { + return res.status(400).json({ + error: `Network service for chainId=${chainId} not found`, + }); + } + + const valid = await networkService.verifySignature( + adminAddress, + JSON.stringify(req.body), + signature, + ); + if (!valid) { + return res.status(401).json({ + error: "Unauthorized (invalid signature)", + }); + } + + const relayerManager = EVMRelayerManagerMap["RM1"][chainId]; + + const relayer = relayerManager.getRelayer(relayerAddress); + if (!relayer) { + return res.status(400).json({ + error: `Relayer with address=${relayerAddress} not found`, + }); + } + + const transactionService = transactionServiceMap[chainId]; + if (!transactionService) { + return res.status(400).json({ + error: `Transaction service for chainId=${chainId} not found`, + }); + } + + const receipt = await transactionService.cancelTransaction(relayer, nonce); + return res.json(receipt); + } catch (err) { + log.error(`Error in /cancel handler: ${customJSONStringify(err)}`); + return res.status(500).json({ + error: customJSONStringify(err), + }); + } +}; diff --git a/src/server/api/admin/health/handler.test.ts b/src/server/api/admin/health/handler.test.ts index f41e7f79..3fc72a1a 100644 --- a/src/server/api/admin/health/handler.test.ts +++ b/src/server/api/admin/health/handler.test.ts @@ -8,7 +8,6 @@ jest.mock("../../../../common/service-manager", () => ({ }, })); -// eslint-disable-next-line import/first import { health } from "./handler"; // The tests for this endpoint are quite minimal and simple. diff --git a/src/server/api/admin/health/handler.ts b/src/server/api/admin/health/handler.ts index 9d0253ad..871364d8 100644 --- a/src/server/api/admin/health/handler.ts +++ b/src/server/api/admin/health/handler.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; import { logger } from "../../../../common/logger"; import { customJSONStringify } from "../../../../common/utils"; @@ -41,6 +40,7 @@ export const health = async (req: Request, res: Response) => { return res .status(chainStatuses.every((cs) => cs.healthy) ? 200 : 500) .json(chainStatuses); + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { log.error(`Error in /health/:chainId handler: ${customJSONStringify(err)}`); return res.status(500).json({ diff --git a/src/server/api/admin/info/handler.ts b/src/server/api/admin/info/handler.ts index ef8dc081..f7e6698b 100644 --- a/src/server/api/admin/info/handler.ts +++ b/src/server/api/admin/info/handler.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; import { logger } from "../../../../common/logger"; import { statusService } from "../../../../common/service-manager"; @@ -8,9 +7,16 @@ const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -// GET /info -// Returns basic information about the bundler, currently only relayer info. +// GET /info/ +// Returns basic information about a supported chain on the bundler, currently only relayer info. export const info = async (req: Request, res: Response) => { + const { chainId } = req.params; + if (!chainId) { + return res.status(400).json({ + error: "chainId is required", + }); + } + try { // This shouldn't ever happen because we have a startup probe that checks if the status service is initialized, // but we want to keep it as a sanity check and return 503 so the users know it's gonna be available soon @@ -20,10 +26,10 @@ export const info = async (req: Request, res: Response) => { .send("Status service not ready to accept requests yet, please wait"); } - const statusInfo = await statusService.info(); + const statusInfo = await statusService.info(parseInt(chainId)); return res.status(200).json(statusInfo); - } catch (err: any) { + } catch (err: unknown) { log.error(`Error in /info handler: ${customJSONStringify(err)}`); return res.status(500).send(customJSONStringify(err)); } diff --git a/src/server/api/admin/router.ts b/src/server/api/admin/router.ts index 47f06aed..d73b4fb9 100644 --- a/src/server/api/admin/router.ts +++ b/src/server/api/admin/router.ts @@ -4,10 +4,12 @@ import { startupProbe } from "./startup/handler"; import { health } from "./health/handler"; import { validateChainId } from "../v2/shared/middleware"; import { info } from "./info/handler"; +import { cancelTransaction } from "./cancel/handler"; export const adminApiRouter = Router(); adminApiRouter.get("/", settings); adminApiRouter.get("/startup", startupProbe); adminApiRouter.get("/health/:chainId?", validateChainId(), health); -adminApiRouter.get("/info", info); +adminApiRouter.get("/info/:chainId", info); +adminApiRouter.post("/cancel", cancelTransaction); diff --git a/src/server/api/admin/startup/handler.test.ts b/src/server/api/admin/startup/handler.test.ts index 1443c045..e3f82a82 100644 --- a/src/server/api/admin/startup/handler.test.ts +++ b/src/server/api/admin/startup/handler.test.ts @@ -6,7 +6,7 @@ jest.mock("../../../../common/service-manager", () => ({ })); // This has to happen after jest.mock or the import won't be mocked -// eslint-disable-next-line import/first + import { startupProbe } from "./handler"; describe("/startup", () => { diff --git a/src/server/api/admin/startup/handler.ts b/src/server/api/admin/startup/handler.ts index 02251fc7..8565adb7 100644 --- a/src/server/api/admin/startup/handler.ts +++ b/src/server/api/admin/startup/handler.ts @@ -1,7 +1,6 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; import { logger } from "../../../../common/logger"; -import { STATUSES } from "../../shared/middleware"; +import { STATUSES } from "../../shared/statuses"; import { customJSONStringify } from "../../../../common/utils"; import { statusService } from "../../../../common/service-manager"; @@ -17,6 +16,7 @@ export const startupProbe = async (req: Request, res: Response) => { if (!statusService) { return res.status(503).json({ ok: false, errors: [] }); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { log.error(`Error in startupProbe handler ${customJSONStringify(error)}`); res diff --git a/src/server/api/methods/biconomy.ts b/src/server/api/methods/biconomy.ts new file mode 100644 index 00000000..cdd706e4 --- /dev/null +++ b/src/server/api/methods/biconomy.ts @@ -0,0 +1,5 @@ +export enum BiconomyMethods { + biconomy_getGasFeeValues = "biconomy_getGasFeeValues", + biconomy_getUserOperationStatus = "biconomy_getUserOperationStatus", + biconomy_getUserOperationsByApiKey = "biconomy_getUserOperationsByApiKey", +} diff --git a/src/server/api/methods/bundler.ts b/src/server/api/methods/bundler.ts new file mode 100644 index 00000000..095cd105 --- /dev/null +++ b/src/server/api/methods/bundler.ts @@ -0,0 +1,7 @@ +export enum BundlerMethods { + eth_sendUserOperation = "eth_sendUserOperation", + eth_estimateUserOperationGas = "eth_estimateUserOperationGas", + eth_getUserOperationByHash = "eth_getUserOperationByHash", + eth_getUserOperationReceipt = "eth_getUserOperationReceipt", + eth_supportedEntryPoints = "eth_supportedEntryPoints", +} diff --git a/src/server/api/methods/eth.ts b/src/server/api/methods/eth.ts new file mode 100644 index 00000000..78d6876b --- /dev/null +++ b/src/server/api/methods/eth.ts @@ -0,0 +1,12 @@ +export enum EthMethods { + eth_chainId = "eth_chainId", + eth_getTransactionCount = "eth_getTransactionCount", + eth_getBalance = "eth_getBalance", + eth_gasPrice = "eth_gasPrice", + eth_feeHistory = "eth_feeHistory", + eth_maxPriorityFeePerGas = "eth_maxPriorityFeePerGas", + eth_estimateGas = "eth_estimateGas", + eth_call = "eth_call", + eth_getTransactionReceipt = "eth_getTransactionReceipt", + eth_sendRawTransaction = "eth_sendRawTransaction", +} diff --git a/src/server/api/router.ts b/src/server/api/router.ts index 33387058..555af51e 100644 --- a/src/server/api/router.ts +++ b/src/server/api/router.ts @@ -1,10 +1,12 @@ import { Router } from "express"; import { adminApiRouter } from "./admin/router"; import { v2Router } from "./v2/router"; +import { v3Router } from "./v3/router"; const routes = Router(); routes.use("/api/v2", v2Router); +routes.use("/api/v3", v3Router); routes.use("/admin", adminApiRouter); export { routes }; diff --git a/src/server/api/shared/auth/Authenticate.ts b/src/server/api/shared/auth/Authenticate.ts deleted file mode 100644 index 3603f735..00000000 --- a/src/server/api/shared/auth/Authenticate.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint-disable import/no-import-module-exports */ -import { Request } from "express"; -import { logger } from "../../../../common/logger"; -import { - axiosPostCall, - customJSONStringify, - parseError, -} from "../../../../common/utils"; -import { config } from "../../../../config"; -import { STATUSES } from "../middleware"; - -const log = logger.child({ - module: module.filename.split("/").slice(-4).join("/"), -}); - -export const authenticate = async (req: Request) => { - try { - const { chainId, apiKey } = req.params; - const { method, id, params } = req.body; - log.info(`chainId from request params: ${chainId}`); - log.info(`apiKey from request params: ${apiKey}`); - log.info(`method from request body: ${method}`); - log.info(`id from request body: ${id}`); - log.info(`params from request body: ${customJSONStringify(params)}`); - - const aaDashboardBackendBaseUrl = config.aaDashboardBackend.url; - - const response = await axiosPostCall( - `${aaDashboardBackendBaseUrl}/${chainId}/${apiKey}`, - { - jsonRpcMethod: method, - jsonRpcId: id, - jsponRpcParams: params, - rawRequest: req.body, - }, - ); - - const { isAuthenticated, message, bundlerRequestId } = response.data; - - log.info( - `Response from AA Dashboard Backend: ${customJSONStringify(response)}`, - ); - - if (!isAuthenticated) { - return { - code: STATUSES.UNAUTHORIZED, - message: `APIKey not authenticated with message: ${message}`, - data: { - bundlerRequestId, - }, - }; - } - - req.body.params[6] = bundlerRequestId; - - return { - code: STATUSES.ACTION_COMPLETE, - message: "Authenticated", - data: { - bundlerRequestId, - }, - }; - } catch (error) { - log.error( - `Error in authentication for bundler request: ${parseError(error)}`, - ); - return { - code: STATUSES.INTERNAL_SERVER_ERROR, - message: "Not authenticated", - data: null, - }; - } -}; diff --git a/src/server/api/shared/auth/RequestAuthenticationHandler.ts b/src/server/api/shared/auth/RequestAuthenticationHandler.ts deleted file mode 100644 index 75f6fcf5..00000000 --- a/src/server/api/shared/auth/RequestAuthenticationHandler.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { parseError } from "../../../../common/utils"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../middleware"; -import { EthMethodType, TransactionMethodType } from "../../../../common/types"; -import { authenticate } from "./Authenticate"; - -export const authenticateBundlerRequest = - () => async (req: Request, res: Response, next: NextFunction) => { - try { - const { method, id } = req.body; - let response = null; - switch (method) { - case TransactionMethodType.BUNDLER: - response = await authenticate(req); - break; - case EthMethodType.ESTIMATE_USER_OPERATION_GAS: - response = await authenticate(req); - break; - case EthMethodType.GET_USER_OPERATION_BY_HASH: - response = await authenticate(req); - break; - case EthMethodType.GET_USER_OPERATION_RECEIPT: - response = await authenticate(req); - break; - case EthMethodType.SUPPORTED_ENTRY_POINTS: - response = await authenticate(req); - break; - case EthMethodType.CHAIN_ID: - response = await authenticate(req); - break; - case EthMethodType.GAS_AND_GAS_PRICES: - response = await authenticate(req); - break; - default: - response = { - jsonrpc: "2.0", - id: id || 1, - error: { - code: STATUSES.BAD_REQUEST, - message: `Method: ${method} not supported by Bundler`, - }, - }; - } - - if (!response) { - return res.status(STATUSES.INTERNAL_SERVER_ERROR).send({ - jsonrpc: "2.0", - id: id || 1, - error: { - code: STATUSES.INTERNAL_SERVER_ERROR, - message: "Internal Server Error", - }, - }); - } - - if (response.code === STATUSES.UNAUTHORIZED) { - return res.send({ - jsonrpc: "2.0", - id: id || 1, - error: { - code: BUNDLER_ERROR_CODES.UNAUTHORIZED_REQUEST, - message: response.message, - }, - }); - } - - req.body.bundlerRequestId = response.data?.bundlerRequestId; - - return next(); - } catch (error) { - const { id } = req.body; - return res.status(STATUSES.INTERNAL_SERVER_ERROR).send({ - jsonrpc: "2.0", - id: id || 1, - error: { - code: STATUSES.INTERNAL_SERVER_ERROR, - message: `Internal Server Error: ${parseError(error)}`, - }, - }); - } - }; diff --git a/src/server/api/shared/auth/UpdateRequest.ts b/src/server/api/shared/auth/UpdateRequest.ts deleted file mode 100644 index f3a0b490..00000000 --- a/src/server/api/shared/auth/UpdateRequest.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* eslint-disable import/no-import-module-exports */ -import { UpdateRequestDataType } from "../../../../common/types"; -import { - axiosPatchCall, - customJSONStringify, - parseError, -} from "../../../../common/utils"; -import { config } from "../../../../config"; -import { logger } from "../../../../common/logger"; - -const log = logger.child({ - module: module.filename.split("/").slice(-4).join("/"), -}); - -export const updateRequest = async ( - updateRequestData: UpdateRequestDataType, -) => { - try { - const { - chainId, - apiKey, - bundlerRequestId, - rawResponse, - httpResponseCode, - transactionId, - } = updateRequestData; - const aaDashboardBackendBaseUrl = config.aaDashboardBackend.url; - - const response = await axiosPatchCall( - `${aaDashboardBackendBaseUrl}/${chainId}/${apiKey}`, - { - _id: bundlerRequestId, - rawResponse, - transactionId, - httpResponseCode, - }, - ); - - log.info( - `Response from AA Dashboard Backend for updating request: ${customJSONStringify( - response, - )}`, - ); - } catch (error) { - log.error( - `Error in updating bundler request with error: ${parseError(error)}`, - ); - } -}; diff --git a/src/server/api/shared/auth/index.ts b/src/server/api/shared/auth/index.ts deleted file mode 100644 index e1955655..00000000 --- a/src/server/api/shared/auth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./RequestAuthenticationHandler"; diff --git a/src/server/api/shared/errors/codes.ts b/src/server/api/shared/errors/codes.ts new file mode 100644 index 00000000..a3f854cd --- /dev/null +++ b/src/server/api/shared/errors/codes.ts @@ -0,0 +1,25 @@ +// The below error codes are taken from either https://www.jsonrpc.org/specification or https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace +export const BUNDLER_ERROR_CODES = { + // 4337 specific error codes + SIMULATE_VALIDATION_FAILED: -32500, + SIMULATE_PAYMASTER_VALIDATION_FAILED: -32501, + OP_CODE_VALIDATION_FAILED: -32502, + USER_OP_EXPIRES_SHORTLY: -32503, + ENTITY_IS_THROTTLED: -32504, + ENTITY_INSUFFICIENT_STAKE: -32505, + UNSUPPORTED_AGGREGATOR: -32506, + INVALID_WALLET_SIGNATURE: -32507, + // JSON RPC Standrad error codes + METHOD_NOT_FOUND: -32601, + INVALID_USER_OP_FIELDS: -32602, + // Service implementation specific error codes + WALLET_TRANSACTION_REVERTED: -32000, + UNAUTHORIZED_REQUEST: -32001, + INTERNAL_SERVER_ERROR: -32002, + BAD_REQUEST: -32003, + USER_OP_HASH_NOT_FOUND: -32004, + UNABLE_TO_PROCESS_USER_OP: -32005, + MAX_PRIORITY_FEE_PER_GAS_TOO_LOW: -32006, + MAX_FEE_PER_GAS_TOO_LOW: -32007, + PRE_VERIFICATION_GAS_TOO_LOW: -32008, +}; diff --git a/src/server/api/shared/middleware/RequestHelpers.ts b/src/server/api/shared/middleware/RequestHelpers.ts deleted file mode 100644 index 12145c49..00000000 --- a/src/server/api/shared/middleware/RequestHelpers.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { curry } from "lodash"; -import { NotFoundError } from "rest-api-errors"; - -export const STATUSES = { - SUCCESS: 200, - CREATED: 201, - MOVED_PERMANENTLY: 301, - ACTION_COMPLETE: 143, - ACTION_FAILED: 144, - USER_ALREADY_EXISTS: 145, - NOT_LOGGED_IN: 146, - USER_DOES_NOT_EXISTS: 146, - TOKEN_EXPIRED: 147, - USER_CONTRACT_WALLET_NOT_FOUND: 148, - DAPP_LIMIT_REACHED: 150, - USER_LIMIT_REACHED: 151, - API_LIMIT_REACHED: 152, - TOKEN_PRICE_UNACCEPTABLE: 153, - DAPP_INACTIVE: 155, - NO_CONTENT: 204, - METHOD_NOT_ALLOWED: 405, - BAD_REQUEST: 400, - UNAUTHORIZED: 401, - FORBIDDEN: 403, - NOT_FOUND: 404, - CONFLICT: 409, - EXPECTATION_FAILED: 417, - UNPROCESSABLE_ENTITY: 422, - INTERNAL_SERVER_ERROR: 500, - BICONOMY_ERROR: 506, - BAD_GATEWAY: 502, - TOO_MANY_REQUESTS: 429, - UNSUPPORTED_NETWORK: 507, - FUND_BUNDLER: 156, - WAIT_FOR_TRANSACTION_TIMEOUT: 157, -}; - -// The below error codes are taken from either https://www.jsonrpc.org/specification or https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace -export const BUNDLER_ERROR_CODES = { - // 4337 specific error codes - SIMULATE_VALIDATION_FAILED: -32500, - SIMULATE_PAYMASTER_VALIDATION_FAILED: -32501, - OP_CODE_VALIDATION_FAILED: -32502, - USER_OP_EXPIRES_SHORTLY: -32503, - ENTITY_IS_THROTTLED: -32504, - ENTITY_INSUFFICIENT_STAKE: -32505, - UNSUPPORTED_AGGREGATOR: -32506, - INVALID_WALLET_SIGNATURE: -32507, - // JSON RPC Standrad error codes - METHOD_NOT_FOUND: -32601, - INVALID_USER_OP_FIELDS: -32602, - // Service implementation specific error codes - WALLET_TRANSACTION_REVERTED: -32000, - UNAUTHORIZED_REQUEST: -32001, - INTERNAL_SERVER_ERROR: -32002, - BAD_REQUEST: -32003, - USER_OP_HASH_NOT_FOUND: -32004, - UNABLE_TO_PROCESS_USER_OP: -32005, - MAX_PRIORITY_FEE_PER_GAS_TOO_LOW: -32006, - MAX_FEE_PER_GAS_TOO_LOW: -32007, - PRE_VERIFICATION_GAS_TOO_LOW: -32008, -}; - -export const DB_ERRORS = { - DUPLICATE_ENTRY: 11000, -}; - -export const sendResponse = ( - res: any, - data: {} | null, - status = STATUSES.SUCCESS, -) => { - res.status(status).json(data).end(); -}; - -export const sendOne = curry((res: any, entity: any) => { - if (!entity) { - throw new NotFoundError(); - } - return sendResponse(res, entity); -}); - -export const createResponseBody = ( - message: string, - code: number, - data?: string | undefined, -) => { - const response: any = {}; - response.log = message; - response.flag = code; - response.message = data || ""; - return response; -}; - -export const sendUnsupportedAPIVersonResponse = ( - res: any, - unsupportedVersion: any, - newSupportedVersion: any, -) => { - sendResponse( - res, - createResponseBody( - `Version ${unsupportedVersion} is not supported. Please switch to version ${newSupportedVersion}`, - STATUSES.MOVED_PERMANENTLY, - ), - STATUSES.MOVED_PERMANENTLY, - ); -}; diff --git a/src/server/api/shared/middleware/index.ts b/src/server/api/shared/middleware/index.ts deleted file mode 100644 index 2ede0a97..00000000 --- a/src/server/api/shared/middleware/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./RequestHelpers"; diff --git a/src/server/api/shared/simulate/SimulateBundlerTransaction.ts b/src/server/api/shared/simulate/SimulateBundlerTransaction.ts index ff310f95..d6d04ff2 100644 --- a/src/server/api/shared/simulate/SimulateBundlerTransaction.ts +++ b/src/server/api/shared/simulate/SimulateBundlerTransaction.ts @@ -1,19 +1,19 @@ -/* eslint-disable import/no-import-module-exports */ import { Request } from "express"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../middleware"; +import { BUNDLER_ERROR_CODES } from "../errors/codes"; import { entryPointMap, + entryPointMapV07, bundlerSimulationServiceMap, + bundlerSimulationServiceMapV07, } from "../../../../common/service-manager"; import { parseError } from "../../../../common/utils"; import { logger } from "../../../../common/logger"; -import { config } from "../../../../config"; +import { STATUSES } from "../statuses"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -// eslint-disable-next-line consistent-return export const validateBundlerTransaction = async (req: Request) => { try { const userOp = req.body.params[0]; @@ -23,17 +23,114 @@ export const validateBundlerTransaction = async (req: Request) => { log.info(`chainId from request params: ${chainId}`); log.info(`dappAPIKey from request params: ${dappAPIKey}`); - if (dappAPIKey === "nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44") { - if (!config.testnetNetworks.includes(parseInt(chainId, 10))) { + const entryPointContracts = entryPointMap[parseInt(chainId, 10)]; + + const entryPointContract = entryPointContracts.find( + (entryPoint) => + entryPoint.address.toLowerCase() === entryPointAddress.toLowerCase(), + )?.entryPointContract; + + if (!entryPointContract) { + return { + code: STATUSES.BAD_REQUEST, + message: "Entry point not supported by Bundler", + }; + } + + let bundlerSimulationAndValidationResponse; + if ( + !simulationTypeData || + Object.keys(simulationTypeData).length === 0 || + simulationTypeData.simulation_type === "validation" + ) { + const start = performance.now(); + bundlerSimulationAndValidationResponse = + await bundlerSimulationServiceMap[ + parseInt(chainId, 10) + ].simulateValidation({ + userOp, + entryPointContract, + chainId: parseInt(chainId, 10), + }); + const end = performance.now(); + log.info( + `simulateValidation of bundlerSimulationServiceMap took ${ + end - start + } milliseconds`, + ); + } else { + const start = performance.now(); + bundlerSimulationAndValidationResponse = + await bundlerSimulationServiceMap[ + parseInt(chainId, 10) + ].simulateValidation({ + userOp, + entryPointContract, + chainId: parseInt(chainId, 10), + }); + const end = performance.now(); + log.info( + `simulateValidationAndExecution of bundlerSimulationServiceMap took ${ + end - start + } milliseconds`, + ); + } + + log.info( + `Bundler simulation and validation response: ${JSON.stringify( + bundlerSimulationAndValidationResponse, + )}`, + ); + + const { code, message, data } = bundlerSimulationAndValidationResponse; + + if (code !== STATUSES.SUCCESS) { + if (code === BUNDLER_ERROR_CODES.WALLET_TRANSACTION_REVERTED) { return { - code: -32400, - message: - "Request to mainnet not allowed. Please reach out and request a mainnet Bundler URL", + code, + message, + handleOpsCallData: data.handleOpsCallData, }; } + return { + code, + message, + }; } + req.body.params[2] = bundlerSimulationAndValidationResponse.data.totalGas; + req.body.params[3] = bundlerSimulationAndValidationResponse.data.userOpHash; + log.info( + `Transaction successfully simulated and validated for userOp: ${JSON.stringify( + userOp, + )} on chainId: ${chainId}`, + ); + return { + code: STATUSES.SUCCESS, + message: "User op successfully simulated and validated", + }; + } catch (error) { + log.error( + `Error in Bundler user op simulation and validation ${parseError(error)}`, + ); + return { + code: STATUSES.INTERNAL_SERVER_ERROR, + error: `Error in Bundler user op simulation and validation ${parseError( + error, + )}`, + }; + } +}; - const entryPointContracts = entryPointMap[parseInt(chainId, 10)]; +export const validateBundlerV3Transaction = async (req: Request) => { + try { + const userOp = req.body.params[0]; + const entryPointAddress = req.body.params[1]; + const { chainId, apiKey } = req.params; + const simulationTypeData = req.body.params[2]; + log.info(`chainId from request params: ${chainId}`); + log.info(`apiKey from request params: ${apiKey}`); + + const entryPointContracts = entryPointMapV07[parseInt(chainId, 10)]; const entryPointContract = entryPointContracts.find( (entryPoint) => @@ -55,7 +152,7 @@ export const validateBundlerTransaction = async (req: Request) => { ) { const start = performance.now(); bundlerSimulationAndValidationResponse = - await bundlerSimulationServiceMap[ + await bundlerSimulationServiceMapV07[ parseInt(chainId, 10) ].simulateValidation({ userOp, @@ -71,7 +168,7 @@ export const validateBundlerTransaction = async (req: Request) => { } else { const start = performance.now(); bundlerSimulationAndValidationResponse = - await bundlerSimulationServiceMap[ + await bundlerSimulationServiceMapV07[ parseInt(chainId, 10) ].simulateValidation({ userOp, @@ -110,7 +207,7 @@ export const validateBundlerTransaction = async (req: Request) => { req.body.params[2] = bundlerSimulationAndValidationResponse.data.totalGas; req.body.params[3] = bundlerSimulationAndValidationResponse.data.userOpHash; log.info( - `Transaction successfully simulated and validated for userOp: ${JSON.stringify( + `V3 Transaction successfully simulated and validated for userOp: ${JSON.stringify( userOp, )} on chainId: ${chainId}`, ); diff --git a/src/server/api/shared/simulate/SimulateHandlerBundler.ts b/src/server/api/shared/simulate/SimulateHandlerBundler.ts index ccae8a1d..b4c2164a 100644 --- a/src/server/api/shared/simulate/SimulateHandlerBundler.ts +++ b/src/server/api/shared/simulate/SimulateHandlerBundler.ts @@ -1,15 +1,16 @@ -/* eslint-disable import/no-import-module-exports */ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable no-case-declarations */ import { NextFunction, Request, Response } from "express"; import { - BiconomyMethodType, - EthMethodType, - TransactionMethodType, -} from "../../../../common/types"; -import { STATUSES } from "../middleware"; -import { validateBundlerTransaction } from "./SimulateBundlerTransaction"; + validateBundlerTransaction, + validateBundlerV3Transaction, +} from "./SimulateBundlerTransaction"; import { parseError } from "../../../../common/utils"; import { logger } from "../../../../common/logger"; +import { STATUSES } from "../statuses"; +import { BundlerMethods } from "../../methods/bundler"; +import { EthMethods } from "../../methods/eth"; +import { BiconomyMethods } from "../../methods/biconomy"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -22,52 +23,168 @@ export const simulateBundlerTransaction = const { method, id } = req.body; let response = null; switch (method) { - case TransactionMethodType.BUNDLER: + case BundlerMethods.eth_sendUserOperation: response = await validateBundlerTransaction(req); break; - case EthMethodType.ESTIMATE_USER_OPERATION_GAS: + case BundlerMethods.eth_estimateUserOperationGas: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, }; break; - case EthMethodType.GET_USER_OPERATION_BY_HASH: + case BundlerMethods.eth_getUserOperationByHash: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, }; break; - case EthMethodType.GET_USER_OPERATION_RECEIPT: + case BundlerMethods.eth_getUserOperationReceipt: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, }; break; - case EthMethodType.SUPPORTED_ENTRY_POINTS: + case BundlerMethods.eth_supportedEntryPoints: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, }; break; - case EthMethodType.CHAIN_ID: + case EthMethods.eth_chainId: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, }; break; - case EthMethodType.GAS_AND_GAS_PRICES: + case BiconomyMethods.biconomy_getGasFeeValues: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, }; break; - case BiconomyMethodType.GET_GAS_FEE_VALUES: + case BiconomyMethods.biconomy_getUserOperationStatus: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, }; break; - case BiconomyMethodType.GET_USER_OPERATION_STATUS: + default: + const end = performance.now(); + log.info( + `simulateBundlerTransaction took ${end - start} milliseconds`, + ); + response = { + jsonrpc: "2.0", + id: id || 1, + error: { + code: STATUSES.BAD_REQUEST, + message: `Method: ${method} not supported by Bundler`, + }, + }; + } + + if (!response) { + const end = performance.now(); + log.info(`simulateBundlerTransaction took ${end - start} milliseconds`); + return res.status(STATUSES.INTERNAL_SERVER_ERROR).send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: STATUSES.INTERNAL_SERVER_ERROR, + message: "Internal Server Error", + }, + }); + } + if ((response as any).code !== STATUSES.SUCCESS) { + if ((response as any).handleOpsCallData !== null) { + const end = performance.now(); + log.info( + `simulateBundlerTransaction took ${end - start} milliseconds`, + ); + return res.status(STATUSES.BAD_REQUEST).send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: (response as any).code, + message: (response as any).message, + handleOpsCallData: (response as any).handleOpsCallData, + }, + }); + } + const end = performance.now(); + log.info(`simulateBundlerTransaction took ${end - start} milliseconds`); + return res.status(STATUSES.BAD_REQUEST).send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: (response as any).code, + message: (response as any).message, + }, + }); + } + const end = performance.now(); + log.info(`simulateBundlerTransaction took ${end - start} milliseconds`); + return next(); + } catch (error) { + const { id } = req.body; + return res.status(STATUSES.INTERNAL_SERVER_ERROR).send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: STATUSES.INTERNAL_SERVER_ERROR, + message: `Internal Server Error: ${parseError(error)}`, + }, + }); + } + }; + +export const simulateBundlerV3Transaction = + () => async (req: Request, res: Response, next: NextFunction) => { + try { + const start = performance.now(); + const { method, id } = req.body; + let response = null; + switch (method) { + case BundlerMethods.eth_sendUserOperation: + response = await validateBundlerV3Transaction(req); + break; + case BundlerMethods.eth_estimateUserOperationGas: + response = { + code: STATUSES.SUCCESS, + message: `Method: ${method} does not require simulation`, + }; + break; + case BundlerMethods.eth_getUserOperationByHash: + response = { + code: STATUSES.SUCCESS, + message: `Method: ${method} does not require simulation`, + }; + break; + case BundlerMethods.eth_getUserOperationReceipt: + response = { + code: STATUSES.SUCCESS, + message: `Method: ${method} does not require simulation`, + }; + break; + case BundlerMethods.eth_supportedEntryPoints: + response = { + code: STATUSES.SUCCESS, + message: `Method: ${method} does not require simulation`, + }; + break; + case EthMethods.eth_chainId: + response = { + code: STATUSES.SUCCESS, + message: `Method: ${method} does not require simulation`, + }; + break; + case BiconomyMethods.biconomy_getGasFeeValues: + response = { + code: STATUSES.SUCCESS, + message: `Method: ${method} does not require simulation`, + }; + break; + case BiconomyMethods.biconomy_getUserOperationStatus: response = { code: STATUSES.SUCCESS, message: `Method: ${method} does not require simulation`, @@ -90,7 +207,9 @@ export const simulateBundlerTransaction = if (!response) { const end = performance.now(); - log.info(`simulateBundlerTransaction took ${end - start} milliseconds`); + log.info( + `simulateBundlerV3Transaction took ${end - start} milliseconds`, + ); return res.status(STATUSES.INTERNAL_SERVER_ERROR).send({ jsonrpc: "2.0", id: id || 1, @@ -104,7 +223,7 @@ export const simulateBundlerTransaction = if ((response as any).handleOpsCallData !== null) { const end = performance.now(); log.info( - `simulateBundlerTransaction took ${end - start} milliseconds`, + `simulateBundlerV3Transaction took ${end - start} milliseconds`, ); return res.status(STATUSES.BAD_REQUEST).send({ jsonrpc: "2.0", @@ -117,7 +236,9 @@ export const simulateBundlerTransaction = }); } const end = performance.now(); - log.info(`simulateBundlerTransaction took ${end - start} milliseconds`); + log.info( + `simulateBundlerV3Transaction took ${end - start} milliseconds`, + ); return res.status(STATUSES.BAD_REQUEST).send({ jsonrpc: "2.0", id: id || 1, @@ -128,7 +249,7 @@ export const simulateBundlerTransaction = }); } const end = performance.now(); - log.info(`simulateBundlerTransaction took ${end - start} milliseconds`); + log.info(`simulateBundlerV3Transaction took ${end - start} milliseconds`); return next(); } catch (error) { const { id } = req.body; diff --git a/src/server/api/shared/statuses.ts b/src/server/api/shared/statuses.ts new file mode 100644 index 00000000..6077ddf7 --- /dev/null +++ b/src/server/api/shared/statuses.ts @@ -0,0 +1,33 @@ +export const STATUSES = { + SUCCESS: 200, + CREATED: 201, + MOVED_PERMANENTLY: 301, + ACTION_COMPLETE: 143, + ACTION_FAILED: 144, + USER_ALREADY_EXISTS: 145, + NOT_LOGGED_IN: 146, + USER_DOES_NOT_EXISTS: 146, + TOKEN_EXPIRED: 147, + USER_CONTRACT_WALLET_NOT_FOUND: 148, + DAPP_LIMIT_REACHED: 150, + USER_LIMIT_REACHED: 151, + API_LIMIT_REACHED: 152, + TOKEN_PRICE_UNACCEPTABLE: 153, + DAPP_INACTIVE: 155, + NO_CONTENT: 204, + METHOD_NOT_ALLOWED: 405, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + FORBIDDEN: 403, + NOT_FOUND: 404, + CONFLICT: 409, + EXPECTATION_FAILED: 417, + UNPROCESSABLE_ENTITY: 422, + INTERNAL_SERVER_ERROR: 500, + BICONOMY_ERROR: 506, + BAD_GATEWAY: 502, + TOO_MANY_REQUESTS: 429, + UNSUPPORTED_NETWORK: 507, + FUND_BUNDLER: 156, + WAIT_FOR_TRANSACTION_TIMEOUT: 157, +}; diff --git a/src/server/api/v2/biconomy_getGasFeeValues/handler.ts b/src/server/api/v2/biconomy_getGasFeeValues/handler.ts index d7b55d55..f7793bd9 100644 --- a/src/server/api/v2/biconomy_getGasFeeValues/handler.ts +++ b/src/server/api/v2/biconomy_getGasFeeValues/handler.ts @@ -1,18 +1,15 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { gasPriceServiceMap } from "../../../../common/service-manager"; import { customJSONStringify, parseError } from "../../../../common/utils"; -// import { updateRequest } from '../../auth/UpdateRequest'; +import { STATUSES } from "../../shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); export const getGasFeeValues = async (req: Request, res: Response) => { - // const bundlerRequestId = req.body.params[6]; - try { const { id } = req.body; const { chainId /* apiKey */ } = req.params; @@ -26,26 +23,6 @@ export const getGasFeeValues = async (req: Request, res: Response) => { )}`, ); - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: { - // callGasLimit, - // verificationGasLimit, - // preVerificationGas, - // validUntil, - // validAfter, - // maxPriorityFeePerGas: gasPrice?.maxPriorityFeePerGas, - // maxFeePerGas: gasPrice?.maxFeePerGas, - // }, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, @@ -56,26 +33,6 @@ export const getGasFeeValues = async (req: Request, res: Response) => { }); } - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: { - // callGasLimit, - // verificationGasLimit, - // preVerificationGas, - // validUntil, - // validAfter, - // maxPriorityFeePerGas: gasPrice, - // maxFeePerGas: gasPrice, - // }, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, @@ -88,20 +45,6 @@ export const getGasFeeValues = async (req: Request, res: Response) => { log.error(`Error in getGasFeeValues handler ${parseError(error)}`); const { id } = req.body; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", id: id || 1, diff --git a/src/server/api/v2/biconomy_getUserOperationStatus/handler.ts b/src/server/api/v2/biconomy_getUserOperationStatus/handler.ts index 2374bf94..eafcfe70 100644 --- a/src/server/api/v2/biconomy_getUserOperationStatus/handler.ts +++ b/src/server/api/v2/biconomy_getUserOperationStatus/handler.ts @@ -1,6 +1,4 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { customJSONStringify, parseError } from "../../../../common/utils"; import { @@ -8,7 +6,8 @@ import { userOperationStateDao, } from "../../../../common/service-manager"; import { UserOperationStateEnum } from "../../../../common/types"; -// import { updateRequest } from '../../auth/UpdateRequest'; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -19,7 +18,7 @@ const getUserOperationStateData = async ( userOpHash: string, ): Promise<{ error: boolean; - result: any; + result: unknown; }> => { log.info( `Getting userOp state for userOpHash: ${userOpHash} on chainId: ${chainId}`, @@ -208,20 +207,6 @@ export const getUserOperationStatus = async (req: Request, res: Response) => { } catch (error) { log.error(`Error in getUserOperationStatus handler: ${parseError(error)}`); const { id } = req.body; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", id: id || 1, diff --git a/src/server/api/v2/eth_chainId/handler.test.ts b/src/server/api/v2/eth_chainId/handler.test.ts index d989e558..54504265 100644 --- a/src/server/api/v2/eth_chainId/handler.test.ts +++ b/src/server/api/v2/eth_chainId/handler.test.ts @@ -22,8 +22,6 @@ describe("eth_chainId", () => { const response = httpMocks.createResponse(); await getChainId(request, response); - // console.log(response._getJSONData()); - // Check the returned values expect(response.statusCode).toBe(200); expect(response._getJSONData()).toEqual({ diff --git a/src/server/api/v2/eth_chainId/handler.ts b/src/server/api/v2/eth_chainId/handler.ts index 8e5e4968..316fcbeb 100644 --- a/src/server/api/v2/eth_chainId/handler.ts +++ b/src/server/api/v2/eth_chainId/handler.ts @@ -1,12 +1,11 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; import { Hex } from "viem"; -import { STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { customJSONStringify } from "../../../../common/utils"; import { InternalServerError } from "../shared/errors"; import { ChainIdResponse } from "./response"; import { RPCErrorResponse } from "../shared/response"; +import { STATUSES } from "../../shared/statuses"; const filenameLogger = logger.child({ module: module.filename.split("/").slice(-4).join("/"), diff --git a/src/server/api/v2/eth_estimateUserOperationGas/errors.ts b/src/server/api/v2/eth_estimateUserOperationGas/errors.ts index 52a8132b..71c85ed4 100644 --- a/src/server/api/v2/eth_estimateUserOperationGas/errors.ts +++ b/src/server/api/v2/eth_estimateUserOperationGas/errors.ts @@ -1,6 +1,4 @@ -/* eslint-disable max-classes-per-file */ - -import { BUNDLER_ERROR_CODES } from "../../shared/middleware"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; import { RPCError } from "../shared/errors"; export class EntryPointNotSupportedError extends RPCError { diff --git a/src/server/api/v2/eth_estimateUserOperationGas/handler.test.ts b/src/server/api/v2/eth_estimateUserOperationGas/handler.test.ts index d4fb728a..353453ef 100644 --- a/src/server/api/v2/eth_estimateUserOperationGas/handler.test.ts +++ b/src/server/api/v2/eth_estimateUserOperationGas/handler.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/first */ import httpMocks, { RequestOptions } from "node-mocks-http"; const entryPointAddress = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789"; @@ -79,8 +78,6 @@ describe("EstimateUserOperationGas", () => { const response = httpMocks.createResponse(); await estimateUserOperationGas(request, response); - // console.log(response._getJSONData()); - // Check the returned values expect(response.statusCode).toBe(200); diff --git a/src/server/api/v2/eth_estimateUserOperationGas/handler.ts b/src/server/api/v2/eth_estimateUserOperationGas/handler.ts index 876de6f8..bdda8fb3 100644 --- a/src/server/api/v2/eth_estimateUserOperationGas/handler.ts +++ b/src/server/api/v2/eth_estimateUserOperationGas/handler.ts @@ -1,7 +1,5 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; import { toHex } from "viem"; -import { STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { bundlerSimulationServiceMap, @@ -17,6 +15,7 @@ import { import { EstimateUserOperationGasResponse } from "./response"; import { InternalServerError, RPCError } from "../shared/errors"; import { RPCErrorResponse } from "../shared/response"; +import { STATUSES } from "../../shared/statuses"; const filenameLogger = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -65,6 +64,7 @@ export const estimateUserOperationGas = async (req: Request, res: Response) => { // Estimate gas for the user operation using the simulator const estimatedUserOpGas = await simulator.estimateUserOperationGas({ userOp, + // eslint-disable-next-line @typescript-eslint/no-explicit-any entryPointContract: entryPointContract as any, chainId: parseInt(chainId, 10), stateOverrideSet, diff --git a/src/server/api/v2/eth_getUserOperationByHash/handler.ts b/src/server/api/v2/eth_getUserOperationByHash/handler.ts index 3faa7066..fbb09754 100644 --- a/src/server/api/v2/eth_getUserOperationByHash/handler.ts +++ b/src/server/api/v2/eth_getUserOperationByHash/handler.ts @@ -1,10 +1,9 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { userOperationDao } from "../../../../common/service-manager"; import { parseError } from "../../../../common/utils"; -// import { updateRequest } from '../../auth/UpdateRequest'; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -31,18 +30,6 @@ export const getUserOperationByHash = async (req: Request, res: Response) => { ); if (!userOperation || !userOperation.transactionHash) { - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: null, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, @@ -86,18 +73,6 @@ export const getUserOperationByHash = async (req: Request, res: Response) => { blockHash, }; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: 1, - // result, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: 1, @@ -106,20 +81,7 @@ export const getUserOperationByHash = async (req: Request, res: Response) => { } catch (error) { log.error(`Error in getUserOperationByHash handler ${parseError(error)}`); const { id } = req.body; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", id: id || 1, diff --git a/src/server/api/v2/eth_getUserOperationReceipt/handler.ts b/src/server/api/v2/eth_getUserOperationReceipt/handler.ts index 5de53e69..0955fefc 100644 --- a/src/server/api/v2/eth_getUserOperationReceipt/handler.ts +++ b/src/server/api/v2/eth_getUserOperationReceipt/handler.ts @@ -1,10 +1,9 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { userOperationDao } from "../../../../common/service-manager"; import { parseError } from "../../../../common/utils"; -// import { updateRequest } from '../../auth/UpdateRequest'; +import { STATUSES } from "../../shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -24,8 +23,6 @@ const log = logger.child({ receipt the TransactionReceipt object. Note that the returned TransactionReceipt is for the entire bundle, not only for this UserOperation. */ export const getUserOperationReceipt = async (req: Request, res: Response) => { - // const bundlerRequestId = req.body.params[6]; - try { const { id } = req.body; const { chainId /* apiKey */ } = req.params; @@ -38,17 +35,6 @@ export const getUserOperationReceipt = async (req: Request, res: Response) => { ); if (!userOperationData || !userOperationData.receipt) { - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: null, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, @@ -83,18 +69,6 @@ export const getUserOperationReceipt = async (req: Request, res: Response) => { receipt, }; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: 1, - // result, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: 1, @@ -103,20 +77,6 @@ export const getUserOperationReceipt = async (req: Request, res: Response) => { } catch (error) { log.error(`Error in getUserOperationReceipt handler: ${parseError(error)}`); const { id } = req.body; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", id: id || 1, diff --git a/src/server/api/v2/eth_getUserOperationsByApiKey/handler.ts b/src/server/api/v2/eth_getUserOperationsByApiKey/handler.ts index 72679acb..ef1d2f4c 100644 --- a/src/server/api/v2/eth_getUserOperationsByApiKey/handler.ts +++ b/src/server/api/v2/eth_getUserOperationsByApiKey/handler.ts @@ -1,9 +1,8 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; -import { STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { userOperationDao } from "../../../../common/service-manager"; import { parseError } from "../../../../common/utils"; +import { STATUSES } from "../../shared/statuses"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), diff --git a/src/server/api/v2/eth_sendUserOperation/handler.test.ts b/src/server/api/v2/eth_sendUserOperation/handler.test.ts new file mode 100644 index 00000000..6ef226d5 --- /dev/null +++ b/src/server/api/v2/eth_sendUserOperation/handler.test.ts @@ -0,0 +1,115 @@ +import httpMocks, { RequestOptions } from "node-mocks-http"; + +const entryPointAddress = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789"; +const entryPointContract = { + address: entryPointAddress, +}; +const chainId = "1"; +const requestId = "123"; + +// These mock functions are used in tests to control what is returned by other services the endpoint depends on +const estimateUserOperationGasMock = jest.fn(); +const getGasPriceMock = jest.fn(); +const transactionDaoSaveMock = jest.fn(); +const userOperationStateDaoSaveMock = jest.fn(); +const userOperationDaoSaveMock = jest.fn(); +const sendTransactionToRelayerMock = jest.fn(); + +async function saveUserOperation( + chainIdd: number, + userOperationData: InitialUserOperationDataType, +): Promise { + userOperationDaoSaveMock(chainIdd, userOperationData); +} + +// πŸ’‘ These import mocks have to happen before we call import { bundleUserOperation } +// because bundleUserOperation imports the service manager produces SIDE EFFECTS (bad practice) like trying to connect to the DB +jest.mock("../../../../common/service-manager", () => ({ + entryPointMap: { + 1: [entryPointContract], + 2: [entryPointContract], + 3: [entryPointContract], + }, + bundlerSimulationServiceMap: { + 1: { + estimateUserOperationGas: estimateUserOperationGasMock, + }, + 3: { + estimateUserOperationGas: estimateUserOperationGasMock, + }, + }, + gasPriceServiceMap: { + 1: { + getGasPrice: getGasPriceMock, + }, + }, + transactionDao: { save: transactionDaoSaveMock }, + userOperationStateDao: { save: userOperationStateDaoSaveMock }, + userOperationDao: { save: saveUserOperation }, + routeTransactionToRelayerMap: { + 1: { + BUNDLER: { + sendUserOperation: sendTransactionToRelayerMock, + }, + }, + }, +})); + +// Now we can import after we have mocked the dependencies +import { eth_sendUserOperation } from "./handler"; +import { InitialUserOperationDataType } from "../../../../common/db"; + +describe("/eth_sendUserOperation", () => { + afterEach(() => { + estimateUserOperationGasMock.mockClear(); + getGasPriceMock.mockClear(); + }); + + // This test acts as a CONTRACT test for the most common use case, ensuring that the returned value has the expected format + test("nonce > max number", async () => { + const bigNonce = 18446744073709551628n; + + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v2/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [ + { + sender: "0xCe267D94aeDF8457135Da153B6F9E74a45781F9E", + paymasterAndData: + "0xe3dc822d77f8ca7ac74c30b0dffea9fcdcaaa321000000000000000000000000000000000000000000000000000000006657398000000000000000000000000000000000000000000000000000000000000000002417136b115ef1b80ba0497e33f160c37bafe485209946a01a095bfdaa8c0060584f88f099505b46628ed9a75a99caade13ba53c62b2919180c5525e0a94759f1b", + nonce: bigNonce, + }, // userOp + entryPointAddress, + "0x1234", // userOpHash + ], + }, + }; + + // Expected nonce: 18446744073709551628 + // Max safe int: 9007199254740991 + // Bad nonce: 18446744073709550000, + + // getGasPriceMock.mockReturnValue(1005n); + sendTransactionToRelayerMock.mockReturnValueOnce({ + code: 200, + transactionId: "0x123", + }); + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await eth_sendUserOperation(request, response); + + // console.log(response._getJSONData()); + // console.log(sendTransactionToRelayerMock.mock.calls[0]); + + // console.log(userOperationDaoSaveMock.mock.calls[0]); + + // Check the returned values + expect(response.statusCode).toBe(200); + }); +}); diff --git a/src/server/api/v2/eth_sendUserOperation/handler.ts b/src/server/api/v2/eth_sendUserOperation/handler.ts index 9cb609eb..894bb108 100644 --- a/src/server/api/v2/eth_sendUserOperation/handler.ts +++ b/src/server/api/v2/eth_sendUserOperation/handler.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; import { logger } from "../../../../common/logger"; import { @@ -7,10 +6,12 @@ import { userOperationDao, userOperationStateDao, } from "../../../../common/service-manager"; +import config from "config"; import { - generateTransactionId, getPaymasterFromPaymasterAndData, parseError, + uniqueRequestId, + uniqueTransactonId, } from "../../../../common/utils"; import { isError, @@ -18,205 +19,233 @@ import { TransactionType, UserOperationStateEnum, } from "../../../../common/types"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; -// import { updateRequest } from '../../auth/UpdateRequest'; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; +import { logMeasureTime } from "../../../../common/utils/timing"; +import { Address } from "viem"; +import pino from "pino"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); -export const bundleUserOperation = async (req: Request, res: Response) => { - // const bundlerRequestId = req.body.params[6]; +/** + * Sends the user operation to the entrypoint contract as per the ERC-4337 spec + * @param req - The request object + * @param res - The response object + * @returns The response object + */ +export const eth_sendUserOperation = async (req: Request, res: Response) => { + const requestId = req.body.id != null ? req.body.id : uniqueRequestId(); + const { chainId, dappAPIKey } = req.params; + const chainIdNumber = parseInt(chainId, 10); + const transactionId = uniqueTransactonId(); + const [userOp, entryPointAddress, gasLimitFromSimulation, userOpHash] = + req.body.params; + + const _log = log.child({ + chainId: chainIdNumber, + dappAPIKey, + requestId, + transactionId, + entryPointAddress, + }); try { - const start = performance.now(); - const { id } = req.body; - const userOp = req.body.params[0]; - const entryPointAddress = req.body.params[1]; - let gasLimitFromSimulation = req.body.params[2] + 500000; - const userOpHash = req.body.params[3]; - const { chainId, dappAPIKey } = req.params; - - const chainIdInNum = parseInt(chainId, 10); - if (chainIdInNum === 5000) { - gasLimitFromSimulation += 5000000000; - } - - const transactionId = generateTransactionId(Date.now().toString()); - log.info( - `transactionId: ${transactionId} for userOpHash: ${userOpHash} on chainId: ${chainIdInNum} for apiKey: ${dappAPIKey}`, - ); - const walletAddress = userOp.sender.toLowerCase(); - - transactionDao.save(chainIdInNum, { - transactionId, - transactionType: TransactionType.BUNDLER, - status: TransactionStatus.PENDING, - chainId: chainIdInNum, - walletAddress, - resubmitted: false, - creationTime: Date.now(), - }); + _log.info(`eth_sendUserOperation: received userOpHash: ${userOpHash}`); - log.info( - `Saving userOp state: ${UserOperationStateEnum.BUNDLER_MEMPOOL} for transactionId: ${transactionId} on chainId: ${chainIdInNum}`, - ); - userOperationStateDao.save(chainIdInNum, { - transactionId, - userOpHash, - state: UserOperationStateEnum.BUNDLER_MEMPOOL, - }); + logMeasureTime(_log, "eth_sendUserOperation", async () => { + const walletAddress = userOp.sender.toLowerCase(); - const { - sender, - nonce, - initCode, - callData, - callGasLimit, - verificationGasLimit, - preVerificationGas, - maxFeePerGas, - maxPriorityFeePerGas, - paymasterAndData, - signature, - } = userOp; - - const paymaster = getPaymasterFromPaymasterAndData(paymasterAndData); - - userOperationDao.save(chainIdInNum, { - transactionId, - dappAPIKey, - status: TransactionStatus.PENDING, - entryPoint: entryPointAddress, - sender, - nonce, - initCode, - callData, - callGasLimit, - verificationGasLimit, - preVerificationGas, - maxFeePerGas, - maxPriorityFeePerGas, - paymasterAndData, - signature, - userOpHash, - chainId: chainIdInNum, - paymaster, - creationTime: Date.now(), - }); + const relayService = + routeTransactionToRelayerMap[chainIdNumber][TransactionType.BUNDLER]; - if (!routeTransactionToRelayerMap[chainIdInNum][TransactionType.BUNDLER]) { - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // transactionId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.BAD_REQUEST, - // message: `${TransactionType.BUNDLER} method not supported for chainId: ${chainId}`, - // }, - // }, - // httpResponseCode: STATUSES.BAD_REQUEST, - // }); - const end = performance.now(); - log.info(`bundleUserOperation took: ${end - start} milliseconds`); - return res.status(STATUSES.BAD_REQUEST).json({ - jsonrpc: "2.0", - id: id || 1, - error: { - code: BUNDLER_ERROR_CODES.BAD_REQUEST, - message: `${TransactionType.BUNDLER} method not supported for chainId: ${chainId}`, - }, + if (!relayService) { + return res.status(STATUSES.BAD_REQUEST).json({ + jsonrpc: "2.0", + id: requestId, + error: { + code: BUNDLER_ERROR_CODES.BAD_REQUEST, + message: `${TransactionType.BUNDLER} method not supported for chainId: ${chainId}`, + }, + }); + } + + // Independent async calls can be concurrently executed to improve the performance + await Promise.all([ + saveInitialTransaction( + _log, + chainIdNumber, + transactionId, + walletAddress, + ), + saveInitialUserOperationState( + _log, + chainIdNumber, + transactionId, + userOpHash, + ), + saveInitialUserOperation( + _log, + transactionId, + dappAPIKey, + entryPointAddress, + userOpHash, + chainIdNumber, + userOp, + ) + ]); + + let gasLimit = gasLimitFromSimulation; + + // Add 5 Gwei to the gasLimit for MANTLE_MAINNET (for some reason?) + if (chainIdNumber === 5000) { + gasLimit += 5e9; + } + + // Add a markup to the gas limit if specified in the config + const configKey = `callGasLimitMarkup.${chainId}`; + if (config.has(configKey)) { + _log.info( + `Adding gas limit markup of ${config.get(configKey)}`, + ); + gasLimit += config.get(configKey); + } + + const response = routeTransactionToRelayerMap[chainIdNumber][ + TransactionType.BUNDLER + ].sendUserOperation({ + type: TransactionType.BUNDLER, + to: entryPointAddress, + data: "0x0", + gasLimit: `0x${Number(gasLimit).toString(16)}`, + chainId: chainIdNumber, + value: "0x0", + userOp, + transactionId, + walletAddress, }); - } - const response = routeTransactionToRelayerMap[chainIdInNum][ - TransactionType.BUNDLER - ].sendTransactionToRelayer({ - type: TransactionType.BUNDLER, - to: entryPointAddress, - data: "0x0", - gasLimit: `0x${Number(gasLimitFromSimulation).toString(16)}`, - chainId: chainIdInNum, - value: "0x0", - userOp, - transactionId, - walletAddress, - }); - if (isError(response)) { - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // transactionId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.BAD_REQUEST, - // message: response.error, - // }, - // }, - // httpResponseCode: STATUSES.BAD_REQUEST, - // }); - const end = performance.now(); - log.info(`bundleUserOperation took: ${end - start} milliseconds`); - return res.status(STATUSES.BAD_REQUEST).json({ + if (isError(response)) { + return res.status(STATUSES.BAD_REQUEST).json({ + jsonrpc: "2.0", + id: requestId, + error: { + code: BUNDLER_ERROR_CODES.BAD_REQUEST, + message: response.error, + }, + }); + } + + return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", - id: id || 1, - error: { - code: BUNDLER_ERROR_CODES.BAD_REQUEST, - message: response.error, - }, + id: requestId, + result: userOpHash, }); - } - - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // transactionId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: userOpHash, - // }, - // httpResponseCode: STATUSES.SUCCESS, - // }); - const end = performance.now(); - log.info(`bundleUserOperation took: ${end - start} milliseconds`); - return res.status(STATUSES.SUCCESS).json({ - jsonrpc: "2.0", - id: id || 1, - result: userOpHash, }); - } catch (error) { - const { id } = req.body; - log.error(`Error in bundle user op ${parseError(error)}`); - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); + } catch (err) { + _log.error({ err }, `eth_sendUserOperation: Unexpected error`); + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", - id: id || 1, + id: requestId, error: { code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - message: `Internal Server error: ${parseError(error)}`, + message: `Internal Server error: ${parseError(err)}`, }, }); } }; + +/** + * Saves the initial user operation state in the database + * @param _log Child logger with request specific data + * @param chainIdNumber Chain id number + * @param transactionId Unique transaction id + * @param userOpHash Hash of the user operation + */ +async function saveInitialUserOperationState( + _log: pino.Logger, + chainIdNumber: number, + transactionId: string, + userOpHash: string, +) { + _log.info( + `Saving userOp state: ${UserOperationStateEnum.BUNDLER_MEMPOOL} for transactionId: ${transactionId}`, + ); + + return userOperationStateDao.save(chainIdNumber, { + transactionId, + userOpHash, + state: UserOperationStateEnum.BUNDLER_MEMPOOL, + }); +} + +/** + * Saves the initial transaction in the database + * @param _log Child logger with request specific data + * @param chainIdNumber Chain id number + * @param transactionId Unique transaction id + * @param walletAddress Wallet address + */ +async function saveInitialTransaction( + _log: pino.Logger, + chainIdNumber: number, + transactionId: string, + walletAddress: Address, +) { + const data = { + transactionId, + transactionType: TransactionType.BUNDLER, + status: TransactionStatus.PENDING, + chainId: chainIdNumber, + walletAddress, + resubmitted: false, + creationTime: Date.now(), + }; + + _log.info(`Saving pending transaction: ${JSON.stringify(data)}`); + + return transactionDao.save(chainIdNumber, data); +} + +/** + * + * @param _log Child logger with request specific data + * @param transactionId Unique transaction id + * @param dappAPIKey Dapp API key + * @param entryPointAddress Address of the entry point + * @param userOpHash Hash of the user operation + * @param chainId Chain id + * @param userOperation User operation object + */ +async function saveInitialUserOperation( + _log: pino.Logger, + transactionId: string, + dappAPIKey: string, + entryPointAddress: Address, + userOpHash: string, + chainId: number, + // TODO: Add interface type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + userOperation: any, +) { + const { paymasterAndData } = userOperation; + const paymaster = getPaymasterFromPaymasterAndData(paymasterAndData); + + _log.info( + `Saving initial userOperation with userOpHash: ${userOpHash} and paymaster: ${paymaster}`, + ); + + userOperationDao.save(chainId, { + transactionId, + dappAPIKey, + status: TransactionStatus.PENDING, + entryPoint: entryPointAddress, + userOpHash, + chainId, + paymaster, + creationTime: Date.now(), + ...userOperation, + }); +} diff --git a/src/server/api/v2/eth_supportedEntryPoints/handler.ts b/src/server/api/v2/eth_supportedEntryPoints/handler.ts index 130d9e9e..7f787adf 100644 --- a/src/server/api/v2/eth_supportedEntryPoints/handler.ts +++ b/src/server/api/v2/eth_supportedEntryPoints/handler.ts @@ -1,18 +1,15 @@ -/* eslint-disable import/no-import-module-exports */ import { Request, Response } from "express"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../../shared/middleware"; import { logger } from "../../../../common/logger"; import { config } from "../../../../config"; import { parseError } from "../../../../common/utils"; -// import { updateRequest } from '../../auth/UpdateRequest'; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), }); export const getSupportedEntryPoints = async (req: Request, res: Response) => { - // const bundlerRequestId = req.body.params[6]; - try { const { id } = req.body; const { chainId /* apiKey */ } = req.params; @@ -32,18 +29,6 @@ export const getSupportedEntryPoints = async (req: Request, res: Response) => { } } - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // result: supportedEntryPoints, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); - return res.status(STATUSES.SUCCESS).json({ jsonrpc: "2.0", id: id || 1, @@ -52,20 +37,6 @@ export const getSupportedEntryPoints = async (req: Request, res: Response) => { } catch (error) { log.error(`Error in supportedEntryPoints handler ${parseError(error)}`); const { id } = req.body; - // updateRequest({ - // chainId: parseInt(chainId, 10), - // apiKey, - // bundlerRequestId, - // rawResponse: { - // jsonrpc: '2.0', - // id: id || 1, - // error: { - // code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, - // message: `Internal Server error: ${parseError(error)}`, - // }, - // }, - // httpResponseCode: STATUSES.INTERNAL_SERVER_ERROR, - // }); return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", id: id || 1, diff --git a/src/server/api/v2/handler.ts b/src/server/api/v2/handler.ts index 190b9ae2..ab68c779 100644 --- a/src/server/api/v2/handler.ts +++ b/src/server/api/v2/handler.ts @@ -1,13 +1,7 @@ import { Request, Response } from "express"; import config from "config"; import { - BiconomyMethodType, - EthMethodType, - TransactionMethodType, -} from "../../../common/types"; -import { BUNDLER_ERROR_CODES, STATUSES } from "../shared/middleware"; -import { - bundleUserOperation, + eth_sendUserOperation, getChainId, estimateUserOperationGas, getUserOperationByHash, @@ -19,6 +13,11 @@ import { } from "."; import { RPCErrorResponse } from "./shared/response"; import { ChainIdNotSupportedError } from "./shared/errors"; +import { BundlerMethods } from "../methods/bundler"; +import { STATUSES } from "../shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../shared/errors/codes"; +import { EthMethods } from "../methods/eth"; +import { BiconomyMethods } from "../methods/biconomy"; const isChainIdSupported = (chainId: number): boolean => { const supportedNetworks = config.get>("supportedNetworks"); @@ -37,32 +36,31 @@ export const handleV2Request = async (req: Request, res: Response) => { let response; switch (method) { - case TransactionMethodType.BUNDLER: - // here ideally it should add to mempool but would be bundling one user op per bundle - response = await bundleUserOperation(req, res); + case BundlerMethods.eth_sendUserOperation: + response = await eth_sendUserOperation(req, res); break; - case EthMethodType.ESTIMATE_USER_OPERATION_GAS: + case BundlerMethods.eth_estimateUserOperationGas: response = await estimateUserOperationGas(req, res); break; - case EthMethodType.GET_USER_OPERATION_BY_HASH: + case BundlerMethods.eth_getUserOperationByHash: response = await getUserOperationByHash(req, res); break; - case EthMethodType.GET_USER_OPERATION_RECEIPT: + case BundlerMethods.eth_getUserOperationReceipt: response = await getUserOperationReceipt(req, res); break; - case EthMethodType.SUPPORTED_ENTRY_POINTS: + case BundlerMethods.eth_supportedEntryPoints: response = await getSupportedEntryPoints(req, res); break; - case EthMethodType.CHAIN_ID: + case EthMethods.eth_chainId: response = await getChainId(req, res); break; - case EthMethodType.GET_USER_OPERATIONS_BY_API_KEY: + case BiconomyMethods.biconomy_getUserOperationsByApiKey: response = await getUserOperationsByApiKey(req, res); break; - case BiconomyMethodType.GET_GAS_FEE_VALUES: + case BiconomyMethods.biconomy_getGasFeeValues: response = await getGasFeeValues(req, res); break; - case BiconomyMethodType.GET_USER_OPERATION_STATUS: + case BiconomyMethods.biconomy_getUserOperationStatus: response = await getUserOperationStatus(req, res); break; default: diff --git a/src/server/api/v2/schema.ts b/src/server/api/v2/schema.ts index 1a5376d4..eb378274 100644 --- a/src/server/api/v2/schema.ts +++ b/src/server/api/v2/schema.ts @@ -46,7 +46,7 @@ const simulationTypeObj = object.keys({ simulation_type: string.valid("validation", "validation_and_execution"), }); -export const bundlerSendUserOpRequestSchema = object.keys({ +export const eth_sendUserOperationSchema = object.keys({ method: string.regex(/eth_sendUserOperation/), params: array.items( alternatives.try(userOpForSendUserOp, entryPointAddress, simulationTypeObj), @@ -100,7 +100,7 @@ const stateOverrideSet = object.pattern( }), ); -export const bundlerEstimateUserOpGasRequestSchema = object.keys({ +export const eth_estimateUserOperationGasSchema = object.keys({ method: string.regex(/eth_estimateUserOperationGas/), params: array.items( alternatives.try( @@ -116,67 +116,56 @@ export const bundlerEstimateUserOpGasRequestSchema = object.keys({ // eth_getUserOperationByHash const userOpHash = string.required().error(new Error("userOpHash is required")); -export const bundlerGetUserOpByHashRequestSchema = object.keys({ +export const eth_getUserOperationByHashSchema = object.keys({ method: string.regex(/eth_getUserOperationByHash/), params: array.items(alternatives.try(userOpHash)), jsonrpc: string.required().error(new Error("jsonrpc is required")), id: number.required().error(new Error("id is required")), }); -// eth_getUserOperationReceipt -export const bundlerGetUserOpReceiptRequestSchema = object.keys({ +export const eth_getUserOperationReceiptSchema = object.keys({ method: string.regex(/eth_getUserOperationReceipt/), params: array.items(alternatives.try(userOpHash)), jsonrpc: string.required().error(new Error("jsonrpc is required")), id: number.required().error(new Error("id is required")), }); -// biconomy_getUserOperationStatus -export const getUserOperationStatusSchema = object.keys({ +export const biconomy_getUserOperationStatusRequestSchema = object.keys({ method: string.regex(/biconomy_getUserOperationStatus/), params: array.items(alternatives.try(userOpHash)), jsonrpc: string.required().error(new Error("jsonrpc is required")), id: number.required().error(new Error("id is required")), }); -// eth_supportedEntryPoints -export const bundlerSupportedEntryPointsRequestSchema = object.keys({ +export const eth_supportedEntryPointsSchema = object.keys({ method: string.regex(/eth_supportedEntryPoints/), params: array, jsonrpc: string.required().error(new Error("jsonrpc is required")), id: number.required().error(new Error("id is required")), }); -// eth_chainId -export const bundlerChainIdRequestSchema = object.keys({ +export const eth_chainIdRequestSchema = object.keys({ method: string.regex(/eth_chainId/), params: array, jsonrpc: string.required().error(new Error("jsonrpc is required")), id: number.required().error(new Error("id is required")), }); -export const getGasFeeValuesRequestSchema = object.keys({ +export const biconomy_getGasFeeValuesRequestSchema = object.keys({ method: string.regex(/biconomy_getGasFeeValues/), params: array, jsonrpc: string.required().error(new Error("jsonrpc is required")), id: number.required().error(new Error("id is required")), }); -export const gasAndGasPricesRequestSchema = object.keys({ - method: string.regex(/eth_getUserOpGasFields/), - params: array, - jsonrpc: string.required().error(new Error("jsonrpc is required")), - id: number.required().error(new Error("id is required")), -}); - const getUserOperationsByApiKeyBody = object.keys({ startTime: string, endTime: string, limit: number, offset: number, }); -// eth_getUserOperationsByApiKey -export const bundlerGetUserOpsByApiKeyRequestSchema = object.keys({ + +export const biconomy_getUserOperationsByApiKeySchema = object.keys({ method: string.regex(/eth_getUserOperationsByApiKey/), params: array.items(alternatives.try(getUserOperationsByApiKeyBody)), jsonrpc: string.required().error(new Error("jsonrpc is required")), diff --git a/src/server/api/v2/shared/errors.ts b/src/server/api/v2/shared/errors.ts index 69aa5fa2..56c316dd 100644 --- a/src/server/api/v2/shared/errors.ts +++ b/src/server/api/v2/shared/errors.ts @@ -1,6 +1,5 @@ -/* eslint-disable max-classes-per-file */ import { parseError } from "../../../../common/utils"; -import { BUNDLER_ERROR_CODES } from "../../shared/middleware"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; export class RPCError { constructor( diff --git a/src/server/api/v2/shared/middleware.ts b/src/server/api/v2/shared/middleware.ts index 20d38d4e..0c9eee25 100644 --- a/src/server/api/v2/shared/middleware.ts +++ b/src/server/api/v2/shared/middleware.ts @@ -1,32 +1,13 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable no-case-declarations */ import { NextFunction, Request, Response } from "express"; import config from "config"; import { logger } from "../../../../common/logger"; -import { - EthMethodType, - BiconomyMethodType, - TransactionMethodType, -} from "../../../../common/types"; -import { - bundlerChainIdRequestSchema, - bundlerEstimateUserOpGasRequestSchema, - bundlerGetUserOpByHashRequestSchema, - bundlerGetUserOpReceiptRequestSchema, - bundlerGetUserOpsByApiKeyRequestSchema, - bundlerSendUserOpRequestSchema, - bundlerSupportedEntryPointsRequestSchema, - gasAndGasPricesRequestSchema, - getGasFeeValuesRequestSchema, - getUserOperationStatusSchema, -} from "../schema"; -import { - BUNDLER_ERROR_CODES, - STATUSES, -} from "../../shared/middleware/RequestHelpers"; + import { parseError } from "../../../../common/utils"; import { RPCErrorResponse } from "./response"; import { ChainIdNotSupportedError } from "./errors"; +import { methodToSchema } from "./schema"; +import { STATUSES } from "../../shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -49,7 +30,7 @@ export const validateChainId = ), ); } - } catch (err: any) { + } catch (err: unknown) { log.error(`Error in validateChainId: ${parseError(err)}`); return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ jsonrpc: "2.0", @@ -66,75 +47,37 @@ export const validateChainId = export const validateBundlerRequest = () => async (req: Request, res: Response, next: NextFunction) => { + const { method, id } = req.body; + const { chainId } = req.params; + + const _log = log.child({ + chainId, + method, + id, + }); + try { - const start = performance.now(); - const { method, id } = req.body; - let validationResponse; - switch (method) { - case TransactionMethodType.BUNDLER: - validationResponse = bundlerSendUserOpRequestSchema.validate( - req.body, - ); - break; - case EthMethodType.ESTIMATE_USER_OPERATION_GAS: - validationResponse = bundlerEstimateUserOpGasRequestSchema.validate( - req.body, - ); - break; - case EthMethodType.GET_USER_OPERATION_BY_HASH: - validationResponse = bundlerGetUserOpByHashRequestSchema.validate( - req.body, - ); - break; - case EthMethodType.GET_USER_OPERATION_RECEIPT: - validationResponse = bundlerGetUserOpReceiptRequestSchema.validate( - req.body, - ); - break; - case EthMethodType.SUPPORTED_ENTRY_POINTS: - validationResponse = - bundlerSupportedEntryPointsRequestSchema.validate(req.body); - break; - case EthMethodType.CHAIN_ID: - validationResponse = bundlerChainIdRequestSchema.validate(req.body); - break; - case EthMethodType.GAS_AND_GAS_PRICES: - validationResponse = gasAndGasPricesRequestSchema.validate(req.body); - break; - case EthMethodType.GET_USER_OPERATIONS_BY_API_KEY: - validationResponse = bundlerGetUserOpsByApiKeyRequestSchema.validate( - req.body, - ); - break; - case BiconomyMethodType.GET_GAS_FEE_VALUES: - validationResponse = getGasFeeValuesRequestSchema.validate(req.body); - break; - case BiconomyMethodType.GET_USER_OPERATION_STATUS: - validationResponse = getUserOperationStatusSchema.validate(req.body); - break; - default: - const end = performance.now(); - log.info(`validateBundlerRequest took ${end - start} milliseconds`); - return res.status(STATUSES.BAD_REQUEST).send({ - jsonrpc: "2.0", - id: id || 1, - error: { - code: BUNDLER_ERROR_CODES.METHOD_NOT_FOUND, - message: - "Wrong transaction type sent in validate BUNDLER request", - }, - }); + const validationSchema = methodToSchema[method]; + if (!validationSchema) { + _log.warn(`Method not supported: ${method}`); + return res.status(STATUSES.BAD_REQUEST).send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.METHOD_NOT_FOUND, + message: `Method not supported: ${method}`, + }, + }); } + + const validationResponse = validationSchema.validate(req.body); const { error } = validationResponse; const valid = error === undefined; if (valid) { - const end = performance.now(); - log.info(`validateBundlerRequest took ${end - start} milliseconds`); return next(); } - log.info( - `error from validation: ${parseError(error)} for method: ${method}`, - ); + + _log.debug({ error }, `Invalid request`); const { details } = error; let message; if (details) { @@ -144,8 +87,6 @@ export const validateBundlerRequest = } else { message = error.message || error.toString(); } - const end = performance.now(); - log.info(`validateBundlerRequest took ${end - start} milliseconds`); return res.send({ jsonrpc: "2.0", id: id || 1, @@ -154,9 +95,9 @@ export const validateBundlerRequest = message, }, }); - } catch (e: any) { + } catch (e: unknown) { const { id } = req.body; - log.error(e); + _log.error(e); return res.status(STATUSES.INTERNAL_SERVER_ERROR).send({ jsonrpc: "2.0", id: id || 1, diff --git a/src/server/api/v2/shared/response.ts b/src/server/api/v2/shared/response.ts index b0f87de4..ad35cf22 100644 --- a/src/server/api/v2/shared/response.ts +++ b/src/server/api/v2/shared/response.ts @@ -1,4 +1,4 @@ -/* eslint-disable max-classes-per-file */ + import { RPCError } from "./errors"; export abstract class RPCResponse { diff --git a/src/server/api/v2/shared/schema.ts b/src/server/api/v2/shared/schema.ts new file mode 100644 index 00000000..f139de3b --- /dev/null +++ b/src/server/api/v2/shared/schema.ts @@ -0,0 +1,33 @@ +import Joi from "joi"; + +import { + eth_chainIdRequestSchema, + eth_sendUserOperationSchema, + eth_getUserOperationReceiptSchema, + eth_supportedEntryPointsSchema, + eth_getUserOperationByHashSchema, + eth_estimateUserOperationGasSchema, + biconomy_getUserOperationsByApiKeySchema, + biconomy_getGasFeeValuesRequestSchema, + biconomy_getUserOperationStatusRequestSchema, +} from "../schema"; +import { BundlerMethods } from "../../methods/bundler"; +import { EthMethods } from "../../methods/eth"; +import { BiconomyMethods } from "../../methods/biconomy"; + +export const methodToSchema: Record = { + [BundlerMethods.eth_sendUserOperation]: eth_sendUserOperationSchema, + [BundlerMethods.eth_estimateUserOperationGas]: + eth_estimateUserOperationGasSchema, + [BundlerMethods.eth_getUserOperationByHash]: eth_getUserOperationByHashSchema, + [BundlerMethods.eth_getUserOperationReceipt]: + eth_getUserOperationReceiptSchema, + [BundlerMethods.eth_supportedEntryPoints]: eth_supportedEntryPointsSchema, + [EthMethods.eth_chainId]: eth_chainIdRequestSchema, + [BiconomyMethods.biconomy_getUserOperationsByApiKey]: + biconomy_getUserOperationsByApiKeySchema, + [BiconomyMethods.biconomy_getGasFeeValues]: + biconomy_getGasFeeValuesRequestSchema, + [BiconomyMethods.biconomy_getUserOperationStatus]: + biconomy_getUserOperationStatusRequestSchema, +} as const; diff --git a/src/server/api/v3/biconomy_getGasFeeValues/handler.ts b/src/server/api/v3/biconomy_getGasFeeValues/handler.ts new file mode 100644 index 00000000..4dffbaef --- /dev/null +++ b/src/server/api/v3/biconomy_getGasFeeValues/handler.ts @@ -0,0 +1,67 @@ +import { Request, Response } from "express"; +import { toHex } from "viem"; +import { logger } from "../../../../common/logger"; +import { gasPriceServiceMap } from "../../../../common/service-manager"; +import { customJSONStringify, parseError } from "../../../../common/utils"; +import { STATUSES } from "../../shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +export const getGasFeeValues = async (req: Request, res: Response) => { + try { + const { id } = req.body; + const { chainId /* apiKey */ } = req.params; + + const gasPrice = await gasPriceServiceMap[Number(chainId)]?.getGasPrice(); + + let maxFeePerGas: bigint; + let maxPriorityFeePerGas: bigint; + + if (typeof gasPrice !== "bigint") { + log.info( + `Gas price for chainId: ${chainId} is: ${customJSONStringify( + gasPrice, + )}`, + ); + maxFeePerGas = gasPrice?.maxFeePerGas || 1n; + maxPriorityFeePerGas = gasPrice?.maxPriorityFeePerGas || 1n; + } else { + maxFeePerGas = gasPrice; + maxPriorityFeePerGas = gasPrice; + } + + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: id || 1, + result: { + slow: { + maxPriorityFeePerGas: toHex(maxPriorityFeePerGas)?.toString(), + maxFeePerGas: toHex(maxFeePerGas)?.toString(), + }, + standard: { + maxPriorityFeePerGas: toHex(maxPriorityFeePerGas)?.toString(), + maxFeePerGas: toHex(maxFeePerGas)?.toString(), + }, + fast: { + maxPriorityFeePerGas: toHex(maxPriorityFeePerGas)?.toString(), + maxFeePerGas: toHex(maxFeePerGas)?.toString(), + }, + }, + }); + } catch (error) { + log.error(`Error in getGasFeeValues handler ${parseError(error)}`); + const { id } = req.body; + + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + message: `Internal Server error: ${parseError(error)}`, + }, + }); + } +}; diff --git a/src/server/api/v3/biconomy_getUserOperationStatus/handler.ts b/src/server/api/v3/biconomy_getUserOperationStatus/handler.ts new file mode 100644 index 00000000..e410b582 --- /dev/null +++ b/src/server/api/v3/biconomy_getUserOperationStatus/handler.ts @@ -0,0 +1,219 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { customJSONStringify, parseError } from "../../../../common/utils"; +import { + userOperationV07Dao, + userOperationStateDao, +} from "../../../../common/service-manager"; +import { UserOperationStateEnum } from "../../../../common/types"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +const getUserOperationStateData = async ( + chainId: number, + userOpHash: string, +): Promise<{ + error: boolean; + result: unknown; +}> => { + log.info( + `Getting userOp state for userOpHash: ${userOpHash} on chainId: ${chainId}`, + ); + const userOperationStateData = await userOperationStateDao.get( + chainId, + userOpHash, + ); + log.info( + `userOperationStateData: ${customJSONStringify( + userOperationStateData, + )} for userOpHash: ${userOpHash} on chainId: ${chainId}`, + ); + + if (userOperationStateData == null) { + log.info(`userOpHash: ${userOpHash} not found for chainId: ${chainId}`); + return { + error: true, + result: { + code: BUNDLER_ERROR_CODES.USER_OP_HASH_NOT_FOUND, + message: `UserOpHash: ${userOpHash} not found for chainId: ${chainId}`, + }, + }; + } + + if ( + userOperationStateData.state === + UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL + ) { + log.info( + `userOp dropped from mempool with userOpHash: ${userOpHash} on chainId: ${chainId}`, + ); + return { + error: false, + result: { + state: UserOperationStateEnum.DROPPED_FROM_BUNDLER_MEMPOOL, + }, + }; + } + + if (userOperationStateData.state === UserOperationStateEnum.BUNDLER_MEMPOOL) { + log.info( + `userOp with userOpHash: ${userOpHash} is in ${UserOperationStateEnum.BUNDLER_MEMPOOL} state on chainId: ${chainId}`, + ); + return { + error: false, + result: { + state: UserOperationStateEnum.BUNDLER_MEMPOOL, + }, + }; + } + + if (userOperationStateData.state === UserOperationStateEnum.SUBMITTED) { + log.info( + `userOp with userOpHash: ${userOpHash} is in ${UserOperationStateEnum.SUBMITTED} state on chainId: ${chainId}`, + ); + return { + error: false, + result: { + state: UserOperationStateEnum.SUBMITTED, + transactionHash: userOperationStateData.transactionHash, + }, + }; + } + + if (userOperationStateData.state === UserOperationStateEnum.FAILED) { + log.info( + `userOp with userOpHash: ${userOpHash} is in ${UserOperationStateEnum.FAILED} state on chainId: ${chainId}`, + ); + return { + error: false, + result: { + state: UserOperationStateEnum.FAILED, + message: userOperationStateData.message, + transactionHash: userOperationStateData.transactionHash, + }, + }; + } + + if (userOperationStateData.state === UserOperationStateEnum.CONFIRMED) { + log.info( + `userOp with userOpHash: ${userOpHash} is in ${UserOperationStateEnum.CONFIRMED} state on chainId: ${chainId}`, + ); + log.info( + `Fetching userOpReceipt for userOpHash: ${userOpHash} on chainId: ${chainId}`, + ); + const userOperationData = + await userOperationV07Dao.getUserOperationDataByUserOpHash( + chainId, + userOpHash, + ); + log.info( + `userOperationData: ${customJSONStringify( + userOperationData, + )} for userOpHash: ${userOpHash} on chainId: ${chainId}`, + ); + + if (userOperationData === null) { + log.info(`userOpHash: ${userOpHash} not found for chainId: ${chainId}`); + return { + error: true, + result: { + code: BUNDLER_ERROR_CODES.USER_OP_HASH_NOT_FOUND, + message: `UserOpHash: ${userOpHash} not found for chainId: ${chainId}`, + }, + }; + } + + const { + entryPoint, + sender, + nonce, + success, + paymaster, + actualGasCost, + actualGasUsed, + logs, + receipt, + reason, + } = userOperationData; + + const result = { + userOpHash, + entryPoint, + sender, + nonce, + success, + paymaster, + actualGasCost, + actualGasUsed, + reason, + logs, + receipt, + }; + + log.info( + `userOp with userOpHash: ${userOpHash} is in ${UserOperationStateEnum.CONFIRMED} state on chainId: ${chainId}`, + ); + return { + error: false, + result: { + state: UserOperationStateEnum.CONFIRMED, + message: userOperationStateData.message, + transactionHash: userOperationStateData.transactionHash, + userOperationReceipt: result, + }, + }; + } + + log.info( + `No state matched for userOpHash: ${userOpHash} on chainId: ${chainId}`, + ); + return { + error: true, + result: { + code: BUNDLER_ERROR_CODES.UNABLE_TO_PROCESS_USER_OP, + message: `Unable to process userOp with UserOpHash: ${userOpHash} on chainId: ${chainId}`, + }, + }; +}; + +export const getUserOperationStatus = async (req: Request, res: Response) => { + try { + const { id } = req.body; + const { chainId /* apiKey */ } = req.params; + const userOpHash = req.body.params[0]; + + const userOperationStateData = await getUserOperationStateData( + parseInt(chainId, 10), + userOpHash, + ); + + if (userOperationStateData.error) { + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: id || 1, + error: userOperationStateData.result, + }); + } + + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: 1, + result: userOperationStateData.result, + }); + } catch (error) { + log.error(`Error in getUserOperationStatus handler: ${parseError(error)}`); + const { id } = req.body; + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + message: `Internal Server error: ${parseError(error)}`, + }, + }); + } +}; diff --git a/src/server/api/v3/eth_chainId/handler.test.ts b/src/server/api/v3/eth_chainId/handler.test.ts new file mode 100644 index 00000000..d989e558 --- /dev/null +++ b/src/server/api/v3/eth_chainId/handler.test.ts @@ -0,0 +1,35 @@ +import httpMocks, { RequestOptions } from "node-mocks-http"; +import { getChainId } from "./handler"; + +const chainId = "80001"; +const requestId = "123"; + +describe("eth_chainId", () => { + it("should return chainId in hex if the chain is supported", async () => { + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v2/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [], + }, + }; + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await getChainId(request, response); + + // console.log(response._getJSONData()); + + // Check the returned values + expect(response.statusCode).toBe(200); + expect(response._getJSONData()).toEqual({ + jsonrpc: "2.0", + id: requestId, + result: "0x13881", // 80001 in hex + }); + }); +}); diff --git a/src/server/api/v3/eth_chainId/handler.ts b/src/server/api/v3/eth_chainId/handler.ts new file mode 100644 index 00000000..45926856 --- /dev/null +++ b/src/server/api/v3/eth_chainId/handler.ts @@ -0,0 +1,41 @@ +import { Request, Response } from "express"; +import { Hex } from "viem"; +import { logger } from "../../../../common/logger"; +import { customJSONStringify } from "../../../../common/utils"; +import { InternalServerError } from "../shared/errors"; +import { ChainIdResponse } from "./response"; +import { RPCErrorResponse } from "../shared/response"; +import { STATUSES } from "../../shared/statuses"; + +const filenameLogger = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +// TODO: Use the network service to actually check if that chainId is supported and RPC works +export const getChainId = async (req: Request, res: Response) => { + const { id } = req.body; + const { chainId, apiKey } = req.params; + + const log = filenameLogger.child({ + chainId, + requestId: id, + apiKey, + }); + + try { + log.info(`chainId in number: ${chainId}`); + + const chainIdInHex: Hex = `0x${parseInt(chainId, 10).toString(16)}`; + log.info(`chainId in hex: ${chainIdInHex}`); + + return res + .status(STATUSES.SUCCESS) + .json(new ChainIdResponse(chainIdInHex, id)); + } catch (error) { + log.error(`Error in getChainId handler ${customJSONStringify(error)}`); + + return res + .status(STATUSES.INTERNAL_SERVER_ERROR) + .json(new RPCErrorResponse(new InternalServerError(error), id)); + } +}; diff --git a/src/server/api/v3/eth_chainId/response.ts b/src/server/api/v3/eth_chainId/response.ts new file mode 100644 index 00000000..8aa4a2ca --- /dev/null +++ b/src/server/api/v3/eth_chainId/response.ts @@ -0,0 +1,11 @@ +import { Hex } from "viem"; +import { RPCResponse } from "../shared/response"; + +export class ChainIdResponse extends RPCResponse { + constructor( + public result: Hex, + requestId?: number, + ) { + super(requestId); + } +} diff --git a/src/server/api/v3/eth_estimateUserOperationGas/errors.ts b/src/server/api/v3/eth_estimateUserOperationGas/errors.ts new file mode 100644 index 00000000..71c85ed4 --- /dev/null +++ b/src/server/api/v3/eth_estimateUserOperationGas/errors.ts @@ -0,0 +1,32 @@ +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { RPCError } from "../shared/errors"; + +export class EntryPointNotSupportedError extends RPCError { + constructor(entryPointAddress: string) { + super( + BUNDLER_ERROR_CODES.BAD_REQUEST, + `Entry point with entryPointAddress: ${entryPointAddress} not supported by the Bundler. + Please make sure that the given entryPointAddress is correct`, + ); + } +} + +export class ChainIdNotSupportedError extends RPCError { + constructor(chainId: string) { + super( + BUNDLER_ERROR_CODES.BAD_REQUEST, + `Can't estimate user operations gas for chainId: ${chainId}. + Please make sure that the chainId is correct and supported by our Bundler`, + ); + } +} + +export class GasPriceError extends RPCError { + constructor(chainId: string) { + super( + BUNDLER_ERROR_CODES.BAD_REQUEST, + `Don't know how to fetch gas price for chainId: ${chainId}. + Please make sure that the chainId is correct and supported by our Bundler`, + ); + } +} diff --git a/src/server/api/v3/eth_estimateUserOperationGas/handler.test.ts b/src/server/api/v3/eth_estimateUserOperationGas/handler.test.ts new file mode 100644 index 00000000..36db81c9 --- /dev/null +++ b/src/server/api/v3/eth_estimateUserOperationGas/handler.test.ts @@ -0,0 +1,416 @@ +import httpMocks, { RequestOptions } from "node-mocks-http"; + +const entryPointAddress = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789"; +const entryPointContract = { + address: entryPointAddress, +}; +const chainId = "1"; +const requestId = "123"; + +// These mock functions are used in tests to control what is returned by other services the endpoint depends on +const estimateUserOperationGasMock = jest.fn(); +const getGasPriceMock = jest.fn(); + +// πŸ’‘ These import mocks have to happen before we call import { estimateUserOperationGas } +// because estimateUserOperationGas imports the service manager produces SIDE EFFECTS (bad practice) like trying to connect to the DB +jest.mock("../../../../common/service-manager", () => ({ + entryPointMapV07: { + 1: [entryPointContract], + 2: [entryPointContract], + 3: [entryPointContract], + }, + bundlerSimulationServiceMapV07: { + 1: { + estimateUserOperationGas: estimateUserOperationGasMock, + }, + 3: { + estimateUserOperationGas: estimateUserOperationGasMock, + }, + }, + gasPriceServiceMap: { + 1: { + getGasPrice: getGasPriceMock, + }, + }, +})); + +// Now we can import after we have mocked the dependencies +import { estimateUserOperationGas } from "./handler"; + +describe("EstimateUserOperationGas", () => { + afterEach(() => { + estimateUserOperationGasMock.mockClear(); + getGasPriceMock.mockClear(); + }); + + // This test acts as a CONTRACT test for the most common use case, ensuring that the returned value has the expected format + test("happy path: gas price is a bigint", async () => { + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [ + {}, // userOp + entryPointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Mock responses from other services + estimateUserOperationGasMock.mockReturnValueOnce({ + code: 200, + data: { + callGasLimit: 1000n, + verificationGasLimit: 1001n, + preVerificationGas: 1002n, + validUntil: 1003n, + validAfter: 1004n, + }, + }); + getGasPriceMock.mockReturnValue(1005n); + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + // console.log(response._getJSONData()); + + // Check the returned values + expect(response.statusCode).toBe(200); + + expect(response._getJSONData()).toEqual({ + jsonrpc: "2.0", + id: "123", + result: { + callGasLimit: 1000, + verificationGasLimit: 1001, + preVerificationGas: 1002, + maxPriorityFeePerGas: 1005, + maxFeePerGas: 1005, + paymasterPostOpGasLimit: null, + paymasterVerificationGasLimit: null, + }, + }); + + // Make sure that the dependencies were called with the expected values + expect(estimateUserOperationGasMock).toHaveBeenCalledWith({ + userOp: {}, + entryPointContract, + chainId: 1, + stateOverrideSet: {}, + }); + + expect(getGasPriceMock).toHaveBeenCalledTimes(1); + }); + + test("happy path: gas price returned is an object", async () => { + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [ + {}, // userOp + entryPointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Mock responses from other services + estimateUserOperationGasMock.mockReturnValueOnce({ + code: 200, + data: { + callGasLimit: 1000n, + verificationGasLimit: 1001n, + preVerificationGas: 1002n, + validUntil: 1003n, + validAfter: 1004n, + }, + }); + getGasPriceMock.mockReturnValue({ + maxPriorityFeePerGas: 1005n, + maxFeePerGas: 1006n, + }); + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + // console.log(response._getJSONData()); + + // Check the returned values + expect(response.statusCode).toBe(200); + + expect(response._getJSONData()).toEqual({ + jsonrpc: "2.0", + id: "123", + result: { + callGasLimit: 1000, + verificationGasLimit: 1001, + preVerificationGas: 1002, + maxPriorityFeePerGas: 1005, + maxFeePerGas: 1006, + paymasterPostOpGasLimit: null, + paymasterVerificationGasLimit: null, + }, + }); + + // Make sure that the dependencies were called with the expected values + expect(estimateUserOperationGasMock).toHaveBeenCalledWith({ + userOp: {}, + entryPointContract, + chainId: 1, + stateOverrideSet: {}, + }); + + expect(getGasPriceMock).toHaveBeenCalledTimes(1); + }); + + test("unsupported entrypoint", async () => { + const unknownEntrypointAddress = "0xunknown"; + + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [ + {}, // userOp + unknownEntrypointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + expect(response.statusCode).toBe(400); + + const responseData = response._getJSONData(); + + expect( + responseData.error.message.startsWith( + `Entry point with entryPointAddress: ${unknownEntrypointAddress} not supported by the Bundler`, + ), + ); + + expect(responseData.id).toBe(requestId); + }); + + test("can't estimateUserOperationGas for chainId", async () => { + const unsupportedChainId = "2"; + + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${unsupportedChainId}/test`, + params: { chainId: unsupportedChainId }, + body: { + id: requestId, + params: [ + {}, // userOp + entryPointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + // console.log(response._getJSONData()); + + expect(response.statusCode).toBe(400); + + const responseData = response._getJSONData(); + expect( + responseData.error.message.startsWith( + "Can't estimate user operations gas", + ), + ).toBe(true); + + expect(responseData.id).toBe(requestId); + }); + + test("estimateUserOperationGas throws an error", async () => { + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [ + {}, // userOp + entryPointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Mock responses from other services + const err = "Simulator error"; + estimateUserOperationGasMock.mockImplementationOnce(() => { + throw new Error(err); + }); + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + // console.log(response._getJSONData()); + + expect(response.statusCode).toBe(500); + expect(response._getJSONData().error.message.includes(err)).toBe(true); + expect(response._getJSONData().error.code).toBe(-32002); + expect(response._getJSONData().id).toBe(requestId); + }); + + test("estimateUserOperationGas returns an error code", async () => { + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [ + {}, // userOp + entryPointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Mock responses from other services + const errorCode = 123; // whatever is not 200 + estimateUserOperationGasMock.mockReturnValueOnce({ + code: errorCode, + message: "simulator message", + }); + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + // console.log(response._getJSONData()); + + expect(response.statusCode).toBe(400); + expect( + response._getJSONData().error.message.includes("simulator message"), + ).toBe(true); + expect(response._getJSONData().error.code).toBe(errorCode); + expect(response._getJSONData().id).toBe(requestId); + }); + + test("can't get gas price for chain id", async () => { + // chainId = 3 has an entrypoint and simulation service, but gas price service is undefined + const targetChainId = "3"; + + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${targetChainId}/test`, + params: { chainId: targetChainId }, + body: { + id: requestId, + params: [ + {}, // userOp + entryPointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Mock responses from other services + estimateUserOperationGasMock.mockReturnValueOnce({ + code: 200, + data: { + callGasLimit: 1000n, + verificationGasLimit: 1001n, + preVerificationGas: 1002n, + validUntil: 1003n, + validAfter: 1004n, + }, + }); + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + // console.log(response._getJSONData()); + + expect(response.statusCode).toBe(400); + expect( + response + ._getJSONData() + .error.message.startsWith("Don't know how to fetch gas price"), + ).toBe(true); + expect(response._getJSONData().id).toBe(requestId); + }); + + test("fetch gas price throws an error", async () => { + // Create the RPC request + const requestOptions: RequestOptions = { + method: "POST", + url: `/api/v3/${chainId}/test`, + params: { chainId }, + body: { + id: requestId, + params: [ + {}, // userOp + entryPointAddress, + {}, // stateOverrideSet + ], + }, + }; + + // Mock responses from other services + estimateUserOperationGasMock.mockReturnValueOnce({ + code: 200, + data: { + callGasLimit: 1000n, + verificationGasLimit: 1001n, + preVerificationGas: 1002n, + validUntil: 1003n, + validAfter: 1004n, + }, + }); + + const err = "Gas price error"; + getGasPriceMock.mockImplementationOnce(() => { + throw new Error(err); + }); + + // Send the request + const request = httpMocks.createRequest(requestOptions); + const response = httpMocks.createResponse(); + await estimateUserOperationGas(request, response); + + // console.log(response._getJSONData()); + + expect(response.statusCode).toBe(500); + expect(response._getJSONData().error.message.includes(err)).toBe(true); + expect(response._getJSONData().error.code).toBe(-32002); + expect(response._getJSONData().id).toBe(requestId); + }); +}); diff --git a/src/server/api/v3/eth_estimateUserOperationGas/handler.ts b/src/server/api/v3/eth_estimateUserOperationGas/handler.ts new file mode 100644 index 00000000..a26aed51 --- /dev/null +++ b/src/server/api/v3/eth_estimateUserOperationGas/handler.ts @@ -0,0 +1,133 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { + bundlerSimulationServiceMapV07, + entryPointMapV07, + gasPriceServiceMap, +} from "../../../../common/service-manager"; +import { customJSONStringify, parseError } from "../../../../common/utils"; +import { + EntryPointNotSupportedError, + ChainIdNotSupportedError, + GasPriceError, +} from "./errors"; +import { EstimateUserOperationGasResponse } from "./response"; +import { InternalServerError, RPCError } from "../shared/errors"; +import { RPCErrorResponse } from "../shared/response"; +import { STATUSES } from "../../shared/statuses"; + +const filenameLogger = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +export const estimateUserOperationGas = async (req: Request, res: Response) => { + const { id } = req.body; + const { chainId, apiKey } = req.params; + + // create a child logger so all important tracing info is logged with each call + const log = filenameLogger.child({ + chainId, + requestId: id, + apiKey, + }); + + log.info( + `EP v7 estimateUserOperationGas called, entryPointAddress: ${req.body.params[1]}`, + ); + + try { + const [userOp, entryPointAddress, stateOverrideSet] = req.body.params; + + // Check if given entrypoint is supported by our bundler + const entryPointContracts = entryPointMapV07[parseInt(chainId, 10)]; + const entryPointContract = entryPointContracts?.find( + (e) => e.address.toLowerCase() === entryPointAddress.toLowerCase(), + ); + + if (!entryPointContract) { + return res + .status(STATUSES.BAD_REQUEST) + .json( + new RPCErrorResponse( + new EntryPointNotSupportedError(entryPointAddress), + id, + ), + ); + } + + // Check if given chain id is supported by our bundler + const simulator = bundlerSimulationServiceMapV07[parseInt(chainId, 10)]; + if (!simulator) { + return res + .status(STATUSES.BAD_REQUEST) + .json(new RPCErrorResponse(new ChainIdNotSupportedError(chainId), id)); + } + + // Estimate gas for the user operation using the simulator + const estimatedUserOpGas = await simulator.estimateUserOperationGas({ + userOp, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + entryPointContract: entryPointContract as any, + chainId: parseInt(chainId, 10), + stateOverrideSet, + }); + + const { code, message, data } = estimatedUserOpGas; + if (code !== STATUSES.SUCCESS) { + return res + .status(STATUSES.BAD_REQUEST) + .json(new RPCErrorResponse(new RPCError(code, message), id)); + } + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + paymasterPostOpGasLimit, + paymasterVerificationGasLimit, + } = data; + const gasPriceService = gasPriceServiceMap[parseInt(chainId, 10)]; + if (!gasPriceService) { + return res + .status(STATUSES.BAD_REQUEST) + .json(new RPCErrorResponse(new GasPriceError(chainId), id)); + } + + const gasPrice = await gasPriceService.getGasPrice(); + + let maxPriorityFeePerGas: string; + let maxFeePerGas: string; + if (typeof gasPrice !== "bigint") { + log.info( + `Gas price for chainId: ${chainId} is: ${customJSONStringify( + gasPrice, + )}`, + ); + + maxPriorityFeePerGas = + gasPrice?.maxPriorityFeePerGas?.toString() as string; + + maxFeePerGas = gasPrice?.maxFeePerGas?.toString() as string; + } else { + maxPriorityFeePerGas = gasPrice?.toString() as string; + maxFeePerGas = gasPrice?.toString() as string; + } + const response = new EstimateUserOperationGasResponse({ + callGasLimit: Number(callGasLimit), + verificationGasLimit: Number(verificationGasLimit), + preVerificationGas: Number(preVerificationGas), + paymasterPostOpGasLimit: Number(paymasterPostOpGasLimit), + paymasterVerificationGasLimit: Number(paymasterVerificationGasLimit), + maxPriorityFeePerGas: Number(maxPriorityFeePerGas), + maxFeePerGas: Number(maxFeePerGas), + }); + response.id = id; + + return res.status(STATUSES.SUCCESS).json(response); + } catch (error) { + log.error(`Error in estimateUserOperationGas handler ${parseError(error)}`); + return res + .status(STATUSES.INTERNAL_SERVER_ERROR) + .json(new RPCErrorResponse(new InternalServerError(error), id)); + } +}; diff --git a/src/server/api/v3/eth_estimateUserOperationGas/response.ts b/src/server/api/v3/eth_estimateUserOperationGas/response.ts new file mode 100644 index 00000000..18694575 --- /dev/null +++ b/src/server/api/v3/eth_estimateUserOperationGas/response.ts @@ -0,0 +1,17 @@ +import { RPCResponse } from "../shared/response"; + +export class EstimateUserOperationGasResponse extends RPCResponse { + constructor( + public result = { + callGasLimit: 0, + verificationGasLimit: 0, + preVerificationGas: 0, + paymasterPostOpGasLimit: 0, + paymasterVerificationGasLimit: 0, + maxPriorityFeePerGas: 0, + maxFeePerGas: 0, + }, + ) { + super(); + } +} diff --git a/src/server/api/v3/eth_getUserOperationByHash/handler.ts b/src/server/api/v3/eth_getUserOperationByHash/handler.ts new file mode 100644 index 00000000..34cf6a86 --- /dev/null +++ b/src/server/api/v3/eth_getUserOperationByHash/handler.ts @@ -0,0 +1,95 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { userOperationV07Dao } from "../../../../common/service-manager"; +import { parseError } from "../../../../common/utils"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +/** + * null in case the UserOperation is not yet included in a block, + * or a full UserOperation, with + * the addition of entryPoint, blockNumber, blockHash and transactionHash + */ +export const getUserOperationByHash = async (req: Request, res: Response) => { + // const bundlerRequestId = req.body.params[6]; + + try { + const { id } = req.body; + const { chainId /* apiKey */ } = req.params; + + const userOpHash = req.body.params[0]; + + const userOperation = + await userOperationV07Dao.getUserOperationDataByUserOpHash( + parseInt(chainId, 10), + userOpHash, + ); + + if (!userOperation || !userOperation.transactionHash) { + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: id || 1, + result: null, + }); + } + + const { + sender, + nonce, + callData, + callGasLimit, + preVerificationGas, + verificationGasLimit, + paymaster, + factory, + factoryData, + maxFeePerGas, + maxPriorityFeePerGas, + signature, + transactionHash, + blockNumber, + blockHash, + entryPoint, + } = userOperation; + + const result = { + sender, + nonce, + factory, + factoryData, + callData, + callGasLimit, + preVerificationGas, + verificationGasLimit, + paymaster, + maxFeePerGas, + maxPriorityFeePerGas, + signature, + entryPoint, + transactionHash, + blockNumber, + blockHash, + }; + + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: 1, + result, + }); + } catch (error) { + log.error(`Error in getUserOperationByHash handler ${parseError(error)}`); + const { id } = req.body; + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + message: `Internal Server error: ${parseError(error)}`, + }, + }); + } +}; diff --git a/src/server/api/v3/eth_getUserOperationReceipt/handler.ts b/src/server/api/v3/eth_getUserOperationReceipt/handler.ts new file mode 100644 index 00000000..7bdb7fc9 --- /dev/null +++ b/src/server/api/v3/eth_getUserOperationReceipt/handler.ts @@ -0,0 +1,91 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { userOperationV07Dao } from "../../../../common/service-manager"; +import { parseError } from "../../../../common/utils"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +/** + * userOpHash the request hash + entryPoint + sender + nonce + paymaster the paymaster used for this userOp (or empty) + actualGasCost - actual amount paid (by account or paymaster) for this UserOperation + actualGasUsed - total gas used by this UserOperation (including preVerification, creation, validation and execution) + success boolean - did this execution completed without revert + reason in case of revert, this is the revert reason + logs the logs generated by this UserOperation (not including logs of other UserOperations in the same bundle) + receipt the TransactionReceipt object. Note that the returned TransactionReceipt is for the entire bundle, not only for this UserOperation. + */ +export const getUserOperationReceipt = async (req: Request, res: Response) => { + // const bundlerRequestId = req.body.params[6]; + + try { + const { id } = req.body; + const { chainId /* apiKey */ } = req.params; + const userOpHash = req.body.params[0]; + + const userOperationData = + await userOperationV07Dao.getUserOperationDataByUserOpHash( + parseInt(chainId, 10), + userOpHash, + ); + + if (!userOperationData || !userOperationData.receipt) { + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: id || 1, + result: null, + }); + } + + const { + entryPoint, + sender, + nonce, + success, + paymaster, + actualGasCost, + actualGasUsed, + logs, + receipt, + reason, + } = userOperationData; + + const result = { + userOpHash, + entryPoint, + sender, + nonce, + success, + paymaster, + actualGasCost, + actualGasUsed, + reason, + logs, + receipt, + }; + + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: 1, + result, + }); + } catch (error) { + log.error(`Error in getUserOperationReceipt handler: ${parseError(error)}`); + const { id } = req.body; + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + message: `Internal Server error: ${parseError(error)}`, + }, + }); + } +}; diff --git a/src/server/api/v3/eth_getUserOperationsByApiKey/handler.ts b/src/server/api/v3/eth_getUserOperationsByApiKey/handler.ts new file mode 100644 index 00000000..8ae45323 --- /dev/null +++ b/src/server/api/v3/eth_getUserOperationsByApiKey/handler.ts @@ -0,0 +1,225 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { userOperationV07Dao } from "../../../../common/service-manager"; +import { parseError } from "../../../../common/utils"; +import { STATUSES } from "../../shared/statuses"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +const getStartTimeAndEndTimeInMs = ( + startTime: string | null, + endTime: string | null, +) => { + let startTimeInMs: number; + let endTimeInMs: number; + + if (startTime === null && endTime === null) { + log.info("startTime & endTime both are null"); + startTimeInMs = 0; + endTimeInMs = Date.now(); + return { + startTimeInMs, + endTimeInMs, + }; + } + + if (startTime !== null && endTime === null) { + log.info(`startTime: ${startTime}`); + log.info("endTime is null"); + if (!Number.isNaN(Date.parse(startTime))) { + startTimeInMs = Date.parse(startTime); + } else { + startTimeInMs = 0; + } + endTimeInMs = Date.now(); + return { + startTimeInMs, + endTimeInMs, + }; + } + + if (startTime === null && endTime !== null) { + log.info(`endTime: ${endTime}`); + log.info("startTime is null"); + if (!Number.isNaN(Date.parse(endTime))) { + endTimeInMs = Date.parse(endTime); + } else { + endTimeInMs = Date.now(); + } + startTimeInMs = 0; + return { + startTimeInMs, + endTimeInMs, + }; + } + + if (startTime !== null && endTime !== null) { + log.info(`startTime: ${startTime}`); + log.info(`endTime: ${endTime}`); + if (!Number.isNaN(Date.parse(endTime))) { + endTimeInMs = Date.parse(endTime); + } else { + endTimeInMs = Date.now(); + } + if (!Number.isNaN(Date.parse(startTime))) { + startTimeInMs = Date.parse(startTime); + } else { + startTimeInMs = 0; + } + return { + startTimeInMs, + endTimeInMs, + }; + } + + return { + startTimeInMs: 0, + endTimeInMs: Date.now(), + }; +}; + +/** + * it should give a lit of all userOps by the apiKey + * data should be a combination of userOp data + receipt + */ +export const getUserOperationsByApiKey = async ( + req: Request, + res: Response, +) => { + try { + const { chainId, apiKey } = req.params; + const startTime = req.body.params[0]; + const endTime = req.body.params[1]; + let limit = req.body.params[2]; + let offSet = req.body.params[3]; + + const { startTimeInMs, endTimeInMs } = getStartTimeAndEndTimeInMs( + startTime, + endTime, + ); + log.info( + `startTimeInMs: ${startTimeInMs}, endTimeInMs: ${endTimeInMs} for apiKey: ${apiKey} on chainId: ${chainId}`, + ); + + if (typeof limit !== "number" || ![10, 25, 100].includes(limit)) { + limit = 10; + } + + if (typeof offSet !== "number") { + offSet = 0; + } + + log.info( + `getUserOperationsByApiKey request received for apiKey: ${apiKey} on chainId: ${chainId}`, + ); + + const userOperationsData = + await userOperationV07Dao.getUserOperationsDataByApiKey( + parseInt(chainId, 10), + apiKey, + startTimeInMs, + endTimeInMs, + limit, + offSet, + ); + + if (!userOperationsData) { + log.info( + `User operations data could not for apiKey: ${apiKey} on chainId: ${chainId}`, + ); + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: 1, + result: `User operations data could not for apiKey: ${apiKey} on chainId: ${chainId}`, + }); + } + + const totalUserOperationsCount = + await userOperationV07Dao.getUserOperationsCountByApiKey( + parseInt(chainId, 10), + apiKey, + 0, + Date.now(), + ); + + log.info( + `totalUserOperationsCount: ${totalUserOperationsCount} for apiKey: ${apiKey} on chainId: ${chainId}`, + ); + + const userOpsData = []; + + for (let index = 0; index < userOperationsData.length; index += 1) { + const { + sender, + nonce, + factory, + factoryData, + callData, + callGasLimit, + preVerificationGas, + verificationGasLimit, + paymaster, + maxFeePerGas, + maxPriorityFeePerGas, + signature, + transactionHash, + blockNumber, + blockHash, + entryPoint, + success, + actualGasCost, + actualGasUsed, + logs, + receipt, + reason, + } = userOperationsData[index]; + + userOpsData.push({ + sender, + nonce, + factory, + factoryData, + callData, + callGasLimit, + preVerificationGas, + verificationGasLimit, + maxFeePerGas, + maxPriorityFeePerGas, + signature, + transactionHash, + blockNumber, + blockHash, + entryPoint, + success, + paymaster, + actualGasCost, + actualGasUsed, + logs, + receipt, + reason, + }); + } + + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: 1, + result: { + totalNumOfUserOps: totalUserOperationsCount, + numOfUserOps: userOpsData.length, + limit, + offSet, + userOpsData, + }, + }); + } catch (error) { + log.error( + `Error in getUserOperationsByApiKey handler ${parseError(error)}`, + ); + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + code: STATUSES.INTERNAL_SERVER_ERROR, + error: `Internal Server Error: ${parseError(error)}`, + }); + } +}; diff --git a/src/server/api/v3/eth_sendUserOperation/handler.ts b/src/server/api/v3/eth_sendUserOperation/handler.ts new file mode 100644 index 00000000..2b9e1bae --- /dev/null +++ b/src/server/api/v3/eth_sendUserOperation/handler.ts @@ -0,0 +1,173 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { + transactionDao, + userOperationV07Dao, + userOperationStateDao, + routeTransactionToRelayerMap, +} from "../../../../common/service-manager"; +import { + generateTransactionId, + getPaymasterFromPaymasterAndDataV7, + parseError, +} from "../../../../common/utils"; +import { + isError, + TransactionStatus, + TransactionType, + UserOperationStateEnum, +} from "../../../../common/types"; +import { STATUSES } from "../../shared/statuses"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +export const bundleUserOperation = async (req: Request, res: Response) => { + try { + const start = performance.now(); + const { id } = req.body; + const userOp = req.body.params[0]; + const entryPointAddress = req.body.params[1]; + let gasLimitFromSimulation = req.body.params[2] + 500000; + const userOpHash = req.body.params[3]; + const { chainId, apiKey } = req.params; + + const chainIdInNum = parseInt(chainId, 10); + if (chainIdInNum === 5000) { + gasLimitFromSimulation += 5000000000; + } + + if (!routeTransactionToRelayerMap[chainIdInNum][TransactionType.BUNDLER]) { + const end = performance.now(); + log.info(`bundleUserOperation took: ${end - start} milliseconds`); + return res.status(STATUSES.BAD_REQUEST).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.BAD_REQUEST, + message: `${TransactionType.BUNDLER} method not supported for chainId: ${chainId}`, + }, + }); + } + + const transactionId = generateTransactionId(Date.now().toString()); + log.info( + `transactionId: ${transactionId} for userOpHash: ${userOpHash} on chainId: ${chainIdInNum} for apiKey: ${apiKey}`, + ); + const walletAddress = userOp.sender.toLowerCase(); + + transactionDao.save(chainIdInNum, { + transactionId, + transactionType: TransactionType.BUNDLER, + status: TransactionStatus.PENDING, + chainId: chainIdInNum, + walletAddress, + resubmitted: false, + creationTime: Date.now(), + }); + + log.info( + `Saving userOp state: ${UserOperationStateEnum.BUNDLER_MEMPOOL} for transactionId: ${transactionId} on chainId: ${chainIdInNum}`, + ); + + userOperationStateDao.save(chainIdInNum, { + transactionId, + userOpHash, + state: UserOperationStateEnum.BUNDLER_MEMPOOL, + }); + + const { + sender, + nonce, + callData, + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + paymasterAndData, + signature, + paymasterData, + factory, + factoryData, + paymasterPostOpGasLimit, + paymasterVerificationGasLimit, + } = userOp; + + const paymaster = getPaymasterFromPaymasterAndDataV7(paymasterAndData); + + userOperationV07Dao.save(chainIdInNum, { + transactionId, + apiKey, + status: TransactionStatus.PENDING, + entryPoint: entryPointAddress, + sender, + nonce, + callData, + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + signature, + userOpHash, + chainId: chainIdInNum, + paymaster, + paymasterData, + paymasterPostOpGasLimit, + paymasterVerificationGasLimit, + factory, + factoryData, + creationTime: Date.now(), + }); + + const response = routeTransactionToRelayerMap[chainIdInNum][ + TransactionType.BUNDLER + ].sendUserOperation({ + type: TransactionType.BUNDLER, + to: entryPointAddress, + data: "0x0", + gasLimit: `0x${Number(gasLimitFromSimulation).toString(16)}`, + chainId: chainIdInNum, + value: "0x0", + userOp, + transactionId, + walletAddress, + }); + + if (isError(response)) { + const end = performance.now(); + log.info(`bundleUserOperation took: ${end - start} milliseconds`); + return res.status(STATUSES.BAD_REQUEST).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.BAD_REQUEST, + message: response.error, + }, + }); + } + + const end = performance.now(); + log.info(`bundleUserOperation took: ${end - start} milliseconds`); + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: id || 1, + result: userOpHash, + }); + } catch (error) { + const { id } = req.body; + log.error(`Error in bundle user op ${parseError(error)}`); + + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + message: `Internal Server error: ${parseError(error)}`, + }, + }); + } +}; diff --git a/src/server/api/v3/eth_supportedEntryPoints/handler.ts b/src/server/api/v3/eth_supportedEntryPoints/handler.ts new file mode 100644 index 00000000..5025f4ca --- /dev/null +++ b/src/server/api/v3/eth_supportedEntryPoints/handler.ts @@ -0,0 +1,49 @@ +import { Request, Response } from "express"; +import { logger } from "../../../../common/logger"; +import { config } from "../../../../config"; +import { parseError } from "../../../../common/utils"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; +import { STATUSES } from "../../shared/statuses"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +export const getSupportedEntryPoints = async (req: Request, res: Response) => { + try { + const { id } = req.body; + const { chainId /* apiKey */ } = req.params; + + log.info(`chainId: ${chainId}`); + + const chainIdInInt = parseInt(chainId, 10); + const { entryPointV07Data } = config; + + const supportedEntryPoints = []; + + for (const [entryPointAddress, chainIds] of Object.entries( + entryPointV07Data, + )) { + if (chainIds.supportedChainIds.includes(chainIdInInt)) { + supportedEntryPoints.push(entryPointAddress); + } + } + + return res.status(STATUSES.SUCCESS).json({ + jsonrpc: "2.0", + id: id || 1, + result: supportedEntryPoints, + }); + } catch (error) { + log.error(`Error in supportedEntryPoints handler ${parseError(error)}`); + const { id } = req.body; + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + message: `Internal Server error: ${parseError(error)}`, + }, + }); + } +}; diff --git a/src/server/api/v3/handler.ts b/src/server/api/v3/handler.ts new file mode 100644 index 00000000..dca28071 --- /dev/null +++ b/src/server/api/v3/handler.ts @@ -0,0 +1,73 @@ +import { Request, Response } from "express"; +import config from "config"; +import { + bundleUserOperation, + getChainId, + estimateUserOperationGas, + getUserOperationByHash, + getUserOperationReceipt, + getSupportedEntryPoints, + getGasFeeValues, + getUserOperationStatus, +} from "."; +import { RPCErrorResponse } from "./shared/response"; +import { ChainIdNotSupportedError } from "./shared/errors"; +import { STATUSES } from "../shared/statuses"; +import { BundlerMethods } from "../methods/bundler"; +import { EthMethods } from "../methods/eth"; +import { BiconomyMethods } from "../methods/biconomy"; +import { BUNDLER_ERROR_CODES } from "../shared/errors/codes"; + +const isChainIdSupported = (chainId: number): boolean => { + const supportedNetworksV07 = config.get>( + "supportedNetworksV07", + ); + return supportedNetworksV07.includes(chainId); +}; + +export const handleV3Request = async (req: Request, res: Response) => { + const { method, id } = req.body; + const { chainId } = req.params; + + if (!isChainIdSupported(parseInt(chainId, 10))) { + return res + .status(STATUSES.BAD_REQUEST) + .json(new RPCErrorResponse(new ChainIdNotSupportedError(chainId), id)); + } + + let response; + switch (method) { + case BundlerMethods.eth_sendUserOperation: + // here ideally it should add to mempool but would be bundling one user op per bundle + response = await bundleUserOperation(req, res); + break; + case BundlerMethods.eth_estimateUserOperationGas: + response = await estimateUserOperationGas(req, res); + break; + case BundlerMethods.eth_getUserOperationByHash: + response = await getUserOperationByHash(req, res); + break; + case BundlerMethods.eth_getUserOperationReceipt: + response = await getUserOperationReceipt(req, res); + break; + case BundlerMethods.eth_supportedEntryPoints: + response = await getSupportedEntryPoints(req, res); + break; + case EthMethods.eth_chainId: + response = await getChainId(req, res); + break; + case BiconomyMethods.biconomy_getGasFeeValues: + response = await getGasFeeValues(req, res); + break; + case BiconomyMethods.biconomy_getUserOperationStatus: + response = await getUserOperationStatus(req, res); + break; + default: + return res.status(STATUSES.BAD_REQUEST).send({ + code: BUNDLER_ERROR_CODES.METHOD_NOT_FOUND, + error: `method: ${method} not supported`, + }); + } + + return response; +}; diff --git a/src/server/api/v3/index.ts b/src/server/api/v3/index.ts new file mode 100644 index 00000000..ae6715e3 --- /dev/null +++ b/src/server/api/v3/index.ts @@ -0,0 +1,8 @@ +export * from "./eth_sendUserOperation/handler"; +export * from "./eth_chainId/handler"; +export * from "./eth_estimateUserOperationGas/handler"; +export * from "./eth_getUserOperationByHash/handler"; +export * from "./eth_getUserOperationReceipt/handler"; +export * from "./eth_supportedEntryPoints/handler"; +export * from "./biconomy_getGasFeeValues/handler"; +export * from "./biconomy_getUserOperationStatus/handler"; diff --git a/src/server/api/v3/router.ts b/src/server/api/v3/router.ts new file mode 100644 index 00000000..8ab30fa0 --- /dev/null +++ b/src/server/api/v3/router.ts @@ -0,0 +1,14 @@ +import { Router } from "express"; +import { simulateBundlerV3Transaction } from "../shared/simulate"; +import { handleV3Request } from "./handler"; +import { validateBundlerRequest } from "./shared/middleware"; + +export const v3Router = Router(); + +v3Router.post( + "/:chainId/:apiKey", + validateBundlerRequest(), + simulateBundlerV3Transaction(), + handleV3Request, +); +v3Router.get("/:chainId/:apiKey", validateBundlerRequest(), handleV3Request); diff --git a/src/server/api/v3/schema.ts b/src/server/api/v3/schema.ts new file mode 100644 index 00000000..8e7b3dd2 --- /dev/null +++ b/src/server/api/v3/schema.ts @@ -0,0 +1,172 @@ +import Joi from "joi"; + +const { number, object, string, alternatives, array } = Joi.types(); + +// eth_sendUserOperation +const userOpForSendUserOp = object.keys({ + sender: string + .regex(/^0x[a-fA-F0-9]{40}$/) + .required() + .error(new Error("sender address is required")), + nonce: string + .required() + .error(new Error("nonce is required and should be a string")), + factory: string, + factoryData: string, + callData: string + .required() + .error(new Error("callData is required and should be a hex string")), + callGasLimit: string + .required() + .error(new Error("callGasLimit is required and should be a string")), + verificationGasLimit: string + .required() + .error( + new Error("verificationGasLimit is required and should be a string"), + ), + preVerificationGas: string + .required() + .error(new Error("preVerificationGas is required and should be a string")), + maxFeePerGas: string + .required() + .error(new Error("maxFeePerGas is required and should be a string")), + maxPriorityFeePerGas: string + .required() + .error(new Error("maxFeePerGas is required and should be a string")), + paymaster: string, + paymasterVerificationGasLimit: string, + paymasterPostOpGasLimit: string, + paymasterData: string, + signature: string + .required() + .error(new Error("signature is required and should be a string")), +}); + +const entryPointAddress = string + .required() + .error(new Error("entryPointAddress is required")); + +const simulationTypeObj = object.keys({ + simulation_type: string.valid("validation", "validation_and_execution"), +}); + +export const bundlerSendUserOpRequestSchema = object.keys({ + method: string.regex(/eth_sendUserOperation/), + params: array.items( + alternatives.try(userOpForSendUserOp, entryPointAddress, simulationTypeObj), + ), + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +// eth_estimateUserOperationGas +/** + * Parameters: same as eth_sendUserOperation gas limits (and prices) parameters are optional, + * but are used if specified. + * maxFeePerGas and maxPriorityFeePerGas default to zero, + * so no payment is required by neither account nor paymaster. + */ +const userOpEstimateUserOpGas = object.keys({ + sender: string + .regex(/^0x[a-fA-F0-9]{40}$/) + .required() + .error(new Error("sender address is required")), + nonce: string + .required() + .error(new Error("nonce is required and should be a string")), + factory: string, + factoryData: string, + callData: string + .required() + .error(new Error("callData is required and should be a hex string")), + callGasLimit: string, + verificationGasLimit: string, + preVerificationGas: string, + maxFeePerGas: string, + maxPriorityFeePerGas: string, + paymaster: string, + paymasterVerificationGasLimit: string, + paymasterPostOpGasLimit: string, + paymasterData: string, + signature: string, +}); + +const stateOverrideSet = object.pattern( + string, + object.keys({ + balance: string, + nonce: string, + code: string, + state: object, + stateDiff: object, + }), +); + +export const bundlerEstimateUserOpGasRequestSchema = object.keys({ + method: string.regex(/eth_estimateUserOperationGas/), + params: array.items( + alternatives.try( + userOpEstimateUserOpGas, + entryPointAddress, + stateOverrideSet, + ), + ), + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +// eth_getUserOperationByHash +const userOpHash = string.required().error(new Error("userOpHash is required")); + +export const bundlerGetUserOpByHashRequestSchema = object.keys({ + method: string.regex(/eth_getUserOperationByHash/), + params: array.items(alternatives.try(userOpHash)), + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +// eth_getUserOperationReceipt +export const bundlerGetUserOpReceiptRequestSchema = object.keys({ + method: string.regex(/eth_getUserOperationReceipt/), + params: array.items(alternatives.try(userOpHash)), + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +// biconomy_getUserOperationStatus +export const getUserOperationStatusSchema = object.keys({ + method: string.regex(/biconomy_getUserOperationStatus/), + params: array.items(alternatives.try(userOpHash)), + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +// eth_supportedEntryPoints +export const bundlerSupportedEntryPointsRequestSchema = object.keys({ + method: string.regex(/eth_supportedEntryPoints/), + params: array, + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +// eth_chainId +export const bundlerChainIdRequestSchema = object.keys({ + method: string.regex(/eth_chainId/), + params: array, + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +export const getGasFeeValuesRequestSchema = object.keys({ + method: string.regex(/biconomy_getGasFeeValues/), + params: array, + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); + +export const gasAndGasPricesRequestSchema = object.keys({ + method: string.regex(/eth_getUserOpGasFields/), + params: array, + jsonrpc: string.required().error(new Error("jsonrpc is required")), + id: number.required().error(new Error("id is required")), +}); diff --git a/src/server/api/v3/shared/errors.ts b/src/server/api/v3/shared/errors.ts new file mode 100644 index 00000000..56c316dd --- /dev/null +++ b/src/server/api/v3/shared/errors.ts @@ -0,0 +1,27 @@ +import { parseError } from "../../../../common/utils"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; + +export class RPCError { + constructor( + public code = BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + public message = "Internal Server Error", + ) {} +} + +export class InternalServerError extends RPCError { + constructor(error: unknown) { + super( + BUNDLER_ERROR_CODES.INTERNAL_SERVER_ERROR, + `Internal Server Error: ${parseError(error)}`, + ); + } +} + +export class ChainIdNotSupportedError extends RPCError { + constructor(chainId: string) { + super( + BUNDLER_ERROR_CODES.BAD_REQUEST, + `chainId: ${chainId} not supported by the Bundler`, + ); + } +} diff --git a/src/server/api/v3/shared/middleware.ts b/src/server/api/v3/shared/middleware.ts new file mode 100644 index 00000000..bb0e5b91 --- /dev/null +++ b/src/server/api/v3/shared/middleware.ts @@ -0,0 +1,155 @@ +/* eslint-disable no-case-declarations */ +import { NextFunction, Request, Response } from "express"; +import config from "config"; +import { logger } from "../../../../common/logger"; +import { + bundlerChainIdRequestSchema, + bundlerEstimateUserOpGasRequestSchema, + bundlerGetUserOpByHashRequestSchema, + bundlerGetUserOpReceiptRequestSchema, + bundlerSendUserOpRequestSchema, + bundlerSupportedEntryPointsRequestSchema, + getGasFeeValuesRequestSchema, + getUserOperationStatusSchema, +} from "../schema"; +import { parseError } from "../../../../common/utils"; +import { RPCErrorResponse } from "./response"; +import { ChainIdNotSupportedError } from "./errors"; +import { STATUSES } from "../../shared/statuses"; +import { BundlerMethods } from "../../methods/bundler"; +import { EthMethods } from "../../methods/eth"; +import { BiconomyMethods } from "../../methods/biconomy"; +import { BUNDLER_ERROR_CODES } from "../../shared/errors/codes"; + +const log = logger.child({ + module: module.filename.split("/").slice(-4).join("/"), +}); + +// validateChainId middleware checks if the chainId provided in the request is supported by this bundler +export const validateChainId = + () => async (req: Request, res: Response, next: NextFunction) => { + try { + const supportedNetworks = config.get>("supportedNetworks"); + const chainId = parseInt(req.params.chainId, 10); + + if (chainId && !supportedNetworks.includes(chainId)) { + return res + .status(STATUSES.BAD_REQUEST) + .json( + new RPCErrorResponse( + new ChainIdNotSupportedError(req.params.chainId), + 0, + ), + ); + } + } catch (err: unknown) { + log.error(`Error in validateChainId: ${parseError(err)}`); + return res.status(STATUSES.INTERNAL_SERVER_ERROR).json({ + jsonrpc: "2.0", + id: 0, + error: { + code: STATUSES.INTERNAL_SERVER_ERROR, + message: parseError(err), + }, + }); + } + + return next(); + }; + +export const validateBundlerRequest = + () => async (req: Request, res: Response, next: NextFunction) => { + try { + const start = performance.now(); + const { method, id } = req.body; + let validationResponse; + log.info(`Received method: ${method}`); + switch (method) { + case BundlerMethods.eth_sendUserOperation: + validationResponse = bundlerSendUserOpRequestSchema.validate( + req.body, + ); + break; + case BundlerMethods.eth_estimateUserOperationGas: + validationResponse = bundlerEstimateUserOpGasRequestSchema.validate( + req.body, + ); + break; + case BundlerMethods.eth_getUserOperationByHash: + validationResponse = bundlerGetUserOpByHashRequestSchema.validate( + req.body, + ); + break; + case BundlerMethods.eth_getUserOperationReceipt: + validationResponse = bundlerGetUserOpReceiptRequestSchema.validate( + req.body, + ); + break; + case BundlerMethods.eth_supportedEntryPoints: + validationResponse = + bundlerSupportedEntryPointsRequestSchema.validate(req.body); + break; + case EthMethods.eth_chainId: + validationResponse = bundlerChainIdRequestSchema.validate(req.body); + break; + case BiconomyMethods.biconomy_getGasFeeValues: + validationResponse = getGasFeeValuesRequestSchema.validate(req.body); + break; + case BiconomyMethods.biconomy_getUserOperationStatus: + validationResponse = getUserOperationStatusSchema.validate(req.body); + break; + default: + const end = performance.now(); + log.info(`validateBundlerRequest took ${end - start} milliseconds`); + return res.status(STATUSES.BAD_REQUEST).send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.METHOD_NOT_FOUND, + message: + "Wrong transaction type sent in validate BUNDLER request", + }, + }); + } + const { error } = validationResponse; + const valid = error === undefined; + if (valid) { + const end = performance.now(); + log.info(`validateBundlerRequest took ${end - start} milliseconds`); + return next(); + } + log.info( + `error from validation: ${parseError(error)} for method: ${method}`, + ); + const { details } = error; + let message; + if (details) { + message = details + .map((i) => (i.context ? i.context.message : i.message)) + .join(","); + } else { + message = error.message || error.toString(); + } + const end = performance.now(); + log.info(`validateBundlerRequest took ${end - start} milliseconds`); + return res.send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: BUNDLER_ERROR_CODES.INVALID_USER_OP_FIELDS, + message, + }, + }); + } catch (e: unknown) { + const { id } = req.body; + log.error(e); + return res.status(STATUSES.INTERNAL_SERVER_ERROR).send({ + jsonrpc: "2.0", + id: id || 1, + error: { + code: STATUSES.INTERNAL_SERVER_ERROR, + message: parseError(e), + }, + }); + } + }; diff --git a/src/server/api/v3/shared/response.ts b/src/server/api/v3/shared/response.ts new file mode 100644 index 00000000..ad35cf22 --- /dev/null +++ b/src/server/api/v3/shared/response.ts @@ -0,0 +1,18 @@ + +import { RPCError } from "./errors"; + +export abstract class RPCResponse { + constructor( + public id = 1, + public jsonrpc = "2.0", + ) {} +} + +export class RPCErrorResponse extends RPCResponse { + constructor( + public error: RPCError, + requestId?: number, + ) { + super(requestId); + } +} diff --git a/src/server/app.ts b/src/server/app.ts index 7a004a95..3674107b 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -1,11 +1,6 @@ import rTracer from "cls-rtracer"; import cors from "cors"; -import express, { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - NextFunction, - Request, - Response, -} from "express"; +import express, { NextFunction, Request, Response } from "express"; import cons from "consolidate"; import logger from "pino-http"; import { randomUUID } from "node:crypto"; @@ -14,6 +9,7 @@ import { routes } from "./api/router"; const app = express(); declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace namespace Express { interface Request { userId: string; @@ -45,6 +41,7 @@ app.set("view engine", "hbs"); app.use( ( req: Request, + // eslint-disable-next-line @typescript-eslint/no-explicit-any res: { setHeader: (arg0: string, arg1: any) => void }, next: NextFunction, ) => { @@ -89,6 +86,7 @@ app.route("/:chainId/health").get((req, res) => { // error handler app.use( ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any err: any, req: Request, res: Response, diff --git a/src/server/index.ts b/src/server/index.ts index 774c5c4a..12d79a96 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,6 +1,6 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable import/first */ -require("dotenv").config({ path: `${__dirname}/../../.env` }); +import dotenv from "dotenv"; + +dotenv.config({ path: `${__dirname}/../../.env` }); import tracer from "dd-trace"; import { logger } from "../common/logger"; diff --git a/src/server/server.ts b/src/server/server.ts index d16eff60..ed064aa9 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -1,9 +1,8 @@ -/* eslint-disable import/no-import-module-exports */ import http from "http"; import { logger } from "../common/logger"; import app from "./app"; -let server: any; +let server: http.Server; const log = logger.child({ module: module.filename.split("/").slice(-4).join("/"), @@ -30,6 +29,7 @@ const port = normalizePort(process.env.PORT || "3000"); * Event listener for HTTP server "error" event. */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any function onError(error: any) { if (error.syscall !== "listen") { throw error; @@ -56,7 +56,7 @@ function onError(error: any) { function onListening() { const addr = server.address(); - const bind = typeof addr === "string" ? `pipe ${addr}` : `port ${addr.port}`; + const bind = typeof addr === "string" ? `pipe ${addr}` : `port ${addr?.port}`; log.info(`App listening on ${bind}`); } diff --git a/src/test/e2e/api/fee-option.test.ts b/src/test/e2e/api/fee-option.test.ts deleted file mode 100644 index 8e2b548b..00000000 --- a/src/test/e2e/api/fee-option.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -describe("broken test suite", () => { - it("should be fixed", () => {}); -}); -// import { RedisCacheService } from '../../../common/cache'; -// import { GasPriceManager } from '../../../common/gas-price'; -// import { EVMNetworkService } from '../../../common/network'; -// import { config } from '../../../config'; -// import { FeeOption } from '../../../server/src/services'; - -// const chainId = 5; -// const cacheService = RedisCacheService.getInstance(); -// const networkService = new EVMNetworkService({ -// chainId, -// rpcUrl: config.chains.provider[chainId], -// fallbackRpcUrls: config.chains.fallbackUrls[chainId] || [], -// }); - -// // test feeoption endpoint -// describe('get fee options', () => { -// beforeAll(async () => { -// await cacheService.connect(); -// }); - -// it(`should return fee options for chainId ${chainId}`, async () => { -// const gasPriceManager = new GasPriceManager(cacheService, networkService, { -// chainId, -// EIP1559SupportedNetworks: config.EIP1559SupportedNetworks, -// }); -// const gasPriceService = gasPriceManager.setup(); -// expect(gasPriceService).toBeDefined(); -// if (gasPriceService) { -// const feeOptionService = new FeeOption(gasPriceService, cacheService, { -// chainId, -// }); -// const { response, code } = await feeOptionService.get(); - -// expect(code).toBe(200); -// expect(response).toBeDefined(); - -// if (response) { -// expect(Array.isArray(response)).toBe(true); -// expect(response.length).toBeGreaterThan(0); - -// expect(response[0]).toHaveProperty('tokenGasPrice'); -// expect(response[0]).toHaveProperty('symbol'); -// expect(response[0]).toHaveProperty('decimal'); -// expect(response[0]).toHaveProperty('offset'); -// expect(response[0]).toHaveProperty('address'); -// expect(response[0]).toHaveProperty('logoUrl'); -// expect(response[0]).toHaveProperty('feeTokenTransferGas'); -// expect(response[0]).toHaveProperty('refundReceiver'); - -// expect(typeof response[0].tokenGasPrice).toBe('number'); -// expect(typeof response[0].symbol).toBe('string'); -// expect(typeof response[0].decimal).toBe('number'); -// expect(typeof response[0].offset).toBe('number'); -// expect(typeof response[0].address).toBe('string'); -// expect(typeof response[0].logoUrl).toBe('string'); -// expect(typeof response[0].feeTokenTransferGas).toBe('number'); -// expect(typeof response[0].refundReceiver).toBe('string'); - -// expect(response[0].symbol).toBe('ETH'); -// expect(response[0].decimal).toBe(18); -// expect(response[0].offset).toBe(1); -// expect(response[0].address).toBe( -// '0x0000000000000000000000000000000000000000', -// ); -// expect(response[0].logoUrl).toBe( -// 'https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/eth.png', -// ); -// expect(response[0].feeTokenTransferGas).toBe(7300); -// expect(response[0].refundReceiver).toBe( -// '0xc75Bb3956c596efc6DB663cd3e2f64929d6AB0fc', -// ); -// } -// } -// }); - -// afterAll(async () => { -// await cacheService.close(); -// }); -// }); diff --git a/src/test/e2e/api/relay-api.test.ts b/src/test/e2e/api/relay-api.test.ts deleted file mode 100644 index ab48ade4..00000000 --- a/src/test/e2e/api/relay-api.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -describe("Relay API", () => { - it("should return a list of relays", async () => { - const d = 10; - expect(d).toBe(10); - }); -}); diff --git a/src/test/e2e/gas-price/gas-price.test.ts b/src/test/e2e/gas-price/gas-price.test.ts deleted file mode 100644 index c20c98ce..00000000 --- a/src/test/e2e/gas-price/gas-price.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -describe("disabled test", () => { - it("should be fixed", () => {}); -}); - -// import { RedisCacheService } from "../../../common/cache"; -// import { -// GasPriceManager, -// GasPriceServiceType, -// } from "../../../common/gas-price"; -// import { GasPriceType } from "../../../common/gas-price/types"; -// import { EVMNetworkService } from "../../../common/network"; -// import { config } from "../../../config"; - -// const chainId = 80001; - -// const cacheService = RedisCacheService.getInstance(); - -// const networkServiceMap: { -// [chainId: number]: EVMNetworkService; -// } = {}; -// const gasPriceServiceMap: { -// [chainId: number]: GasPriceServiceType; -// } = {}; - -// networkServiceMap[chainId] = new EVMNetworkService({ -// chainId, -// rpcUrl: config.chains.provider[chainId], -// }); - -// const gasPriceManager = new GasPriceManager( -// cacheService, -// networkServiceMap[chainId], -// { -// chainId, -// EIP1559SupportedNetworks: config.EIP1559SupportedNetworks, -// }, -// ); -// gasPriceServiceMap[chainId] = gasPriceManager.setup(); - -// // create test in jest to check if gas price are updating correctly -// describe("Gas Price", () => { -// beforeAll(async () => { -// await cacheService.connect(); -// }); - -// it(`should update gas price in redis for chainId: ${chainId}`, async () => { -// await gasPriceServiceMap[chainId]?.setGasPrice( -// GasPriceType.DEFAULT, -// "1000000000", -// ); -// }); - -// it(`should get gas price from redis for chainId: ${chainId}`, async () => { -// const gasPrice = await gasPriceServiceMap[chainId]?.getGasPrice( -// GasPriceType.DEFAULT, -// ); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe("string"); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// expect(gasPrice).toBe("1000000000"); -// }); - -// afterAll(async () => { -// await cacheService.close(); -// }); -// }); diff --git a/src/test/e2e/nativeTransfer.test.ts b/src/test/e2e/nativeTransfer.test.ts new file mode 100644 index 00000000..7d81ceb6 --- /dev/null +++ b/src/test/e2e/nativeTransfer.test.ts @@ -0,0 +1,1511 @@ +import { + createWalletClient, + http, + PrivateKeyAccount, + publicActions, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { + base, + avalanche, + optimism, + polygon, + bsc, + arbitrum, + gnosis, + blast, + baseSepolia, + berachainTestnetbArtio, + sepolia, +} from "viem/chains"; +import { + BiconomySmartAccountV2, + createSmartAccountClient, + UserOperationStruct, +} from "@biconomy/account"; +import { createNexusClient, getCustomChain } from "@biconomy/sdk"; +import axios, { AxiosError } from "axios"; + +/** + * This file contains end-to-end tests that can run against a local or production bundler instance. + * The tests are disabled by default because they send real transactions and require real keys, URLs and balance in the smart account. + * To run the tests, you need to set the following environment variables: + * - PRIVATE_KEY: the private key of the account that will send the transactions + * - BUNDLER_HOSTNAME: the hostname of the bundler instance + * + * If you want to test any of the chains you have to delete the 'skip' statement + * and provide any of the following environment variables, depending on the chain you want to test: + * - BASE_MAINNET_PAYMASTER_URL: the paymaster URL for the base chain + * - OPTIMISM_MAINNET_PAYMASTER_URL: the paymaster URL for the optimism chain + * - AVALANCHE_MAINNET_PAYMASTER_URL: the paymaster URL for the avalanche chain + * - POLYGON_MAINNET_PAYMASTER_URL: the paymaster URL for the polygon chain + * - BSC_MAINNET_PAYMASTER_URL: the paymaster URL for the bsc chain + * - ARBITRUM_MAINNET_PAYMASTER_URL: the paymaster URL for the arbitrum chain + * + * You can refer to .env.example file for the environment variables. + * + * To run the tests, you can use the following command: + * - yarn test:e2e + * + * πŸ”₯ Don't commit un-skipped e2e tests so someone doesn't run them by accident πŸ”₯ + */ +describe("e2e", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (BigInt.prototype as any).toJSON = function () { + return this.toString(); + }; + + const privateKey = process.env.PRIVATE_KEY; + if (!privateKey) { + throw new Error("PRIVATE_KEY is not defined"); + } + const bundlerHostname = process.env.BUNDLER_HOSTNAME; + if (!bundlerHostname) { + throw new Error("BUNDLER_HOSTNAME is not defined"); + } + + const valueToSend = 1n; // send only 1 wei + + const stubPaymasterAndData = + "0x00000f79b7faf42eebadba19acc07cd08af4478900000000000000000000000029c3e9456c0eca5beb1f78763204bac6c682407700000000000000000000000000000000000000000000000000000000676410e60000000000000000000000000000000000000000000000000000000067640fba0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004170f9cfb3f7a4204e0a497c8d19a98e2157b0df2740019ba2250a4c73de30539f14ea86cc47e0c96c16fd61b4b2f7ef777cf2c306974a03d56bdcc9313040242b1c00000000000000000000000000000000000000000000000000000000000000"; + + describe.skip("base-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${base.id}/biconomy`; + + const paymasterUrl = process.env.BASE_MAINNET_PAYMASTER_URL; + + const signer = createWalletClient({ + account, + chain: base, + transport: http(), + }).extend(publicActions); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: signer, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("BASE_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${base.id}/test`; + + logConfig(base.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: base, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + + console.log(`${base.name} EPv0.7.0 txHash: ${receipt.transactionHash}`); + console.log(receipt); + + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("base-sepolia", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${baseSepolia.id}/test`; + + const paymasterUrl = process.env.BASE_SEPOLIA_PAYMASTER_URL; + + const signer = createWalletClient({ + account, + chain: baseSepolia, + transport: http(), + }).extend(publicActions); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: signer, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, 1n); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("BASE_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${baseSepolia.id}/biconomy`; + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: baseSepolia, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + + console.log( + `${baseSepolia.name} EPv0.7.0 txHash: ${receipt.transactionHash}`, + ); + console.log(receipt); + + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("optimism-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${optimism.id}/biconomy`; + + const paymasterUrl = process.env.OPTIMISM_MAINNET_PAYMASTER_URL; + + const client = createWalletClient({ + account, + chain: optimism, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("OPTIMISM_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${optimism.id}/test`; + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: optimism, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log( + `${optimism.name} EPv0.7.0 txHash: ${receipt.transactionHash}`, + ); + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("avalanche-mainnet", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${avalanche.id}/biconomy`; + const account = privateKeyToAccount(`0x${privateKey}`); + + const paymasterUrl = process.env.AVALANCHE_MAINNET_PAYMASTER_URL; + + const client = createWalletClient({ + account, + chain: avalanche, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("AVALANCHE_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe.skip("bsc-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${bsc.id}/biconomy`; + + const paymasterUrl = process.env.BSC_MAINNET_PAYMASTER_URL; + + const client = createWalletClient({ + account, + chain: bsc, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + // const receipt = await sendUserOperation(account, smartAccount, false); + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("AVALANCHE_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${bsc.id}/biconomy`; + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: bsc, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + + console.log(receipt); + + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("arbitrum-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${arbitrum.id}/biconomy`; + + const paymasterUrl = process.env.ARBITRUM_MAINNET_PAYMASTER_URL; + + const client = createWalletClient({ + account, + chain: arbitrum, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("ARBITRUM_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${arbitrum.id}/test`; + + logConfig(arbitrum.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: arbitrum, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log( + `${arbitrum.name} EPv0.7.0 txHash: ${receipt.transactionHash}`, + ); + // console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("polygon-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${polygon.id}/biconomy`; + + const paymasterUrl = process.env.POLYGON_MAINNET_PAYMASTER_URL; + + const client = createWalletClient({ + account, + chain: polygon, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("POLYGON_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${polygon.id}/test`; + + logConfig(polygon.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: polygon, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log( + `${polygon.name} EPv0.7.0 txHash: ${receipt.transactionHash}`, + ); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("gnosis-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${gnosis.id}/test`; + + const paymasterUrl = process.env.GNOSIS_MAINNET_PAYMASTER_URL; + + const client = createWalletClient({ + account, + chain: gnosis, + transport: http(), + }); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: client, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("GNOSIS_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${gnosis.id}/test`; + + logConfig(gnosis.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: gnosis, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log( + `${gnosis.name} EPv0.7.0 txHash: ${receipt.transactionHash}`, + ); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("blast-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${blast.id}/biconomy`; + + logConfig(blast.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: blast, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log( + `${blast.name} EPv0.7.0 txHash: ${receipt.transactionHash}`, + ); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("berachain-bartio", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${berachainTestnetbArtio.id}/biconomy`; + + logConfig(berachainTestnetbArtio.id, bundlerUrl, account, ""); + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: berachainTestnetbArtio, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log( + `${berachainTestnetbArtio.name} EPv0.7.0 txHash: ${receipt.transactionHash}`, + ); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("ethereum-sepolia", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + describe.skip("EntryPoint v0.6.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v2/${sepolia.id}/biconomy`; + + const paymasterUrl = process.env.SEPOLIA_MAINNET_PAYMASTER_URL; + + const signer = createWalletClient({ + account, + chain: sepolia, + transport: http(), + }).extend(publicActions); + + let smartAccount: BiconomySmartAccountV2; + + const tx = { + to: account.address, + value: 1n, + }; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: signer, + bundlerUrl, + paymasterUrl, + }); + await requireBalance(smartAccount, valueToSend); + }); + + it("should perform a native transfer without a paymaster", async () => { + const userOpResponse = await smartAccount.sendTransaction(tx); + + const receipt = await userOpResponse.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }, 60_000); + + if (paymasterUrl) { + it("should perform a native transfer using a paymaster", async () => { + if (!paymasterUrl) { + throw new Error("SEPOLIA_MAINNET_PAYMASTER_URL is not defined"); + } + + const unestimatedUserOperation: Partial = + await smartAccount.signUserOp({ + sender: await smartAccount.getAccountAddress(), + nonce: await smartAccount.getNonce(), + initCode: "0x", + callData: await smartAccount.encodeExecute(tx.to, tx.value, "0x"), + callGasLimit: 1n, + verificationGasLimit: 1n, + preVerificationGas: 1n, + maxFeePerGas: 1n, + maxPriorityFeePerGas: 1n, + paymasterAndData: stubPaymasterAndData, + }); + + const gasEstimate = await smartAccount.bundler?.estimateUserOpGas( + unestimatedUserOperation, + ); + + const { + callGasLimit, + verificationGasLimit, + preVerificationGas, + maxFeePerGas, + maxPriorityFeePerGas, + } = gasEstimate!; + + let estimatedUserOperation = await smartAccount.signUserOp({ + ...unestimatedUserOperation, + callGasLimit: BigInt(callGasLimit), + verificationGasLimit: BigInt(verificationGasLimit), + preVerificationGas: BigInt(preVerificationGas), + maxFeePerGas: BigInt(maxFeePerGas), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas), + }); + + estimatedUserOperation.paymasterAndData = await sponsorUserOperation( + paymasterUrl, + estimatedUserOperation, + ); + + estimatedUserOperation = await smartAccount.signUserOp( + estimatedUserOperation, + ); + + const response = await smartAccount.bundler!.sendUserOp( + estimatedUserOperation, + ); + + const receipt = await response.wait(); + + console.log(receipt); + + expect(receipt.success).toBe("true"); + }); + } + }); + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${sepolia.id}/biconomy`; + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: sepolia, + transport: http(), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + + console.log(receipt); + + expect(receipt.status).toBe("success"); + }); + }); + }); + + describe.skip("monad-testnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + const monadTestnetChainId = 10143; + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${monadTestnetChainId}/biconomy`; + + const rpcUrl = process.env.MONAD_TESTNET_RPC_URL; + if (!rpcUrl) { + throw new Error("MONAD_TESTNET_RPC_URL is not defined"); + } + + it("should perform a native transfer without a paymaster", async () => { + try { + const nexusClient = await createNexusClient({ + signer: account, + bootStrapAddress: "0x7052eE73e9e9cA6884eb2146cA5c020492E5bB9D", + factoryAddress: "0xF8524aB72c688069DfFa8B1Cbb6005929B5Aff58", + validatorAddress: "0xEbc7f5Cff2cABcFdfD65a37A9342240b39A73fcd", + chain: getCustomChain( + "Monad Testnet", + monadTestnetChainId, + rpcUrl, + "", + { + name: "S", + symbol: "S", + decimals: 18, + }, + ), + transport: http(rpcUrl), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log(receipt); + expect(receipt.status).toBe("success"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + console.error(error); + throw error.message; + } + }); + }); + }); + + describe("sonic-mainnet", () => { + const account = privateKeyToAccount(`0x${privateKey}`); + + const sonicMainnetChainId = 146; + + describe("EntryPoint v0.7.0", () => { + const bundlerUrl = `${bundlerHostname}/api/v3/${sonicMainnetChainId}/biconomy`; + + it("should perform a native transfer without a paymaster", async () => { + const nexusClient = await createNexusClient({ + signer: account, + chain: getCustomChain( + "Sonic Mainnet", + sonicMainnetChainId, + "https://rpc.soniclabs.com/", + "", + { + name: "S", + symbol: "S", + decimals: 18, + }, + ), + transport: http("https://rpc.soniclabs.com/"), + bundlerTransport: http(bundlerUrl), + }); + + const smartAccountAddress = nexusClient.account.address; + console.log(`Nexus address: ${smartAccountAddress}`); + + const hash = await nexusClient.sendTransaction({ + calls: [ + { + to: smartAccountAddress, + value: 1n, + }, + ], + }); + + const receipt = await nexusClient.waitForTransactionReceipt({ hash }); + console.log(receipt); + expect(receipt.status).toBe("success"); + }); + }); + }); +}); + +function logConfig( + chainId: number, + bundlerUrl: string, + account: PrivateKeyAccount, + paymasterUrl?: string, +) { + console.log( + JSON.stringify({ + chainId, + bundlerUrl, + paymasterUrl, + eoaAddress: account.address, + }), + ); +} + +async function requireBalance( + smartAccount: BiconomySmartAccountV2, + valueToSend: bigint, +) { + const balances = await smartAccount.getBalances(); + const nativeBalance = balances[balances.length - 1]; + + console.log( + `smartAccount=${await smartAccount.getAddress()} native balance is ${nativeBalance.amount} wei`, + ); + + if (nativeBalance.amount < valueToSend) { + throw new Error( + `Insufficient balance in smart account with address ${await smartAccount.getAddress()} +Balance: ${nativeBalance.amount} wei, required: ${valueToSend} wei`, + ); + } +} + +async function sponsorUserOperation( + paymasterUrl: string, + estimatedUserOperation: UserOperationStruct, +) { + try { + const sponsorUserOperationResponse = await axios.post(paymasterUrl, { + id: 1, + jsonrpc: "2.0", + method: "pm_sponsorUserOperation", + params: [ + estimatedUserOperation, + { + mode: "SPONSORED", + calculateGasLimits: false, + expiryDuration: 300, + sponsorshipInfo: { + webhookData: {}, + smartAccountInfo: { + name: "BICONOMY", + version: "2.0.0", + }, + }, + }, + ], + }); + + const { paymasterAndData } = sponsorUserOperationResponse.data.result; + + return paymasterAndData; + } catch (err) { + const axiosError = err as AxiosError; + throw new Error(axiosError.message); + } +} diff --git a/src/test/e2e/network-service/network-service.test.ts b/src/test/e2e/network-service/network-service.test.ts deleted file mode 100644 index c722159b..00000000 --- a/src/test/e2e/network-service/network-service.test.ts +++ /dev/null @@ -1,312 +0,0 @@ -describe("broken test", () => { - it.todo("should be fixed"); -}); - -// /* eslint-disable import/no-import-module-exports */ -// /* eslint-disable no-await-in-loop */ -// import { BigNumber, ethers } from 'ethers'; -// import { config } from '../../../config'; -// import { EVMNetworkService } from '../../../common/network'; -// import { logger } from '../../../common/logger'; -// import { EVMAccount } from '../../../relayer/src/services/account'; - -// const log = logger.child({ module: module.filename.split('/').slice(-4).join('/') }); - -// const networkServiceMap: { -// [chainId: number]: EVMNetworkService; -// } = {}; - -// for (const supportedNetwork of config.supportedNetworks) { -// networkServiceMap[supportedNetwork] = new EVMNetworkService({ -// chainId: supportedNetwork, -// rpcUrl: config.chains.provider[supportedNetwork], -// fallbackRpcUrls: config.chains.fallbackUrls[supportedNetwork] || [], -// }); -// } - -// describe('Network Service: Rpc Urls', () => { -// it('Main rpc url should be active for chainId: 5', async () => { -// const { data } = await networkServiceMap[5].sendRpcCall('eth_blockNumber', []); -// const blockNumber = Number(BigNumber.from(data.result)); -// expect(blockNumber).toBeGreaterThan(0); -// }); - -// it('Main rpc url should be active for chainId: 80001', async () => { -// const { data } = await networkServiceMap[80001].sendRpcCall('eth_blockNumber', []); -// const blockNumber = Number(BigNumber.from(data.result)); -// expect(blockNumber).toBeGreaterThan(0); -// }); - -// it('Fallback urls should be active for chainId: 5', async () => { -// for ( -// let fallBackRpcUrlIndex = 0; -// fallBackRpcUrlIndex < networkServiceMap[5].fallbackRpcUrls.length; -// fallBackRpcUrlIndex += 1 -// ) { -// const fallBackRpcUrl = networkServiceMap[5].fallbackRpcUrls[fallBackRpcUrlIndex]; -// log.info(`Checking rpcUrl: ${fallBackRpcUrl}`); -// networkServiceMap[5].setActiveRpcUrl(fallBackRpcUrl); -// const { data } = await networkServiceMap[5].sendRpcCall('eth_blockNumber', []); -// const blockNumber = Number(BigNumber.from(data.result)); -// expect(blockNumber).toBeGreaterThan(0); -// } -// }); - -// it('Fallback urls should be active for chainId: 80001', async () => { -// for ( -// let fallBackRpcUrlIndex = 0; -// fallBackRpcUrlIndex < networkServiceMap[80001].fallbackRpcUrls.length; -// fallBackRpcUrlIndex += 1 -// ) { -// const fallBackRpcUrl = networkServiceMap[80001].fallbackRpcUrls[fallBackRpcUrlIndex]; -// log.info(`Checking rpcUrl: ${fallBackRpcUrl}`); -// networkServiceMap[80001].setActiveRpcUrl(fallBackRpcUrl); -// const { data } = await networkServiceMap[80001].sendRpcCall('eth_blockNumber', []); -// const blockNumber = Number(BigNumber.from(data.result)); -// expect(blockNumber).toBeGreaterThan(0); -// } -// }); -// }); - -// describe('Network Service: Gas Prices', () => { -// it('Type 0 transaction type gas price is not null/zero for chainId: 5', async () => { -// const { gasPrice } = await networkServiceMap[5].getGasPrice(); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// }); - -// it('Type 0 transaction type gas price is not null/zero for chainId: 80001', async () => { -// const { gasPrice } = await networkServiceMap[80001].getGasPrice(); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// }); - -// it('Type 2 transaction type gas price is not null/zero for chainId: 5', async () => { -// const { -// maxPriorityFeePerGas, -// maxFeePerGas, -// } = await networkServiceMap[5].getEIP1559GasPrice(); -// expect(maxPriorityFeePerGas).not.toBeNull(); -// expect(typeof maxPriorityFeePerGas).toBe('string'); -// expect(Number(maxPriorityFeePerGas)).toBeGreaterThan(0); - -// expect(maxFeePerGas).not.toBeNull(); -// expect(typeof maxFeePerGas).toBe('string'); -// expect(Number(maxFeePerGas)).toBeGreaterThan(0); -// }); - -// it('Type 2 transaction type gas price is not null/zero for chainId: 80001', async () => { -// const { -// maxPriorityFeePerGas, -// maxFeePerGas, -// } = await networkServiceMap[80001].getEIP1559GasPrice(); -// expect(maxPriorityFeePerGas).not.toBeNull(); -// expect(typeof maxPriorityFeePerGas).toBe('string'); -// expect(Number(maxPriorityFeePerGas)).toBeGreaterThan(0); - -// expect(maxFeePerGas).not.toBeNull(); -// expect(typeof maxFeePerGas).toBe('string'); -// expect(Number(maxFeePerGas)).toBeGreaterThan(0); -// }); -// }); - -// describe('Network Service: Native Asset Balance', () => { -// it('Fetches the correct native asset balance on chainId: 80001', async () => { -// // test wallet -// const wallet = ethers.Wallet.createRandom(); - -// // owner address -// // const ownerAddressPublicKey = '0x4C07E2fa10f9871142883139B32Cb03F2A180494'; -// const ownerAddressPrivateKey = 'e3b3818b1b604cf6dfc3133faa9a524f1e2ea0d5894a003c4b857952f6b146f6'; -// const ownerWallet = new ethers.Wallet( -// ownerAddressPrivateKey, -// networkServiceMap[80001].ethersProvider, -// ); -// const { gasPrice } = await networkServiceMap[80001].getGasPrice(); - -// // transfer 0.00001 native token from main account -// const transactionResponse = await ownerWallet.sendTransaction({ -// to: wallet.address, -// from: ownerWallet.address, -// nonce: networkServiceMap[80001].getNonce(ownerWallet.address), -// gasLimit: 21000, -// gasPrice, -// value: BigNumber.from('10000000'), -// }); - -// await networkServiceMap[80001].waitForTransaction(transactionResponse.hash); - -// // check if the getBalance gets 0.00001 -// const walletBalance = Number(await networkServiceMap[80001].getBalance(wallet.address)); -// expect(walletBalance).not.toBeNull(); -// expect(typeof walletBalance).toBe('number'); -// expect(walletBalance).toBe(10000000); -// }); - -// it('Fetches the correct native asset balance on chainId: 5', async () => { -// // test wallet -// const wallet = ethers.Wallet.createRandom(); - -// // owner address -// // const ownerAddressPublicKey = '0x040a9cbC4453B0eeaE12f3210117B422B890C1ED'; -// const ownerAddressPrivateKey = 'd952fa86f1e2fed30fb3a6da6e5c24d7deb65b6bb46da3ece5f56fd39e64bbd0'; -// const ownerWallet = new ethers.Wallet( -// ownerAddressPrivateKey, -// networkServiceMap[5].ethersProvider, -// ); -// const { gasPrice } = await networkServiceMap[5].getGasPrice(); - -// // transfer 0.00001 native token from main account -// const transactionResponse = await ownerWallet.sendTransaction({ -// to: wallet.address, -// from: ownerWallet.address, -// nonce: networkServiceMap[5].getNonce(ownerWallet.address), -// gasLimit: 21000, -// gasPrice, -// value: BigNumber.from('10000000'), -// }); - -// await networkServiceMap[5].waitForTransaction(transactionResponse.hash); - -// // check if the getBalance gets 0.00001 -// const walletBalance = Number(await networkServiceMap[5].getBalance(wallet.address)); - -// expect(walletBalance).not.toBeNull(); -// expect(typeof walletBalance).toBe('number'); -// expect(walletBalance).toBe(10000000); -// }); -// }); - -// describe('Network Service: Nonce Check', () => { -// it('Check if nonce is correctly incremented on chainId: 80001', async () => { -// // call getNonce() on an address, nonce should x - -// // owner address -// const ownerAddressPublicKey = '0x4C07E2fa10f9871142883139B32Cb03F2A180494'; -// const ownerAddressPrivateKey = 'e3b3818b1b604cf6dfc3133faa9a524f1e2ea0d5894a003c4b857952f6b146f6'; -// const ownerWallet = new ethers.Wallet( -// ownerAddressPrivateKey, -// networkServiceMap[80001].ethersProvider, -// ); - -// const nonceBeforeTransaction = await networkServiceMap[80001].getNonce(ownerAddressPublicKey); -// const { gasPrice } = await networkServiceMap[80001].getGasPrice(); - -// // do a transaction -// // transfer 0.00001 native token from main account -// await ownerWallet.sendTransaction({ -// to: ownerAddressPublicKey, -// from: ownerAddressPublicKey, -// nonce: networkServiceMap[80001].getNonce(ownerAddressPublicKey), -// gasLimit: 21000, -// gasPrice, -// value: BigNumber.from('10000000'), -// }); - -// // then call getNonce() on that address, nonce should x + 1 -// const nonceAfterTransaction = await networkServiceMap[80001].getNonce(ownerAddressPublicKey); -// const nonceDifference = nonceAfterTransaction - nonceBeforeTransaction; -// expect(nonceDifference).toBe(1); -// }); - -// it('Check if nonce is correctly incremented on chainId: 5', async () => { -// // call getNonce() on an address, nonce should x - -// // owner address -// const ownerAddressPublicKey = '0x040a9cbC4453B0eeaE12f3210117B422B890C1ED'; -// const ownerAddressPrivateKey = 'd952fa86f1e2fed30fb3a6da6e5c24d7deb65b6bb46da3ece5f56fd39e64bbd0'; -// const ownerWallet = new ethers.Wallet( -// ownerAddressPrivateKey, -// networkServiceMap[5].ethersProvider, -// ); - -// const nonceBeforeTransaction = await networkServiceMap[5].getNonce(ownerAddressPublicKey); -// const { gasPrice } = await networkServiceMap[5].getGasPrice(); - -// // do a transaction -// // transfer 0.00001 native token from main account -// await ownerWallet.sendTransaction({ -// to: ownerAddressPublicKey, -// from: ownerAddressPublicKey, -// nonce: networkServiceMap[5].getNonce(ownerAddressPublicKey), -// gasLimit: 21000, -// gasPrice, -// value: BigNumber.from('10000000'), -// }); - -// // then call getNonce() on that address, nonce should x + 1 -// const nonceAfterTransaction = await networkServiceMap[5].getNonce(ownerAddressPublicKey); -// const nonceDifference = nonceAfterTransaction - nonceBeforeTransaction; -// expect(nonceDifference).toBe(1); -// }); -// }); - -// describe('Network Service: Sending Transaction', () => { -// it('Transaction should be sent and confirm on chainId: 80001', async () => { -// const wallet = ethers.Wallet.createRandom(); - -// // owner address -// const ownerAddressPublicKey = '0x4C07E2fa10f9871142883139B32Cb03F2A180494'; -// const ownerAddressPrivateKey = 'e3b3818b1b604cf6dfc3133faa9a524f1e2ea0d5894a003c4b857952f6b146f6'; - -// const evmAccount = new EVMAccount(ownerAddressPublicKey, ownerAddressPrivateKey); - -// const { gasPrice } = await networkServiceMap[80001].getGasPrice(); -// const nonce = await networkServiceMap[80001].getNonce(ownerAddressPublicKey); - -// const rawTransactionData = { -// from: ownerAddressPublicKey, -// gasPrice, -// data: '0x', -// gasLimit: '0x989680', -// to: wallet.address, -// value: '0x989680', -// chainId: 80001, -// nonce, -// }; - -// const transactionExecutionResponse = await networkServiceMap[80001] -// .sendTransaction(rawTransactionData, evmAccount); - -// const { hash } = transactionExecutionResponse; - -// const transactionReceipt = await networkServiceMap[80001].waitForTransaction(hash); -// expect(transactionReceipt).not.toBeNull(); -// expect(transactionReceipt.status).toBe(1); -// }); - -// it('Transaction should be sent and confirm on chainId: 5', async () => { -// const wallet = ethers.Wallet.createRandom(); - -// // owner address -// const ownerAddressPublicKey = '0x040a9cbC4453B0eeaE12f3210117B422B890C1ED'; -// const ownerAddressPrivateKey = 'd952fa86f1e2fed30fb3a6da6e5c24d7deb65b6bb46da3ece5f56fd39e64bbd0'; - -// const evmAccount = new EVMAccount(ownerAddressPublicKey, ownerAddressPrivateKey); - -// const { gasPrice } = await networkServiceMap[5].getGasPrice(); -// const nonce = await networkServiceMap[5].getNonce(ownerAddressPublicKey); - -// const rawTransactionData = { -// from: ownerAddressPublicKey, -// gasPrice, -// data: '0x', -// gasLimit: '0x989680', -// to: wallet.address, -// value: '0x989680', -// chainId: 5, -// nonce, -// }; - -// const transactionExecutionResponse = await networkServiceMap[5] -// .sendTransaction(rawTransactionData, evmAccount); - -// const { hash } = transactionExecutionResponse; - -// const transactionReceipt = await networkServiceMap[5].waitForTransaction(hash); -// expect(transactionReceipt).not.toBeNull(); -// expect(transactionReceipt.status).toBe(1); -// }); -// }); diff --git a/src/test/e2e/relayer-manager/relayer-manager.test.ts b/src/test/e2e/relayer-manager/relayer-manager.test.ts deleted file mode 100644 index f37e7bdc..00000000 --- a/src/test/e2e/relayer-manager/relayer-manager.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -describe("broken test suite", () => { - it("should be fixed", () => {}); -}); -// import { ethers } from 'ethers'; -// import { RedisCacheService } from '../../../common/cache'; -// import { TransactionDAO } from '../../../common/db'; -// import { GasPriceManager } from '../../../common/gas-price'; -// import { EVMNetworkService } from '../../../common/network'; -// import { NotificationManager } from '../../../common/notification'; -// import { SlackNotificationService } from '../../../common/notification/slack/SlackNotificationService'; -// import { RetryTransactionHandlerQueue, TransactionHandlerQueue } from '../../../common/queue'; -// import { EVMRawTransactionType } from '../../../common/types'; -// import { config } from '../../../config'; -// import { EVMAccount, IEVMAccount } from '../../../relayer/src/services/account'; -// import { EVMNonceManager } from '../../../relayer/src/services/nonce-manager'; -// import { EVMRelayerManager, IRelayerManager } from '../../../relayer/src/services/relayer-manager'; -// import { EVMRelayerQueue } from '../../../relayer/src/services/relayer-queue'; -// import { EVMTransactionListener } from '../../../relayer/src/services/transaction-listener'; -// import { EVMTransactionService } from '../../../relayer/src/services/transaction-service'; - -// // test relayer manager -// describe('relayer manager', () => { -// const chainId = 5; - -// const EVMRelayerManagerMap: { -// [name: string]: { -// [chainId: number]: IRelayerManager; -// } -// } = {}; - -// const cacheService = RedisCacheService.getInstance(); - -// const networkService = new EVMNetworkService({ -// chainId, -// rpcUrl: config.chains.provider[chainId], -// fallbackRpcUrls: config.chains.fallbackUrls[chainId] || [], -// }); - -// const gasPriceManager = new GasPriceManager(cacheService, networkService, { -// chainId, -// EIP1559SupportedNetworks: config.EIP1559SupportedNetworks, -// }); - -// const gasPriceService = gasPriceManager.setup(); - -// const transactionQueue = new TransactionHandlerQueue({ -// chainId, -// }); - -// const retryTransactionQueue = new RetryTransactionHandlerQueue({ -// chainId, -// }); - -// const transactionDao = new TransactionDAO(); - -// const transactionListener = new EVMTransactionListener({ -// networkService, -// transactionQueue, -// retryTransactionQueue, -// transactionDao, -// cacheService, -// options: { -// chainId, -// }, -// }); - -// const nonceManager = new EVMNonceManager({ -// options: { -// chainId, -// }, -// networkService, -// cacheService, -// }); - -// const slackNotificationService = new SlackNotificationService( -// config.slack.token, -// config.slack.channel, -// ); -// const notificationManager = new NotificationManager(slackNotificationService); - -// if (gasPriceService) { -// const transactionService = new EVMTransactionService({ -// networkService, -// transactionListener, -// nonceManager, -// gasPriceService, -// transactionDao, -// cacheService, -// notificationManager, -// options: { -// chainId, -// }, -// }); - -// const relayerQueue = new EVMRelayerQueue([]); -// const relayerManager = config.relayerManagers[0]; -// if (!EVMRelayerManagerMap[relayerManager.name]) { -// EVMRelayerManagerMap[relayerManager.name] = {}; -// } -// const relayerMangerInstance = new EVMRelayerManager({ -// networkService, -// gasPriceService, -// cacheService, -// transactionService, -// nonceManager, -// relayerQueue, -// options: { -// chainId, -// name: relayerManager.name, -// relayerSeed: relayerManager.relayerSeed, -// minRelayerCount: relayerManager.minRelayerCount[chainId], -// maxRelayerCount: relayerManager.maxRelayerCount[chainId], -// inactiveRelayerCountThreshold: -// relayerManager.inactiveRelayerCountThreshold[chainId], -// pendingTransactionCountThreshold: -// relayerManager.pendingTransactionCountThreshold[chainId], -// newRelayerInstanceCount: -// relayerManager.newRelayerInstanceCount[chainId], -// // eslint-disable-next-line max-len -// fundingBalanceThreshold: ethers.utils.parseEther(relayerManager.fundingBalanceThreshold[chainId].toString()), -// fundingRelayerAmount: relayerManager.fundingRelayerAmount[chainId], -// ownerAccountDetails: new EVMAccount( -// relayerManager.ownerAccountDetails[chainId].publicKey, -// relayerManager.ownerAccountDetails[chainId].privateKey, -// ), -// gasLimitMap: relayerManager.gasLimitMap, -// }, -// }); -// EVMRelayerManagerMap[relayerManager.name][chainId] = relayerMangerInstance; - -// let addressList: string[] = []; -// beforeAll(async () => { -// await transactionQueue.connect(); -// await retryTransactionQueue.connect(); -// addressList = await relayerMangerInstance.createRelayers(); -// }); - -// it('should return a list of relayers', async () => { -// expect(addressList.length).toBeGreaterThan(0); -// }); - -// it('should get total relayer count', () => { -// const totalRelayerCount = relayerMangerInstance.getRelayersCount(); -// expect(typeof totalRelayerCount).toBe('number'); -// expect(totalRelayerCount).toBeGreaterThan(0); -// }); - -// it('should get active relayer count', () => { -// const activeRelayerCount = relayerMangerInstance.getRelayersCount(true); -// expect(typeof activeRelayerCount).toBe('number'); -// expect(activeRelayerCount).toBeGreaterThan(0); -// }); - -// it('should fund relayers', async () => { -// await relayerMangerInstance.fundRelayers(addressList); -// for (const address of addressList) { -// const isBalanceBelowThreshold = relayerMangerInstance.hasBalanceBelowThreshold(address); -// expect(isBalanceBelowThreshold).toBe(false); -// const relayerData = relayerMangerInstance.relayerQueue.list().find( -// (relayer) => relayer.address === address, -// ); -// expect(relayerData).toBeDefined(); -// // check balance above threshold -// expect(Number(relayerData?.balance)).toBeGreaterThan(0); -// expect(relayerData?.pendingCount).toBe(0); -// } -// }); - -// it('should get an active relayer', async () => { -// const relayer = relayerMangerInstance.getActiveRelayer(); -// expect(relayer).toBeDefined(); -// }); - -// it('should not add a new relayer', async () => { -// const address = '0x0000000000000000000000000000000000000000'; -// await relayerMangerInstance.addActiveRelayer(address); -// const relayerData = relayerMangerInstance.relayerQueue -// .list().find((relayer) => relayer.address === address); -// expect(relayerData).toBeUndefined(); -// }); - -// it('should set and fetch min relayer count', async () => { -// const minRelayerCount = 5; -// relayerMangerInstance.setMinRelayerCount(minRelayerCount); -// const updatedMinRelayerCount = relayerMangerInstance.getMinRelayerCount(); -// expect(updatedMinRelayerCount).toBe(minRelayerCount); -// }); - -// it('should set and fetch max relayer count', async () => { -// const maxRelayerCount = 10; -// relayerMangerInstance.setMaxRelayerCount(maxRelayerCount); -// const updatedMaxRelayerCount = relayerMangerInstance.getMaxRelayerCount(); -// expect(updatedMaxRelayerCount).toBe(maxRelayerCount); -// }); - -// it('should set and fetch inactive relayer count threshold', async () => { -// const inactiveRelayerCountThreshold = 10; -// relayerMangerInstance.setInactiveRelayerCountThreshold(inactiveRelayerCountThreshold); -// const updatedInactiveRelayerCountThreshold = relayerMangerInstance -// .getInactiveRelayerCountThreshold(); -// expect(updatedInactiveRelayerCountThreshold).toBe(inactiveRelayerCountThreshold); -// }); - -// it('should set and fetch pending transaction count threshold', async () => { -// const pendingTransactionCountThreshold = 10; -// relayerMangerInstance.setPendingTransactionCountThreshold(pendingTransactionCountThreshold); -// const updatedPendingTransactionCountThreshold = relayerMangerInstance -// .getPendingTransactionCountThreshold(); -// expect(updatedPendingTransactionCountThreshold).toBe(pendingTransactionCountThreshold); -// }); -// } -// }); diff --git a/src/test/e2e/token-price/token-price.test.ts b/src/test/e2e/token-price/token-price.test.ts deleted file mode 100644 index fa664e31..00000000 --- a/src/test/e2e/token-price/token-price.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -describe("broken test suite", () => { - it("should be fixed", () => {}); -}); - -// import { RedisCacheService } from '../../../common/cache'; -// import { CMCTokenPriceManager } from '../../../common/token-price'; -// import { config } from '../../../config'; - -// describe('get token price', () => { -// const cacheService = RedisCacheService.getInstance(); - -// const tokenService = new CMCTokenPriceManager(cacheService, { -// apiKey: config.tokenPrice.coinMarketCapApi, -// networkSymbolCategories: config.tokenPrice.networkSymbols, -// updateFrequencyInSeconds: config.tokenPrice.updateFrequencyInSeconds, -// symbolMapByChainId: config.tokenPrice.symbolMapByChainId, -// }); - -// beforeAll(async () => { -// await cacheService.connect(); -// }); - -// it('should return token price for chain id 5', async () => { -// const tokenPrice = await tokenService.getTokenPrice('5'); -// expect(typeof tokenPrice).toBe('string'); -// expect(Number(tokenPrice)).toBeGreaterThan(0); -// }); - -// afterAll(async () => { -// await cacheService.close(); -// }); -// }); diff --git a/src/test/e2e/transaction/transaction.test.ts b/src/test/e2e/transaction/transaction.test.ts deleted file mode 100644 index 251f8b93..00000000 --- a/src/test/e2e/transaction/transaction.test.ts +++ /dev/null @@ -1,247 +0,0 @@ -describe("broken test suite", () => { - it("should be fixed", () => {}); -}); -// import { ethers } from 'ethers'; -// import { RedisCacheService } from '../../../common/cache'; -// import { Mongo, TransactionDAO } from '../../../common/db'; -// import { GasPriceManager } from '../../../common/gas-price'; -// import { GasPriceType } from '../../../common/gas-price/types'; -// import { relayerManagerTransactionTypeNameMap } from '../../../common/maps'; -// import { EVMNetworkService } from '../../../common/network'; -// import { NotificationManager } from '../../../common/notification'; -// import { SlackNotificationService } from '../../../common/notification/slack/SlackNotificationService'; -// import { TransactionHandlerQueue, RetryTransactionHandlerQueue } from '../../../common/queue'; -// import { TransactionType } from '../../../common/types'; -// import { generateTransactionId } from '../../../common/utils'; -// import { config } from '../../../config'; -// import { EVMAccount, IEVMAccount } from '../../../relayer/src/services/account'; -// import { EVMNonceManager } from '../../../relayer/src/services/nonce-manager'; -// import { EVMTransactionListener } from '../../../relayer/src/services/transaction-listener'; -// import { EVMTransactionService } from '../../../relayer/src/services/transaction-service'; - -// const dbInstance = Mongo.getInstance(); -// const transactionDao = new TransactionDAO(); -// const cacheService = RedisCacheService.getInstance(); -// const chainId = 5; - -// const networkService = new EVMNetworkService({ -// chainId, -// rpcUrl: config.chains.provider[chainId], -// fallbackRpcUrls: config.chains.fallbackUrls[chainId] || [], -// }); - -// const transactionQueue = new TransactionHandlerQueue({ -// chainId, -// }); - -// const gasPriceManager = new GasPriceManager(cacheService, networkService, { -// chainId, -// EIP1559SupportedNetworks: config.EIP1559SupportedNetworks, -// }); - -// const gasPriceService = gasPriceManager.setup(); -// if (gasPriceService) { -// gasPriceService.schedule(); -// } -// if (!gasPriceService) { -// throw new Error(`Gasprice service is not setup for chainId ${chainId}`); -// } - -// const retryTransactionQueue = new RetryTransactionHandlerQueue({ -// chainId, -// }); -// retryTransactionQueue.connect(); - -// const nonceManager = new EVMNonceManager({ -// options: { -// chainId, -// }, -// networkService, -// cacheService, -// }); - -// const transactionListener = new EVMTransactionListener({ -// networkService, -// transactionQueue, -// retryTransactionQueue, -// transactionDao, -// cacheService, -// options: { -// chainId, -// }, -// }); - -// const slackNotificationService = new SlackNotificationService( -// config.slack.token, -// config.slack.channel, -// ); -// const notificationManager = new NotificationManager(slackNotificationService); - -// const transactionService = new EVMTransactionService({ -// networkService, -// transactionListener, -// nonceManager, -// gasPriceService, -// transactionDao, -// cacheService, -// notificationManager, -// options: { -// chainId, -// }, -// }); - -// const setQuoteAbi = [{ -// inputs: [{ internalType: 'string', name: 'newQuote', type: 'string' }], name: 'setQuote', outputs: [], stateMutability: 'nonpayable', type: 'function', -// }, { inputs: [], stateMutability: 'nonpayable', type: 'constructor' }, { -// inputs: [], name: 'admin', outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', type: 'function', -// }, { -// inputs: [], name: 'getQuote', outputs: [{ internalType: 'string', name: 'currentQuote', type: 'string' }, { internalType: 'address', name: 'currentOwner', type: 'address' }], stateMutability: 'view', type: 'function', -// }, { -// inputs: [], name: 'owner', outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', type: 'function', -// }, { -// inputs: [], name: 'quote', outputs: [{ internalType: 'string', name: '', type: 'string' }], stateMutability: 'view', type: 'function', -// }]; -// const setQuoteAddress = '0xe31b0bcbda693bff2529f4a1d9f7e8f6d924c6ab'; - -// const setQuoteContract = networkService.getContract(customJSONStringify(setQuoteAbi), setQuoteAddress); - -// describe('Transaction Service: Sending Transaction on chainId: 5', () => { -// let nonceBeforeTransaction: number; -// let transactionExecutionResponse: ethers.providers.TransactionResponse | null; -// let transactionId: string; -// let activeRelayer: IEVMAccount | null; - -// // check for null gasLimit -// // same nonce of relayer -// beforeAll(async () => { -// await dbInstance.connect(); -// await cacheService.connect(); -// await transactionQueue.connect(); - -// activeRelayer = new EVMAccount('0x040a9cbC4453B0eeaE12f3210117B422B890C1ED', 'd952fa86f1e2fed30fb3a6da6e5c24d7deb65b6bb46da3ece5f56fd39e64bbd0'); - -// if (!activeRelayer) { -// throw new Error(`No active relayer for transactionType: ${TransactionType.SCW} on chainId: ${chainId}`); -// } - -// nonceBeforeTransaction = await nonceManager.getNonce(activeRelayer.getPublicKey()); -// const { data } = await setQuoteContract.populateTransaction.setQuote(`Current time: ${Date.now()}`); - -// const transactionData: any = { -// to: setQuoteAddress, -// value: '0x0', -// data: data as string, -// gasLimit: '0x249F0', -// }; - -// transactionId = generateTransactionId(transactionData.toString()); - -// transactionData.transactionId = transactionId; - -// const response = await transactionService.sendTransaction( -// transactionData, -// activeRelayer, -// TransactionType.SCW, -// relayerManagerTransactionTypeNameMap.SCW, -// ); -// transactionExecutionResponse = response.transactionExecutionResponse; -// transactionId = response.transactionId; -// }); - -// it('Transaction hash is generated', async () => { -// expect((transactionExecutionResponse as ethers.providers.TransactionResponse).hash) -// .not.toBeNull(); -// expect(typeof (transactionExecutionResponse as ethers.providers.TransactionResponse).hash).toBe('string'); -// }); - -// it('Nonce should have been incremented by 1', async () => { -// const nonceAfterTransaction = await nonceManager.getNonce( -// (activeRelayer as IEVMAccount).getPublicKey(), -// ); -// const nonceDifference = nonceAfterTransaction - nonceBeforeTransaction; -// expect(nonceDifference).toBe(1); -// }); - -// it('Transaction is confirmed on chain', async () => { -// const transactionReceipt = await networkService.waitForTransaction( -// (transactionExecutionResponse as ethers.providers.TransactionResponse).hash, -// ); -// expect(transactionReceipt.status).toBe(1); -// }); -// }); - -// describe('Retry Transaction Service: Transaction should be bumped up and confimred on chainId: 5', () => { -// let nonceBeforeTransaction: number; -// let transactionExecutionResponse: ethers.providers.TransactionResponse | null; -// let transactionId: string; -// let activeRelayer: IEVMAccount | null; - -// beforeAll(async () => { -// await dbInstance.connect(); -// await cacheService.connect(); -// await transactionQueue.connect(); -// activeRelayer = new EVMAccount('0x040a9cbC4453B0eeaE12f3210117B422B890C1ED', 'd952fa86f1e2fed30fb3a6da6e5c24d7deb65b6bb46da3ece5f56fd39e64bbd0'); - -// if (!activeRelayer) { -// throw new Error(`No active relayer for transactionType: ${TransactionType.SCW} on chainId: ${chainId}`); -// } - -// nonceBeforeTransaction = await nonceManager.getNonce(activeRelayer.getPublicKey()); -// const { data } = await setQuoteContract.populateTransaction.setQuote(`Current time: ${Date.now()}`); - -// const transactionData: any = { -// to: setQuoteAddress, -// value: '0x0', -// data: data as string, -// gasLimit: '0x249F0', -// }; - -// transactionId = generateTransactionId(transactionData.toString()); - -// transactionData.transactionId = transactionId; - -// const currentGasPrice = await gasPriceService.getGasPrice(GasPriceType.DEFAULT); - -// // Set low gas price value in cache -// await gasPriceService.setGasPrice( -// GasPriceType.DEFAULT, -// (Math.round(Number((currentGasPrice)) * 0.75)).toString(), -// ); - -// const response = await transactionService.sendTransaction( -// transactionData, -// activeRelayer, -// TransactionType.SCW, -// relayerManagerTransactionTypeNameMap.SCW, -// ); -// transactionExecutionResponse = response.transactionExecutionResponse; -// transactionId = response.transactionId; - -// // Delete low gas price value from cache -// await gasPriceService.setGasPrice( -// GasPriceType.DEFAULT, -// currentGasPrice, -// ); -// }); - -// it('Transaction hash is generated', async () => { -// expect((transactionExecutionResponse as ethers.providers.TransactionResponse).hash) -// .not.toBeNull(); -// expect(typeof (transactionExecutionResponse as ethers.providers.TransactionResponse).hash).toBe('string'); -// }); - -// it('Nonce should have been incremented by 1', async () => { -// const nonceAfterTransaction = await nonceManager.getNonce( -// (activeRelayer as IEVMAccount).getPublicKey(), -// ); -// const nonceDifference = nonceAfterTransaction - nonceBeforeTransaction; -// expect(nonceDifference).toBe(1); -// }); - -// it('Transaction is confirmed on chain', async () => { -// const transactionReceipt = await networkService.waitForTransaction( -// (transactionExecutionResponse as ethers.providers.TransactionResponse).hash, -// ); -// expect(transactionReceipt.status).toBe(1); -// }); -// }); diff --git a/src/test/smoke/health.test.ts b/src/test/smoke/health.test.ts new file mode 100644 index 00000000..83c8bf9c --- /dev/null +++ b/src/test/smoke/health.test.ts @@ -0,0 +1,42 @@ +import config from "config"; + +/** + * Smoke test to check if the server is running and healthy. + * This can be run on a local environment or on a deployed environment. + * If you're testing locally, make sure you have the server running before running this test. + * If you want to target production, edit the SMOKE_TEST_BUNDLER_HOSTNAME environment variable in the .env file. + * + * To make sure smoke tests pass on a local environment you can use config/smoke.json + * and disable funding & checking of relayers because you don't want to acrquire funds + * for every mainnet & testnet we support. + */ +describe("smoke-test", () => { + const bundlerHostname = process.env.SMOKE_TEST_BUNDLER_HOSTNAME; + if (!bundlerHostname) { + throw new Error("SMOKE_TEST_BUNDLER_HOSTNAME not set"); + } + + describe("health", () => { + const supportedNetworks: number[] = config.get("supportedNetworks"); + + console.log(`Smoke test target bundler: ${bundlerHostname}`); + + for (const chainId of supportedNetworks) { + it(`/admin/health/${chainId}`, async () => { + const expected = { + chainId, + healthy: true, + }; + + const response = await fetch( + `${bundlerHostname}/admin/health/${chainId}`, + ); + expect(response.status).toBe(200); + + const data = await response.json(); + + expect(data).toMatchObject(expected); + }); + } + }); +}); diff --git a/src/test/unit/gas-price/gas-price.test.ts b/src/test/unit/gas-price/gas-price.test.ts deleted file mode 100644 index e3f45984..00000000 --- a/src/test/unit/gas-price/gas-price.test.ts +++ /dev/null @@ -1,239 +0,0 @@ -describe("broken test suite", () => { - it("should be fixed", () => {}); -}); - -// /* eslint-disable @typescript-eslint/indent */ -// import { ethers } from 'ethers'; -// import { GasPriceManager, GasPriceServiceType } from '../../../common/gas-price'; -// import { GasPriceType } from '../../../common/gas-price/types'; -// import { EVMNetworkService } from '../../../common/network'; -// import { config } from '../../../config'; -// import { MockCache } from '../mocks/mockCache'; -// import { MockNetworkService } from '../mocks/mockNetworkService'; - -// const goerli = 5; -// const mumbai = 80001; // to test eip1559 supported networks - -// describe('GasPriceManager', () => { -// let gasPriceManagerGoerli; -// let gasPriceManagerMumbai; -// const gasPriceServiceMap: { -// [chainId: number]: GasPriceServiceType; -// } = {}; - -// let cacheService: MockCache; -// beforeAll(async () => { -// cacheService = new MockCache(); -// const networkServiceMap: { -// [chainId: number]: EVMNetworkService; -// } = {}; -// networkServiceMap[goerli] = new MockNetworkService({ -// goerli, -// rpcUrl: config.chains.provider[goerli], -// fallbackRpcUrls: config.chains.fallbackUrls[goerli] || [], -// }); - -// gasPriceManagerGoerli = new GasPriceManager(cacheService, networkServiceMap[goerli], { -// chainId: goerli, -// EIP1559SupportedNetworks: [], -// }); - -// gasPriceServiceMap[goerli] = gasPriceManagerGoerli.setup(); - -// networkServiceMap[mumbai] = new MockNetworkService({ -// mumbai, -// rpcUrl: config.chains.provider[mumbai], -// fallbackRpcUrls: config.chains.fallbackUrls[mumbai] || [], -// }); -// gasPriceManagerMumbai = new GasPriceManager(cacheService, networkServiceMap[goerli], { -// chainId: mumbai, -// EIP1559SupportedNetworks: [80001], -// }); -// gasPriceServiceMap[mumbai] = gasPriceManagerMumbai.setup(); -// }); -// afterEach(async () => { -// jest.resetAllMocks(); -// }); - -// it(`should update gas price in redis for chainId: ${goerli}, if type is STRING`, async () => { -// await gasPriceServiceMap[goerli]?.setGasPrice(GasPriceType.DEFAULT, '1000000000'); -// }); - -// it(`should update gas price in redis for chainId: ${goerli}, if type is not STRING`, async () => { -// await gasPriceServiceMap[goerli]?.setGasPrice(GasPriceType.DEFAULT, { -// maxPriorityFeePerGas: '100000000', -// maxFeePerGas: '1000000000', -// }); -// }); - -// it(`should update gas price in redis for chainId: ${goerli}`, async () => { -// await gasPriceServiceMap[goerli]?.setGasPrice(GasPriceType.DEFAULT, '1000000000'); -// }); - -// it(`should get gas price from redis for chainId: ${goerli}`, async () => { -// const gasPrice = await gasPriceServiceMap[goerli]?.getGasPrice(GasPriceType.DEFAULT); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// expect(gasPrice).toBe('1000000000'); -// }); - -// it('should get gas price from redis for eip1559 supported chains', async () => { -// const expectedOutput = { maxFeePerGas: '1000000000', maxPriorityFeePerGas: '1000000000' }; - -// const gasPrice = await gasPriceServiceMap[mumbai]?.getGasPrice(GasPriceType.DEFAULT); -// expect(gasPrice).not.toBeUndefined(); -// expect(typeof gasPrice).toBe('object'); -// expect(customJSONStringify(gasPrice)).toBe(customJSONStringify(expectedOutput)); -// }); - -// it(`should get gas price from Network for chainId: ${goerli}`, async () => { -// jest.spyOn(cacheService, 'get').mockReturnValue(Promise.resolve('')); -// const gasPrice = await gasPriceServiceMap[goerli]?.getGasPrice(GasPriceType.DEFAULT); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// expect(gasPrice).toBe('20000000'); -// }); - -// it('should get gas price from network for eip1559 supported chains', async () => { -// jest.spyOn(cacheService, 'get').mockReturnValue(Promise.resolve('')); - -// const expectedOutput = { -// maxFeePerGas: '20000000', -// maxPriorityFeePerGas: '20000000', -// }; - -// const gasPrice = await gasPriceServiceMap[mumbai]?.getGasPrice(GasPriceType.DEFAULT); -// expect(gasPrice).not.toBeUndefined(); -// expect(typeof gasPrice).toBe('object'); -// expect(customJSONStringify(gasPrice)).toBe(customJSONStringify(expectedOutput)); -// }); - -// it(`should get GasPrice from CACHE while calling getGasPriceForSimulation for chainId: ${goerli}`, async () => { -// jest.spyOn(cacheService, 'get').mockReturnValue(Promise.resolve('1000000000')); -// const gasPrice = await gasPriceServiceMap[goerli]?.getGasPriceForSimulation( -// GasPriceType.DEFAULT, -// ); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// expect(gasPrice).toBe('1000000000'); -// }); - -// it(`should get GasPrice from NETWORK while calling getGasPriceForSimulation for chainId: ${goerli}`, async () => { -// jest.spyOn(cacheService, 'get').mockReturnValue(Promise.resolve('')); -// const gasPrice = await gasPriceServiceMap[goerli]?.getGasPriceForSimulation( -// GasPriceType.DEFAULT, -// ); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// expect(gasPrice).toBe('20000000'); -// }); - -// it(`should call getBumpedUpGasPrice for non-eip1559 transaction with bumpup % < 1.1 for chainId: ${goerli}`, async () => { -// const pastGasPrice = '20000000'; -// const bumpingPercentage = 9; -// const gasPrice = await gasPriceServiceMap[goerli]?.getBumpedUpGasPrice( -// pastGasPrice, -// bumpingPercentage, -// ); - -// const resubmitGasPrice = 1.1 * Number(pastGasPrice); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// expect(gasPrice).toBe(ethers.BigNumber.from(resubmitGasPrice)._hex); -// }); - -// it(`should call getBumpedUpGasPrice for non-eip1559 transaction with bumpup % > 1.1 for chainId: ${goerli}`, async () => { -// const pastGasPrice = '20000000'; -// const bumpingPercentage = 20; -// const gasPrice = await gasPriceServiceMap[goerli]?.getBumpedUpGasPrice( -// pastGasPrice, -// bumpingPercentage, -// ); - -// const resubmitGasPrice = ethers.utils.hexValue( -// ethers.BigNumber.from(pastGasPrice) -// .mul(bumpingPercentage + 100) -// .div(100), -// ); -// expect(gasPrice).not.toBeNull(); -// expect(typeof gasPrice).toBe('string'); -// expect(Number(gasPrice)).toBeGreaterThan(0); -// expect(gasPrice).toBe(ethers.BigNumber.from(resubmitGasPrice)._hex); -// }); - -// it(`should call getBumpedUpGasPrice for eip1559 transaction with bumpup % > 1.1 for chainId: ${goerli}`, async () => { -// const pastGasPrice = { -// maxFeePerGas: '1000000000', -// maxPriorityFeePerGas: '1000000000', -// }; -// const bumpingPercentage = 20; -// const gasPrice = await gasPriceServiceMap[mumbai]?.getBumpedUpGasPrice( -// pastGasPrice, -// bumpingPercentage, -// ); - -// const bumpedUpMaxPriorityFeePerGas = ethers.utils.hexValue( -// ethers.BigNumber.from(pastGasPrice.maxPriorityFeePerGas) -// .mul(bumpingPercentage + 100) -// .div(100), -// ); - -// const bumpedUpMaxFeePerGas = ethers.utils.hexValue( -// ethers.BigNumber.from(pastGasPrice.maxFeePerGas) -// .mul(bumpingPercentage + 100) -// .div(100), -// ); - -// const expectedGasPrice = { -// maxFeePerGas: bumpedUpMaxFeePerGas, -// maxPriorityFeePerGas: -// bumpedUpMaxPriorityFeePerGas, -// }; - -// expect(gasPrice).not.toBeUndefined(); -// expect(typeof gasPrice).toBe('object'); -// expect(customJSONStringify(gasPrice)).toBe(customJSONStringify(expectedGasPrice)); -// }); - -// it(`should call getBumpedUpGasPrice for eip1559 transaction with bumpup % < 1.1 for chainId: ${goerli}`, async () => { -// const pastGasPrice = { -// maxFeePerGas: '1000000000', -// maxPriorityFeePerGas: '1000000000', -// }; -// const bumpingPercentage = 9; -// const gasPrice = await gasPriceServiceMap[mumbai]?.getBumpedUpGasPrice( -// pastGasPrice, -// bumpingPercentage, -// ); - -// const expectedGasPrice = { -// maxFeePerGas: ethers.BigNumber.from(( -// Number(pastGasPrice.maxFeePerGas) * 1.11 -// ).toString()).toHexString(), -// maxPriorityFeePerGas: ethers.BigNumber.from(( -// Number(pastGasPrice.maxPriorityFeePerGas) * 1.11 -// ).toString()).toHexString(), -// }; - -// expect(gasPrice).not.toBeUndefined(); -// expect(typeof gasPrice).toBe('object'); -// expect(customJSONStringify(gasPrice)).toBe(customJSONStringify(expectedGasPrice)); -// }); - -// it(`should set setMaxFeeGasPrice price in redis for chainId: ${goerli}, if type is STRING`, async () => { -// const maxFeePerGas = '20000'; -// await gasPriceServiceMap[goerli]?.setMaxFeeGasPrice(GasPriceType.DEFAULT, maxFeePerGas); - -// jest.spyOn(cacheService, 'get').mockReturnValue(Promise.resolve(maxFeePerGas)); -// const getMaxFeeFas = await gasPriceServiceMap[goerli]?.getMaxFeeGasPrice( -// GasPriceType.DEFAULT, -// ); - -// expect(getMaxFeeFas).toBe(maxFeePerGas); -// }); -// }); diff --git a/src/test/unit/mocks/mockCache.ts b/src/test/unit/mocks/mockCache.ts deleted file mode 100644 index 5c2936d7..00000000 --- a/src/test/unit/mocks/mockCache.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* eslint-disable import/no-import-module-exports */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable class-methods-use-this */ -/* eslint-disable @typescript-eslint/indent */ -import Redlock, { Lock } from "redlock"; -import { ICacheService } from "../../../common/cache/interface"; -import { logger } from "../../../common/logger"; - -const log = logger.child({ - module: module.filename.split("/").slice(-4).join("/"), -}); - -export class MockCache implements ICacheService { - redisClient: any; - - release() { - this.redisClient = null; - } - - async connect(): Promise { - log.info("Initiating Redis connection"); - } - - close(): Promise { - throw new Error("Method not implemented."); - } - - get(key: string): Promise { - return Promise.resolve("1000000000"); - } - - set( - key: string, - value: string, - hideValueInLogs?: boolean | undefined, - ): Promise { - return Promise.resolve(true); - } - - increment(key: string, incrementBy?: number | undefined): Promise { - throw new Error("Method not implemented."); - } - - decrement(key: string, decrementBy?: number | undefined): Promise { - throw new Error("Method not implemented."); - } - - expire(key: string, expiryTime: number): Promise { - return Promise.resolve(false); - } - - delete(key: string): Promise { - return Promise.resolve(true); - } - - getRedLock(): any { - return { - acquire: async (resources: string[], duration: number) => true, - }; - } - - unlockRedLock(lock: Lock): Promise { - throw new Error("Method not implemented."); - } - - connectRedLock(): Redlock { - return new Redlock([this.redisClient], { - // the expected clock drift; for more details - // see http://redis.io/topics/distlock - driftFactor: 0.01, // multiplied by lock ttl to determine drift time - - // the max number of times Redlock will attempt - // to lock a resource before erroring - retryCount: 5, - - // the time in ms between attempts - retryDelay: 8000, // time in ms - - // the max time in ms randomly added to retries - // to improve performance under high contention - // see https://www.awsarchitectureblog.com/2015/03/backoff.html - retryJitter: 200, // time in ms - }); - } -} diff --git a/src/test/unit/mocks/mockEVMAccount.ts b/src/test/unit/mocks/mockEVMAccount.ts index c11cb288..175f13f4 100644 --- a/src/test/unit/mocks/mockEVMAccount.ts +++ b/src/test/unit/mocks/mockEVMAccount.ts @@ -1,4 +1,4 @@ -// /* eslint-disable @typescript-eslint/no-unused-vars */ +// // import { EVMRawTransactionType } from "../../../common/types"; // import { IEVMAccount } from "../../../relayer/account"; diff --git a/src/test/unit/mocks/mockGasPrice.ts b/src/test/unit/mocks/mockGasPrice.ts index a2ad5525..05c82ec3 100644 --- a/src/test/unit/mocks/mockGasPrice.ts +++ b/src/test/unit/mocks/mockGasPrice.ts @@ -1,6 +1,6 @@ // /* eslint-disable max-len */ // /* eslint-disable class-methods-use-this */ -// /* eslint-disable @typescript-eslint/no-unused-vars */ +// // import { extend } from 'lodash'; // import { ICacheService } from '../../../common/cache'; // import { IGasPriceService } from '../../../common/gas-price'; diff --git a/src/test/unit/mocks/mockJsonProvider.ts b/src/test/unit/mocks/mockJsonProvider.ts deleted file mode 100644 index 24178336..00000000 --- a/src/test/unit/mocks/mockJsonProvider.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -/* eslint-disable class-methods-use-this */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable max-len */ -import { JsonRpcProvider } from "@ethersproject/providers"; -import { BigNumber, ethers } from "ethers"; - -export class MockJsonProvider extends JsonRpcProvider { - estimateGas( - transaction: ethers.utils.Deferrable, - ): Promise { - return Promise.resolve(BigNumber.from(1)); - } -} diff --git a/src/test/unit/mocks/mockNetworkService.ts b/src/test/unit/mocks/mockNetworkService.ts index fd74dd5b..c2bc1551 100644 --- a/src/test/unit/mocks/mockNetworkService.ts +++ b/src/test/unit/mocks/mockNetworkService.ts @@ -1,8 +1,8 @@ // /* eslint-disable no-new-object */ -// /* eslint-disable @typescript-eslint/no-unused-vars */ +// // /* eslint-disable class-methods-use-this */ // /* eslint-disable @typescript-eslint/space-before-blocks */ -// /* eslint-disable import/no-extraneous-dependencies */ +// // /* eslint-disable @typescript-eslint/indent */ // import { JsonRpcProvider, TransactionResponse, TransactionReceipt } from '@ethersproject/providers'; // import { BigNumber, Contract } from 'ethers'; diff --git a/src/test/unit/mocks/mockNetworkServiceFail.ts b/src/test/unit/mocks/mockNetworkServiceFail.ts index 2435ec2d..371f8b1c 100644 --- a/src/test/unit/mocks/mockNetworkServiceFail.ts +++ b/src/test/unit/mocks/mockNetworkServiceFail.ts @@ -1,7 +1,7 @@ -// /* eslint-disable @typescript-eslint/no-unused-vars */ +// // /* eslint-disable class-methods-use-this */ // /* eslint-disable @typescript-eslint/space-before-blocks */ -// /* eslint-disable import/no-extraneous-dependencies */ +// // /* eslint-disable @typescript-eslint/indent */ // import { JsonRpcProvider, TransactionResponse, TransactionReceipt } from '@ethersproject/providers'; // import { BigNumber, Contract } from 'ethers'; diff --git a/src/test/unit/network-service/network-service.test.ts b/src/test/unit/network-service/network-service.test.ts index b8bda5a3..d93a4278 100644 --- a/src/test/unit/network-service/network-service.test.ts +++ b/src/test/unit/network-service/network-service.test.ts @@ -1,7 +1,7 @@ describe("broken test suite", () => { it("should be fixed", () => {}); }); -// /* eslint-disable @typescript-eslint/no-unused-vars */ +// // /* eslint-disable max-len */ // import { ethers } from 'ethers'; // import axios from 'axios'; diff --git a/src/test/unit/notification/notification-manager.test.ts b/src/test/unit/notification/notification-manager.test.ts deleted file mode 100644 index c0edf987..00000000 --- a/src/test/unit/notification/notification-manager.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { NotificationManager } from "../../../common/notification"; -import { SlackNotificationService } from "../../../common/notification/slack/SlackNotificationService"; - -const dummySlackChannel = "C04AQ36PJM_"; -const dummySlackToken = "xoxp-1234567890-1234567890-1234567890-abcdef"; - -describe("NotificationManager", () => { - let notificationManager: NotificationManager; - let slackNotificationService: SlackNotificationService; - - beforeAll(async () => { - // Create a mock instance of the class that contains the postMessage method - slackNotificationService = new SlackNotificationService( - dummySlackToken, - dummySlackChannel, - ); - - notificationManager = new NotificationManager(slackNotificationService); - }); - - afterEach(async () => { - jest.resetAllMocks(); - jest.clearAllMocks(); - }); - - it("constructor initializes web client with correct token and channel", () => { - expect(notificationManager.slackNotificationService).toBeInstanceOf( - SlackNotificationService, - ); - }); - - it("should call getSlackNotifyObject()", () => { - const expectedOutput = { - data: { - text: "test", - channel: dummySlackChannel, - }, - }; - jest - .spyOn(notificationManager.slackNotificationService, "getNotifyObject") - .mockReturnValueOnce(expectedOutput); - - const result = notificationManager.getSlackNotifyObject("test"); - expect(result).toEqual(expectedOutput); - }); - - it("should call sendSlackNotification()", () => { - const methodInput = { - data: { - text: "test", - channel: dummySlackChannel, - }, - }; - jest - .spyOn(notificationManager.slackNotificationService, "notify") - .mockImplementationOnce(() => Promise.resolve()); - - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - expect(notificationManager.sendSlackNotification(methodInput)).resolves; - }); -}); diff --git a/src/test/unit/notification/slack-notification.test.ts b/src/test/unit/notification/slack-notification.test.ts deleted file mode 100644 index 67947262..00000000 --- a/src/test/unit/notification/slack-notification.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -describe("broken test", () => { - it.todo("should be fixed"); -}); -// /* eslint-disable @typescript-eslint/no-unused-expressions */ -// import { SlackNotificationService } from "../../../common/notification/slack/SlackNotificationService"; - -// const dummySlackChannel = "C04AQ36PJM_"; -// const dummySlackToken = "xoxp-1234567890-1234567890-1234567890-abcdef"; - -// describe("SlackNotificationService", () => { -// let slackNotificationService: SlackNotificationService; -// let mockPostMessage: jest.Mock; - -// beforeAll(async () => { -// // Create a mock instance of the class that contains the postMessage method -// slackNotificationService = new SlackNotificationService( -// dummySlackToken, -// dummySlackChannel, -// ); -// }); - -// afterEach(async () => { -// jest.resetAllMocks(); -// jest.clearAllMocks(); -// }); - -// it("constructor initializes web client with correct token and channel", () => { -// const slackChannel = "#test-channel"; - -// // Create an instance of the class with the slack token and channel -// let classInstance; -// try { -// classInstance = new SlackNotificationService("", slackChannel); -// } catch (error) { -// expect(classInstance).toThrowError; -// } -// }); - -// it("should call postMessage with the correct arguments", async () => { -// // Define the mock parameters for the Slack message -// const postSlackMessageParams = { -// channel: "#test-channel", -// text: "test message", -// }; - -// jest -// .spyOn(slackNotificationService.web.chat, "postMessage") -// .mockReturnThis(); - -// // Call the postMessage method on the mock class instance -// await slackNotificationService.postMessage(postSlackMessageParams); - -// // Assert that the postMessage method was called with the correct parameters -// expect(slackNotificationService.postMessage).resolves; -// }); - -// it("should call notify with the correct arguments", async () => { -// mockPostMessage = jest.fn(); -// slackNotificationService.postMessage = mockPostMessage; -// const input = { data: { text: "unit test case" } }; -// await slackNotificationService.notify(input); -// expect(mockPostMessage).toHaveBeenCalledWith({ -// text: "unit test case", -// channel: dummySlackChannel, -// }); -// }); - -// it("should call getNotifyObject with the correct arguments", async () => { -// const text = "unit test case"; - -// // Call the getNotifyObject method on the mock class instance -// const result = await slackNotificationService.getNotifyObject(text); - -// // Assert that the returned object is as expected -// expect(result).toEqual({ -// data: { -// text, -// channel: dummySlackChannel, -// }, -// }); -// }); -// }); diff --git a/yarn.lock b/yarn.lock index 71c515a7..52419718 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@aashutoshrathi/word-wrap@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" - integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - "@acuminous/bitsyntax@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz#e0b31b9ee7ad1e4dd840c34864327c33d9f1f653" @@ -16,10 +11,10 @@ debug "^4.3.4" safe-buffer "~5.1.2" -"@adraffy/ens-normalize@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" - integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== +"@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== "@ampproject/remapping@^2.2.0": version "2.3.0" @@ -29,658 +24,114 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@arbitrum/sdk@^3.1.3": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.3.3.tgz#9a49e753b9deeee6336f6a43acb0669961b91128" - integrity sha512-Fe/+h2gs78Efl94U7yXkYAN+FQLLAxDGj+kv6+CXbnUytso+PBvKgNj1e4YPwgtWLlDomdcOXFt/yaP4VKsrBQ== - dependencies: - "@ethersproject/address" "^5.0.8" - "@ethersproject/bignumber" "^5.1.1" - "@ethersproject/bytes" "^5.0.8" - async-mutex "^0.4.0" - ethers "^5.1.0" - -"@aws-crypto/crc32@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa" - integrity sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA== - dependencies: - "@aws-crypto/util" "^3.0.0" - "@aws-sdk/types" "^3.222.0" - tslib "^1.11.1" - -"@aws-crypto/ie11-detection@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz#640ae66b4ec3395cee6a8e94ebcd9f80c24cd688" - integrity sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q== - dependencies: - tslib "^1.11.1" - -"@aws-crypto/sha256-browser@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz#05f160138ab893f1c6ba5be57cfd108f05827766" - integrity sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ== - dependencies: - "@aws-crypto/ie11-detection" "^3.0.0" - "@aws-crypto/sha256-js" "^3.0.0" - "@aws-crypto/supports-web-crypto" "^3.0.0" - "@aws-crypto/util" "^3.0.0" - "@aws-sdk/types" "^3.222.0" - "@aws-sdk/util-locate-window" "^3.0.0" - "@aws-sdk/util-utf8-browser" "^3.0.0" - tslib "^1.11.1" - -"@aws-crypto/sha256-js@3.0.0", "@aws-crypto/sha256-js@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz#f06b84d550d25521e60d2a0e2a90139341e007c2" - integrity sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ== - dependencies: - "@aws-crypto/util" "^3.0.0" - "@aws-sdk/types" "^3.222.0" - tslib "^1.11.1" - -"@aws-crypto/supports-web-crypto@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz#5d1bf825afa8072af2717c3e455f35cda0103ec2" - integrity sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg== - dependencies: - tslib "^1.11.1" - -"@aws-crypto/util@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-3.0.0.tgz#1c7ca90c29293f0883468ad48117937f0fe5bfb0" - integrity sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w== - dependencies: - "@aws-sdk/types" "^3.222.0" - "@aws-sdk/util-utf8-browser" "^3.0.0" - tslib "^1.11.1" - -"@aws-sdk/client-cognito-identity@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.540.0.tgz#ccea36dd4650bdbd0fd2bcf02c3b1fb33ba154eb" - integrity sha512-03vUaIKjvdcOmjDi8Fv9JgY+VQrt9QBpRkI8A1lrdPNgWqTEZXZi/zBsFRsxTe6hgsrZtxVnxLu6krSRILuqtw== - dependencies: - "@aws-crypto/sha256-browser" "3.0.0" - "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/client-sts" "3.540.0" - "@aws-sdk/core" "3.535.0" - "@aws-sdk/credential-provider-node" "3.540.0" - "@aws-sdk/middleware-host-header" "3.535.0" - "@aws-sdk/middleware-logger" "3.535.0" - "@aws-sdk/middleware-recursion-detection" "3.535.0" - "@aws-sdk/middleware-user-agent" "3.540.0" - "@aws-sdk/region-config-resolver" "3.535.0" - "@aws-sdk/types" "3.535.0" - "@aws-sdk/util-endpoints" "3.540.0" - "@aws-sdk/util-user-agent-browser" "3.535.0" - "@aws-sdk/util-user-agent-node" "3.535.0" - "@smithy/config-resolver" "^2.2.0" - "@smithy/core" "^1.4.0" - "@smithy/fetch-http-handler" "^2.5.0" - "@smithy/hash-node" "^2.2.0" - "@smithy/invalid-dependency" "^2.2.0" - "@smithy/middleware-content-length" "^2.2.0" - "@smithy/middleware-endpoint" "^2.5.0" - "@smithy/middleware-retry" "^2.2.0" - "@smithy/middleware-serde" "^2.3.0" - "@smithy/middleware-stack" "^2.2.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/node-http-handler" "^2.5.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/url-parser" "^2.2.0" - "@smithy/util-base64" "^2.3.0" - "@smithy/util-body-length-browser" "^2.2.0" - "@smithy/util-body-length-node" "^2.3.0" - "@smithy/util-defaults-mode-browser" "^2.2.0" - "@smithy/util-defaults-mode-node" "^2.3.0" - "@smithy/util-endpoints" "^1.2.0" - "@smithy/util-middleware" "^2.2.0" - "@smithy/util-retry" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" - -"@aws-sdk/client-sso-oidc@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.540.0.tgz#e4c52889d33ca969add269011b790f2d634fb6d2" - integrity sha512-LZYK0lBRQK8D8M3Sqc96XiXkAV2v70zhTtF6weyzEpgwxZMfSuFJjs0jFyhaeZBZbZv7BBghIdhJ5TPavNxGMQ== - dependencies: - "@aws-crypto/sha256-browser" "3.0.0" - "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/client-sts" "3.540.0" - "@aws-sdk/core" "3.535.0" - "@aws-sdk/middleware-host-header" "3.535.0" - "@aws-sdk/middleware-logger" "3.535.0" - "@aws-sdk/middleware-recursion-detection" "3.535.0" - "@aws-sdk/middleware-user-agent" "3.540.0" - "@aws-sdk/region-config-resolver" "3.535.0" - "@aws-sdk/types" "3.535.0" - "@aws-sdk/util-endpoints" "3.540.0" - "@aws-sdk/util-user-agent-browser" "3.535.0" - "@aws-sdk/util-user-agent-node" "3.535.0" - "@smithy/config-resolver" "^2.2.0" - "@smithy/core" "^1.4.0" - "@smithy/fetch-http-handler" "^2.5.0" - "@smithy/hash-node" "^2.2.0" - "@smithy/invalid-dependency" "^2.2.0" - "@smithy/middleware-content-length" "^2.2.0" - "@smithy/middleware-endpoint" "^2.5.0" - "@smithy/middleware-retry" "^2.2.0" - "@smithy/middleware-serde" "^2.3.0" - "@smithy/middleware-stack" "^2.2.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/node-http-handler" "^2.5.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/url-parser" "^2.2.0" - "@smithy/util-base64" "^2.3.0" - "@smithy/util-body-length-browser" "^2.2.0" - "@smithy/util-body-length-node" "^2.3.0" - "@smithy/util-defaults-mode-browser" "^2.2.0" - "@smithy/util-defaults-mode-node" "^2.3.0" - "@smithy/util-endpoints" "^1.2.0" - "@smithy/util-middleware" "^2.2.0" - "@smithy/util-retry" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" - -"@aws-sdk/client-sso@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.540.0.tgz#732a7f325de3905a719c20ce05e555b445f82b4a" - integrity sha512-rrQZMuw4sxIo3eyAUUzPQRA336mPRnrAeSlSdVHBKZD8Fjvoy0lYry2vNhkPLpFZLso1J66KRyuIv4LzRR3v1Q== - dependencies: - "@aws-crypto/sha256-browser" "3.0.0" - "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/core" "3.535.0" - "@aws-sdk/middleware-host-header" "3.535.0" - "@aws-sdk/middleware-logger" "3.535.0" - "@aws-sdk/middleware-recursion-detection" "3.535.0" - "@aws-sdk/middleware-user-agent" "3.540.0" - "@aws-sdk/region-config-resolver" "3.535.0" - "@aws-sdk/types" "3.535.0" - "@aws-sdk/util-endpoints" "3.540.0" - "@aws-sdk/util-user-agent-browser" "3.535.0" - "@aws-sdk/util-user-agent-node" "3.535.0" - "@smithy/config-resolver" "^2.2.0" - "@smithy/core" "^1.4.0" - "@smithy/fetch-http-handler" "^2.5.0" - "@smithy/hash-node" "^2.2.0" - "@smithy/invalid-dependency" "^2.2.0" - "@smithy/middleware-content-length" "^2.2.0" - "@smithy/middleware-endpoint" "^2.5.0" - "@smithy/middleware-retry" "^2.2.0" - "@smithy/middleware-serde" "^2.3.0" - "@smithy/middleware-stack" "^2.2.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/node-http-handler" "^2.5.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/url-parser" "^2.2.0" - "@smithy/util-base64" "^2.3.0" - "@smithy/util-body-length-browser" "^2.2.0" - "@smithy/util-body-length-node" "^2.3.0" - "@smithy/util-defaults-mode-browser" "^2.2.0" - "@smithy/util-defaults-mode-node" "^2.3.0" - "@smithy/util-endpoints" "^1.2.0" - "@smithy/util-middleware" "^2.2.0" - "@smithy/util-retry" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" - -"@aws-sdk/client-sts@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.540.0.tgz#16ce14db1c5387be3ad9be6dd4f8ed33b63193c8" - integrity sha512-ITHUQxvpqfQX6obfpIi3KYGzZYfe/I5Ixjfxoi5lB7ISCtmxqObKB1fzD93wonkMJytJ7LUO8panZl/ojiJ1uw== - dependencies: - "@aws-crypto/sha256-browser" "3.0.0" - "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/core" "3.535.0" - "@aws-sdk/middleware-host-header" "3.535.0" - "@aws-sdk/middleware-logger" "3.535.0" - "@aws-sdk/middleware-recursion-detection" "3.535.0" - "@aws-sdk/middleware-user-agent" "3.540.0" - "@aws-sdk/region-config-resolver" "3.535.0" - "@aws-sdk/types" "3.535.0" - "@aws-sdk/util-endpoints" "3.540.0" - "@aws-sdk/util-user-agent-browser" "3.535.0" - "@aws-sdk/util-user-agent-node" "3.535.0" - "@smithy/config-resolver" "^2.2.0" - "@smithy/core" "^1.4.0" - "@smithy/fetch-http-handler" "^2.5.0" - "@smithy/hash-node" "^2.2.0" - "@smithy/invalid-dependency" "^2.2.0" - "@smithy/middleware-content-length" "^2.2.0" - "@smithy/middleware-endpoint" "^2.5.0" - "@smithy/middleware-retry" "^2.2.0" - "@smithy/middleware-serde" "^2.3.0" - "@smithy/middleware-stack" "^2.2.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/node-http-handler" "^2.5.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/url-parser" "^2.2.0" - "@smithy/util-base64" "^2.3.0" - "@smithy/util-body-length-browser" "^2.2.0" - "@smithy/util-body-length-node" "^2.3.0" - "@smithy/util-defaults-mode-browser" "^2.2.0" - "@smithy/util-defaults-mode-node" "^2.3.0" - "@smithy/util-endpoints" "^1.2.0" - "@smithy/util-middleware" "^2.2.0" - "@smithy/util-retry" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" - -"@aws-sdk/core@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.535.0.tgz#f3a726c297cea9634d19a1db4e958c918c506c8b" - integrity sha512-+Yusa9HziuaEDta1UaLEtMAtmgvxdxhPn7jgfRY6PplqAqgsfa5FR83sxy5qr2q7xjQTwHtV4MjQVuOjG9JsLw== - dependencies: - "@smithy/core" "^1.4.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/signature-v4" "^2.2.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - fast-xml-parser "4.2.5" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-cognito-identity@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.540.0.tgz#9b6a309d3809a6a056b6b6c9e7ac580cda19db31" - integrity sha512-XOTAIuVgticX+43GMpRbi5OHmJAhHfoHYsVGu0eRLhri1yFqUHXJgHUd51QQtlA8cFQN7JnFFM6sF5EDCPF49g== - dependencies: - "@aws-sdk/client-cognito-identity" "3.540.0" - "@aws-sdk/types" "3.535.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-env@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.535.0.tgz#26248e263a8107953d5496cb3760d4e7c877abcf" - integrity sha512-XppwO8c0GCGSAvdzyJOhbtktSEaShg14VJKg8mpMa1XcgqzmcqqHQjtDWbx5rZheY1VdpXZhpEzJkB6LpQejpA== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-http@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.535.0.tgz#0a42f6b1a61d927bbce9f4afd25112f486bd05da" - integrity sha512-kdj1wCmOMZ29jSlUskRqN04S6fJ4dvt0Nq9Z32SA6wO7UG8ht6Ot9h/au/eTWJM3E1somZ7D771oK7dQt9b8yw== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/fetch-http-handler" "^2.5.0" - "@smithy/node-http-handler" "^2.5.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/util-stream" "^2.2.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-ini@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.540.0.tgz#8e17b23bf242152775db1473f7d2952beb6a5ef9" - integrity sha512-igN/RbsnulIBwqXbwsWmR3srqmtbPF1dm+JteGvUY31FW65fTVvWvSr945Y/cf1UbhPmIQXntlsqESqpkhTHwg== - dependencies: - "@aws-sdk/client-sts" "3.540.0" - "@aws-sdk/credential-provider-env" "3.535.0" - "@aws-sdk/credential-provider-process" "3.535.0" - "@aws-sdk/credential-provider-sso" "3.540.0" - "@aws-sdk/credential-provider-web-identity" "3.540.0" - "@aws-sdk/types" "3.535.0" - "@smithy/credential-provider-imds" "^2.3.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/shared-ini-file-loader" "^2.4.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-node@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.540.0.tgz#e6fd3404de68e7f9580f01aa542b16e9abc58e5c" - integrity sha512-HKQZJbLHlrHX9A0B1poiYNXIIQfy8whTjuosTCYKPDBhhUyVAQfxy/KG726j0v43IhaNPLgTGZCJve4hAsazSw== - dependencies: - "@aws-sdk/credential-provider-env" "3.535.0" - "@aws-sdk/credential-provider-http" "3.535.0" - "@aws-sdk/credential-provider-ini" "3.540.0" - "@aws-sdk/credential-provider-process" "3.535.0" - "@aws-sdk/credential-provider-sso" "3.540.0" - "@aws-sdk/credential-provider-web-identity" "3.540.0" - "@aws-sdk/types" "3.535.0" - "@smithy/credential-provider-imds" "^2.3.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/shared-ini-file-loader" "^2.4.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-process@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.535.0.tgz#ea1e8a38a32e36bbdc3f75eb03352e6eafa0c659" - integrity sha512-9O1OaprGCnlb/kYl8RwmH7Mlg8JREZctB8r9sa1KhSsWFq/SWO0AuJTyowxD7zL5PkeS4eTvzFFHWCa3OO5epA== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/shared-ini-file-loader" "^2.4.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-sso@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.540.0.tgz#1fc5c53a0df8227249c73a3cb7660b1accb79186" - integrity sha512-tKkFqK227LF5ajc5EL6asXS32p3nkofpP8G7NRpU7zOEOQCg01KUc4JRX+ItI0T007CiN1J19yNoFqHLT/SqHg== - dependencies: - "@aws-sdk/client-sso" "3.540.0" - "@aws-sdk/token-providers" "3.540.0" - "@aws-sdk/types" "3.535.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/shared-ini-file-loader" "^2.4.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-web-identity@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.540.0.tgz#775a2090e9f4f89efe2ebdf1e2c109a47561c0e9" - integrity sha512-OpDm9w3A168B44hSjpnvECP4rvnFzD86rN4VYdGADuCvEa5uEcdA/JuT5WclFPDqdWEmFBqS1pxBIJBf0g2Q9Q== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: - "@aws-sdk/client-sts" "3.540.0" - "@aws-sdk/types" "3.535.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/credential-providers@^3.186.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-providers/-/credential-providers-3.540.0.tgz#649725589d210bc1237e2d3d8893302ef3a79ecf" - integrity sha512-tAmvqdZngCrER5/AAwTmDSjO05LGIshKL+lwcJr2OUV5jtQVzfbFrorf+b5dnI+3i8+zGcEAV9omra4XGrO9Kg== - dependencies: - "@aws-sdk/client-cognito-identity" "3.540.0" - "@aws-sdk/client-sso" "3.540.0" - "@aws-sdk/client-sts" "3.540.0" - "@aws-sdk/credential-provider-cognito-identity" "3.540.0" - "@aws-sdk/credential-provider-env" "3.535.0" - "@aws-sdk/credential-provider-http" "3.535.0" - "@aws-sdk/credential-provider-ini" "3.540.0" - "@aws-sdk/credential-provider-node" "3.540.0" - "@aws-sdk/credential-provider-process" "3.535.0" - "@aws-sdk/credential-provider-sso" "3.540.0" - "@aws-sdk/credential-provider-web-identity" "3.540.0" - "@aws-sdk/types" "3.535.0" - "@smithy/credential-provider-imds" "^2.3.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-host-header@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.535.0.tgz#d5264f813592f5e77df25e5a14bbb0e6441812db" - integrity sha512-0h6TWjBWtDaYwHMQJI9ulafeS4lLaw1vIxRjbpH0svFRt6Eve+Sy8NlVhECfTU2hNz/fLubvrUxsXoThaLBIew== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-logger@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.535.0.tgz#1a8ffd6c368edd6cb32e1edf7b1dced95c1820ee" - integrity sha512-huNHpONOrEDrdRTvSQr1cJiRMNf0S52NDXtaPzdxiubTkP+vni2MohmZANMOai/qT0olmEVX01LhZ0ZAOgmg6A== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-recursion-detection@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.535.0.tgz#6aa1e1bd1e84730d58a73021b745e20d4341a92d" - integrity sha512-am2qgGs+gwqmR4wHLWpzlZ8PWhm4ktj5bYSgDrsOfjhdBlWNxvPoID9/pDAz5RWL48+oH7I6SQzMqxXsFDikrw== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/middleware-user-agent@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.540.0.tgz#4981c64c1eeb6b5c453bce02d060b8c71d44994d" - integrity sha512-8Rd6wPeXDnOYzWj1XCmOKcx/Q87L0K1/EHqOBocGjLVbN3gmRxBvpmR1pRTjf7IsWfnnzN5btqtcAkfDPYQUMQ== - dependencies: - "@aws-sdk/types" "3.535.0" - "@aws-sdk/util-endpoints" "3.540.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/region-config-resolver@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.535.0.tgz#20a30fb5fbbe27ab70f2ed16327bae7e367b5cec" - integrity sha512-IXOznDiaItBjsQy4Fil0kzX/J3HxIOknEphqHbOfUf+LpA5ugcsxuQQONrbEQusCBnfJyymrldBvBhFmtlU9Wg== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/types" "^2.12.0" - "@smithy/util-config-provider" "^2.3.0" - "@smithy/util-middleware" "^2.2.0" - tslib "^2.6.2" - -"@aws-sdk/token-providers@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.540.0.tgz#06fb874a62d3c496875768ac648bc6cca4c75a79" - integrity sha512-9BvtiVEZe5Ev88Wa4ZIUbtT6BVcPwhxmVInQ6c12MYNb0WNL54BN6wLy/eknAfF05gpX2/NDU2pUDOyMPdm/+g== - dependencies: - "@aws-sdk/client-sso-oidc" "3.540.0" - "@aws-sdk/types" "3.535.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/shared-ini-file-loader" "^2.4.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/types@3.535.0", "@aws-sdk/types@^3.222.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.535.0.tgz#5e6479f31299dd9df170e63f4d10fe739008cf04" - integrity sha512-aY4MYfduNj+sRR37U7XxYR8wemfbKP6lx00ze2M2uubn7mZotuVrWYAafbMSXrdEMSToE5JDhr28vArSOoLcSg== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/util-endpoints@3.540.0": - version "3.540.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.540.0.tgz#a7fea1d2a5e64623353aaa6ee32dbb86ab9cd3f8" - integrity sha512-1kMyQFAWx6f8alaI6UT65/5YW/7pDWAKAdNwL6vuJLea03KrZRX3PMoONOSJpAS5m3Ot7HlWZvf3wZDNTLELZw== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/types" "^2.12.0" - "@smithy/util-endpoints" "^1.2.0" - tslib "^2.6.2" - -"@aws-sdk/util-locate-window@^3.0.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.535.0.tgz#0200a336fddd47dd6567ce15d01f62be50a315d7" - integrity sha512-PHJ3SL6d2jpcgbqdgiPxkXpu7Drc2PYViwxSIqvvMKhDwzSB1W3mMvtpzwKM4IE7zLFodZo0GKjJ9AsoXndXhA== - dependencies: - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-browser@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.535.0.tgz#d67d72e8b933051620f18ddb1c2be225f79f588f" - integrity sha512-RWMcF/xV5n+nhaA/Ff5P3yNP3Kur/I+VNZngog4TEs92oB/nwOdAg/2JL8bVAhUbMrjTjpwm7PItziYFQoqyig== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/types" "^2.12.0" - bowser "^2.11.0" - tslib "^2.6.2" - -"@aws-sdk/util-user-agent-node@3.535.0": - version "3.535.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.535.0.tgz#f5c26fb6f3f561d3cf35f96f303b1775afad0a5b" - integrity sha512-dRek0zUuIT25wOWJlsRm97nTkUlh1NDcLsQZIN2Y8KxhwoXXWtJs5vaDPT+qAg+OpcNj80i1zLR/CirqlFg/TQ== - dependencies: - "@aws-sdk/types" "3.535.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@aws-sdk/util-utf8-browser@^3.0.0": - version "3.259.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz#3275a6f5eb334f96ca76635b961d3c50259fd9ff" - integrity sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw== - dependencies: - tslib "^2.3.1" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": - version "7.24.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" - integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== - dependencies: - "@babel/highlight" "^7.24.2" + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.23.5": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.1.tgz#31c1f66435f2a9c329bb5716a6d6186c516c3742" - integrity sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA== +"@babel/compat-data@^7.25.9": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e" + integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg== "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.3.tgz#568864247ea10fbd4eff04dda1e05f9e2ea985c3" - integrity sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ== + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.24.2" - "@babel/generator" "^7.24.1" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.24.1" - "@babel/parser" "^7.24.1" - "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.1" - "@babel/types" "^7.24.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" 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.24.1", "@babel/generator@^7.7.2": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.1.tgz#e67e06f68568a4ebf194d1c6014235344f0476d0" - integrity sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A== +"@babel/generator@^7.25.9", "@babel/generator@^7.26.0", "@babel/generator@^7.7.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f" + integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw== dependencies: - "@babel/types" "^7.24.0" + "@babel/parser" "^7.26.2" + "@babel/types" "^7.26.0" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" + jsesc "^3.0.2" -"@babel/helper-compilation-targets@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== +"@babel/helper-compilation-targets@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" + integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" - browserslist "^4.22.2" + "@babel/compat-data" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: - "@babel/types" "^7.22.5" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/helper-module-imports@^7.22.15": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" - integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== - dependencies: - "@babel/types" "^7.24.0" +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" + integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== -"@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz#945681931a52f15ce879fd5b86ce2dae6d3d7f2a" - integrity sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w== +"@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - dependencies: - "@babel/types" "^7.22.5" +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== +"@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: - "@babel/types" "^7.22.5" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" -"@babel/helper-string-parser@^7.23.4": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" - integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== - -"@babel/helpers@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.1.tgz#183e44714b9eba36c3038e442516587b1e0a1a94" - integrity sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11" + integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ== dependencies: - "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.1" - "@babel/types" "^7.24.0" - -"@babel/highlight@^7.24.2": - version "7.24.2" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" - integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a" - integrity sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg== + "@babel/types" "^7.26.0" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -696,14 +147,28 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-import-attributes@^7.24.7": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -718,13 +183,13 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.7.2": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10" - integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== dependencies: - "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -738,7 +203,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -766,7 +231,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== @@ -774,51 +246,116 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" - integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== - dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - -"@babel/template@^7.22.15", "@babel/template@^7.24.0", "@babel/template@^7.3.3": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" - integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.24.0" - "@babel/types" "^7.24.0" - -"@babel/traverse@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" - integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== - dependencies: - "@babel/code-frame" "^7.24.1" - "@babel/generator" "^7.24.1" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.24.1" - "@babel/types" "^7.24.0" + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/template@^7.25.9", "@babel/template@^7.3.3": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/traverse@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" + integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/generator" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/template" "^7.25.9" + "@babel/types" "^7.25.9" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.3.3": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" - integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" + integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@biconomy/account@^4.5.7": + version "4.5.7" + resolved "https://registry.yarnpkg.com/@biconomy/account/-/account-4.5.7.tgz#e615f718433205d65887f4178b7a9f55fa818dda" + integrity sha512-hJjbCJNanwDzXprKCCrHB1iV+MdQagt79Zuyv3+j1EojseWmFPXAvZagS2rcYJu1XPFlt65nckQLtW8I/eutnw== + dependencies: + merkletreejs "^0.4.0" + +"@biconomy/gas-estimations@^0.2.75": + version "0.2.75" + resolved "https://registry.yarnpkg.com/@biconomy/gas-estimations/-/gas-estimations-0.2.75.tgz#1a7bb7567de8b48e3ea558aab5ff8b5e7cd07c7f" + integrity sha512-KRAHX++eqrOCgYmQkJ5kUMxcLCgHIp2tJ4KFkvxq0TnUDTfeGNtQDlerMET6fCSq0+mE2n4Lc7Hu25nef6c55Q== + dependencies: + zod "^3.24.1" + zod-validation-error "^3.4.0" + +"@biconomy/sdk@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@biconomy/sdk/-/sdk-0.0.29.tgz#5cc9dd718ea3e9aeb5e654ea64c9cf8721fdd8c4" + integrity sha512-MKCSHGeLUbVJ51vSK/oj4E1ieYOz9XnefYm78Nfnz6t5Y+iv3XecBl+vv96ZwOMr2MNHcPCL2xg8NkxVgjWtBQ== + +"@commitlint/config-validator@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-19.5.0.tgz#f0a4eda2109fc716ef01bb8831af9b02e3a1e568" + integrity sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw== + dependencies: + "@commitlint/types" "^19.5.0" + ajv "^8.11.0" + +"@commitlint/execute-rule@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz#c13da8c03ea0379f30856111e27d57518e25b8a2" + integrity sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg== + +"@commitlint/load@>6.1.1": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-19.5.0.tgz#67f90a294894d1f99b930b6152bed2df44a81794" + integrity sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA== + dependencies: + "@commitlint/config-validator" "^19.5.0" + "@commitlint/execute-rule" "^19.5.0" + "@commitlint/resolve-extends" "^19.5.0" + "@commitlint/types" "^19.5.0" + chalk "^5.3.0" + cosmiconfig "^9.0.0" + cosmiconfig-typescript-loader "^5.0.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" + +"@commitlint/resolve-extends@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz#f3ec33e12d10df90cae0bfad8e593431fb61b18e" + integrity sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA== + dependencies: + "@commitlint/config-validator" "^19.5.0" + "@commitlint/types" "^19.5.0" + global-directory "^4.0.1" + import-meta-resolve "^4.0.0" + lodash.mergewith "^4.6.2" + resolve-from "^5.0.0" + +"@commitlint/types@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-19.5.0.tgz#c5084d1231d4dd50e40bdb656ee7601f691400b3" + integrity sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg== + dependencies: + "@types/conventional-commits-parser" "^5.0.0" + chalk "^5.3.0" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -826,40 +363,45 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@datadog/native-appsec@7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-7.1.0.tgz#e8e6254236ac6fd7d4fb8b1156b34de64ec3e174" - integrity sha512-5FATunIxmvuSGDwPmbXfOi21wC7rjfbdLX4QiT5LR+iRLjRLT5iETqwdTsqy0WOQIHmxdWuddRvuakAg3921aA== +"@datadog/libdatadog@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@datadog/libdatadog/-/libdatadog-0.2.2.tgz#ac02c76ac9a38250dca740727c7cdf00244ce3d3" + integrity sha512-rTWo96mEPTY5UbtGoFj8/wY0uKSViJhsPg/Z6aoFWBFXQ8b45Ix2e/yvf92AAwrhG+gPLTxEqTXh3kef2dP8Ow== + +"@datadog/native-appsec@8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-8.3.0.tgz#91afd89d18d386be4da8a1b0e04500f2f8b5eb66" + integrity sha512-RYHbSJ/MwJcJaLzaCaZvUyNLUKFbMshayIiv4ckpFpQJDiq1T8t9iM2k7008s75g1vRuXfsRNX7MaLn4aoFuWA== dependencies: node-gyp-build "^3.9.0" -"@datadog/native-iast-rewriter@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.3.0.tgz#67abddc698504ad76736c0d49681bcf9e330e1bd" - integrity sha512-78ivSaaSXOaHn3VumF9kcSI443nbPfVAWsnDTH9X1ZbqXjHpSlHHTZgK9z/TNbkvuJarS/X1GBioPMcgea1Ejg== +"@datadog/native-iast-rewriter@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.5.0.tgz#b613defe86e78168f750d1f1662d4ffb3cf002e6" + integrity sha512-WRu34A3Wwp6oafX8KWNAbedtDaaJO+nzfYQht7pcJKjyC2ggfPeF7SoP+eDo9wTn4/nQwEOscSR4hkJqTRlpXQ== dependencies: lru-cache "^7.14.0" node-gyp-build "^4.5.0" -"@datadog/native-iast-taint-tracking@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-1.7.0.tgz#0bcd4f287c25403d2a965939b33c35ae8544b363" - integrity sha512-p3qnYJrUr9TQ38tuOFoutDAQWOobLdaaWpTl0SHu126JH3ANBxWL/QirtJy6czfzLpm5eXwYHwHidD1Y0mNPdg== +"@datadog/native-iast-taint-tracking@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-3.2.0.tgz#9fb6823d82f934e12c06ea1baa7399ca80deb2ec" + integrity sha512-Mc6FzCoyvU5yXLMsMS9yKnEqJMWoImAukJXolNWCTm+JQYCMf2yMsJ8pBAm7KyZKliamM9rCn7h7Tr2H3lXwjA== dependencies: node-gyp-build "^3.9.0" -"@datadog/native-metrics@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-2.0.0.tgz#65bf03313ee419956361e097551db36173e85712" - integrity sha512-YklGVwUtmKGYqFf1MNZuOHvTYdKuR4+Af1XkWcMD8BwOAjxmd9Z+97328rCOY8TFUJzlGUPaXzB8j2qgG/BMwA== +"@datadog/native-metrics@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-3.0.1.tgz#dc276c93785c0377a048e316f23b7c8ff3acfa84" + integrity sha512-0GuMyYyXf+Qpb/F+Fcekz58f2mO37lit9U3jMbWY/m8kac44gCPABzL5q3gWbdH+hWgqYfQoEYsdNDGSrKfwoQ== dependencies: node-addon-api "^6.1.0" node-gyp-build "^3.9.0" -"@datadog/pprof@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.2.0.tgz#a6c2779335b4f0fd51754e4de193e98591de387e" - integrity sha512-pSwLARpNLAIV1JttxXOBRKTn/NQYXDy1PJaV458YFDdAYxnBqpsYTat3/nX+8V5GoN4SfdHDci3zqXM+Ym66gQ== +"@datadog/pprof@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.4.1.tgz#08c9bcf5d8efb2eeafdfc9f5bb5402f79fb41266" + integrity sha512-IvpL96e/cuh8ugP5O8Czdup7XQOLHeIDgM5pac5W7Lc1YzGe5zTtebKFpitvb1CPw1YY+1qFx0pWGgKP2kOfHg== dependencies: delay "^5.0.0" node-gyp-build "<4.0" @@ -868,21 +410,172 @@ source-map "^0.7.4" "@datadog/sketches-js@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@datadog/sketches-js/-/sketches-js-2.1.0.tgz#8c7e8028a5fc22ad102fa542b0a446c956830455" - integrity sha512-smLocSfrt3s53H/XSVP3/1kP42oqvrkjUPtyaFd1F79ux24oE31BKt+q0c6lsa6hOYrFzsIwyc5GXAI5JmfOew== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@datadog/sketches-js/-/sketches-js-2.1.1.tgz#9ec2251b3c932b4f43e1d164461fa6cb6f28b7d0" + integrity sha512-d5RjycE+MObE/hU+8OM5Zp4VjTwiPLRa8299fj7muOmR16fb942z8byoMbCErnGh0lBevvgkGrLclQDvINbIyg== + +"@es-joy/jsdoccomment@~0.46.0": + version "0.46.0" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.46.0.tgz#47a2ee4bfc0081f252e058272dfab680aaed464d" + integrity sha512-C3Axuq1xd/9VqFZpW4YAzOx5O9q/LP46uIQy/iNDpHG3fmPa6TBtvfglMCs3RBiBxAIi0Go97r8+jvTt55XMyQ== + dependencies: + comment-parser "1.4.1" + esquery "^1.6.0" + jsdoc-type-pratt-parser "~4.0.0" + +"@esbuild/aix-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz#51299374de171dbd80bb7d838e1cfce9af36f353" + integrity sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ== + +"@esbuild/android-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz#58565291a1fe548638adb9c584237449e5e14018" + integrity sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw== + +"@esbuild/android-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.1.tgz#5eb8c652d4c82a2421e3395b808e6d9c42c862ee" + integrity sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ== + +"@esbuild/android-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.1.tgz#ae19d665d2f06f0f48a6ac9a224b3f672e65d517" + integrity sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg== + +"@esbuild/darwin-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz#05b17f91a87e557b468a9c75e9d85ab10c121b16" + integrity sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q== + +"@esbuild/darwin-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz#c58353b982f4e04f0d022284b8ba2733f5ff0931" + integrity sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw== + +"@esbuild/freebsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz#f9220dc65f80f03635e1ef96cfad5da1f446f3bc" + integrity sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA== + +"@esbuild/freebsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz#69bd8511fa013b59f0226d1609ac43f7ce489730" + integrity sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g== + +"@esbuild/linux-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz#8050af6d51ddb388c75653ef9871f5ccd8f12383" + integrity sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g== + +"@esbuild/linux-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz#ecaabd1c23b701070484990db9a82f382f99e771" + integrity sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ== + +"@esbuild/linux-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz#3ed2273214178109741c09bd0687098a0243b333" + integrity sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ== + +"@esbuild/linux-loong64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz#a0fdf440b5485c81b0fbb316b08933d217f5d3ac" + integrity sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw== + +"@esbuild/linux-mips64el@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz#e11a2806346db8375b18f5e104c5a9d4e81807f6" + integrity sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q== + +"@esbuild/linux-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz#06a2744c5eaf562b1a90937855b4d6cf7c75ec96" + integrity sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw== + +"@esbuild/linux-riscv64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz#65b46a2892fc0d1af4ba342af3fe0fa4a8fe08e7" + integrity sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA== + +"@esbuild/linux-s390x@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz#e71ea18c70c3f604e241d16e4e5ab193a9785d6f" + integrity sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw== + +"@esbuild/linux-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz#d47f97391e80690d4dfe811a2e7d6927ad9eed24" + integrity sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ== + +"@esbuild/netbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz#44e743c9778d57a8ace4b72f3c6b839a3b74a653" + integrity sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA== + +"@esbuild/openbsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz#05c5a1faf67b9881834758c69f3e51b7dee015d7" + integrity sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q== + +"@esbuild/openbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz#2e58ae511bacf67d19f9f2dcd9e8c5a93f00c273" + integrity sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA== + +"@esbuild/sunos-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz#adb022b959d18d3389ac70769cef5a03d3abd403" + integrity sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA== + +"@esbuild/win32-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz#84906f50c212b72ec360f48461d43202f4c8b9a2" + integrity sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A== + +"@esbuild/win32-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz#5e3eacc515820ff729e90d0cb463183128e82fac" + integrity sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ== + +"@esbuild/win32-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" + integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== + +"@eslint-community/eslint-plugin-eslint-comments@^4.3.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.4.1.tgz#dbfab6f2447c22be8758a0a9a9c80e56d2e2b93f" + integrity sha512-lb/Z/MzbTf7CaVYM9WCFNQZ4L1yi3ev2fsFPF99h31ljhSEyUoyEsKsNWiU+qD1glbYTDJdqgyaLKtyTkkqtuQ== + dependencies: + escape-string-regexp "^4.0.0" + ignore "^5.2.4" -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== dependencies: - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.0.tgz#3251a528998de914d59bb21ba4c11767cf1b3519" + integrity sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.1.2" -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== +"@eslint/core@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.9.0.tgz#168ee076f94b152c01ca416c3e5cf82290ab4fcd" + integrity sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg== "@eslint/eslintrc@^2.1.4": version "2.1.4" @@ -899,352 +592,51 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== - -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" - integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" - integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/networks" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/web" "^5.7.0" - -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" - integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.8", "@ethersproject/address@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" - integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - -"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" - integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== - dependencies: - "@ethersproject/bytes" "^5.7.0" - -"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" - integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.1.1", "@ethersproject/bignumber@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" - integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - bn.js "^5.2.1" - -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.0.8", "@ethersproject/bytes@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" - integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" - integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - -"@ethersproject/contracts@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" - integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== - dependencies: - "@ethersproject/abi" "^5.7.0" - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" - integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" - integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/basex" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/pbkdf2" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/wordlists" "^5.7.0" - -"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" - integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/hdnode" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/pbkdf2" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/random" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - aes-js "3.0.0" - scrypt-js "3.0.1" - -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" - integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - js-sha3 "0.8.0" - -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" - integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== - -"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" - integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== +"@eslint/eslintrc@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== dependencies: - "@ethersproject/logger" "^5.7.0" + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.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" -"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" - integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" +"@eslint/js@9.16.0", "@eslint/js@^9.0.0", "@eslint/js@^9.13.0": + version "9.16.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.16.0.tgz#3df2b2dd3b9163056616886c86e4082f45dbf3f4" + integrity sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" - integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== +"@eslint/plugin-kit@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz#812980a6a41ecf3a8341719f92a6d1e784a2e0e8" + integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA== dependencies: - "@ethersproject/logger" "^5.7.0" + levn "^0.4.1" -"@ethersproject/providers@5.7.2": - version "5.7.2" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" - integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/base64" "^5.7.0" - "@ethersproject/basex" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/networks" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/random" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/web" "^5.7.0" - bech32 "1.1.4" - ws "7.4.6" - -"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" - integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" - integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" - integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - hash.js "1.1.7" - -"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" - integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - bn.js "^5.2.1" - elliptic "6.5.4" - hash.js "1.1.7" - -"@ethersproject/solidity@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" - integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" - integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" - integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - -"@ethersproject/units@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" - integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/wallet@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" - integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/hdnode" "^5.7.0" - "@ethersproject/json-wallets" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/random" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/wordlists" "^5.7.0" - -"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" - integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== - dependencies: - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" - integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@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" "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": version "9.3.0" @@ -1258,30 +650,44 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" - integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== "@ioredis/commands@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== +"@isaacs/ttlcache@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2" + integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1510,9 +916,9 @@ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" @@ -1530,48 +936,41 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@mongodb-js/saslprep@^1.1.0", "@mongodb-js/saslprep@^1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz#0c48a96c8d799e81fae311b7251aa5c1dc7c6e95" - integrity sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA== +"@mongodb-js/saslprep@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz#e974bab8eca9faa88677d4ea4da8d09a52069004" + integrity sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw== dependencies: sparse-bitfield "^3.0.3" -"@noble/curves@1.2.0", "@noble/curves@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" - integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== - dependencies: - "@noble/hashes" "1.3.2" - -"@noble/curves@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6" - integrity sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg== - dependencies: - "@noble/hashes" "1.4.0" - -"@noble/curves@~1.4.0": +"@noble/curves@1.4.2", "@noble/curves@~1.4.0": version "1.4.2" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== dependencies: "@noble/hashes" "1.4.0" -"@noble/hashes@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@noble/curves@1.7.0", "@noble/curves@^1.4.0", "@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.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== -"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== +"@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.6.1", "@noble/hashes@^1.4.0", "@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" @@ -1586,7 +985,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -1594,22 +993,22 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.4.0": +"@opentelemetry/api@>=1.0.0 <1.9.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.8.0.tgz#5aa7abb48f23f693068ed2999ae627d2f7d902ec" integrity sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w== "@opentelemetry/core@^1.14.0": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.22.0.tgz#a9f33689acd4703ac780c6595497374e2113c7e5" - integrity sha512-0VoAlT6x+Xzik1v9goJ3pZ2ppi6+xd3aUfg4brfrLkDBHRIVjMP0eBHrKrhB+NKcDyMAg8fAbGL3Npg/F6AwWA== + version "1.28.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.28.0.tgz#e97290a3e36c59480ffb2287fe2713c66749274c" + integrity sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw== dependencies: - "@opentelemetry/semantic-conventions" "1.22.0" + "@opentelemetry/semantic-conventions" "1.27.0" -"@opentelemetry/semantic-conventions@1.22.0": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.22.0.tgz#d7502533a7c96e25baab86bac965468e0703a8b4" - integrity sha512-CAOgFOKLybd02uj/GhCdEeeBjOS0yeoDeo/CA7ASBSmenpZHAKGB3iDm/rv3BQLcabb/OprDEsSQ1y0P8A7Siw== +"@opentelemetry/semantic-conventions@1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" + integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== "@pkgr/core@^0.1.0": version "0.1.1" @@ -1669,58 +1068,28 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@redis/bloom@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" - integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== - -"@redis/client@1.5.14": - version "1.5.14" - resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.14.tgz#1107893464d092f140d77c468b018a6ed306a180" - integrity sha512-YGn0GqsRBFUQxklhY7v562VMOP0DcmlrHHs3IV1mFE3cbxe31IITUkqhBcIhVSI/2JqtWAJXg5mjV4aU+zD0HA== +"@rhinestone/module-sdk@^0.1.32": + version "0.1.32" + resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.32.tgz#32571e606b587de984bf56cba3569c91c0b7a2a6" + integrity sha512-wNzeIepHPhD0jiw3UOar+dAb6VKv4p8D4+947n0SLZHBdJ5S7moRCCL082NCo7Pz8+/C9kVriJASFeLZmJLo4g== dependencies: - cluster-key-slot "1.1.2" - generic-pool "3.9.0" - yallist "4.0.0" - -"@redis/graph@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.1.tgz#8c10df2df7f7d02741866751764031a957a170ea" - integrity sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw== - -"@redis/json@1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.6.tgz#b7a7725bbb907765d84c99d55eac3fcf772e180e" - integrity sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw== - -"@redis/search@1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.1.6.tgz#33bcdd791d9ed88ab6910243a355d85a7fedf756" - integrity sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw== - -"@redis/time-series@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.5.tgz#a6d70ef7a0e71e083ea09b967df0a0ed742bc6ad" - integrity sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg== + solady "^0.0.235" + tslib "^2.7.0" -"@scure/base@~1.1.0", "@scure/base@~1.1.2": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" - integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== "@scure/base@~1.1.6": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.7.tgz#fe973311a5c6267846aa131bc72e96c5d40d2b30" - integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== -"@scure/bip32@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" - integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== - dependencies: - "@noble/curves" "~1.2.0" - "@noble/hashes" "~1.3.2" - "@scure/base" "~1.1.2" +"@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/bip32@1.4.0": version "1.4.0" @@ -1731,13 +1100,14 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" -"@scure/bip39@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" - integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== +"@scure/bip32@1.6.0", "@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/hashes" "~1.3.0" - "@scure/base" "~1.1.0" + "@noble/curves" "~1.7.0" + "@noble/hashes" "~1.6.0" + "@scure/base" "~1.2.1" "@scure/bip39@1.3.0": version "1.3.0" @@ -1747,448 +1117,78 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" -"@sideway/address@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" - integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" - integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== - -"@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@sinonjs/commons@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== - dependencies: - "@sinonjs/commons" "^3.0.0" - -"@slack/logger@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714" - integrity sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA== - dependencies: - "@types/node" ">=12.0.0" - -"@slack/types@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.11.0.tgz#948c556081c3db977dfa8433490cc2ff41f47203" - integrity sha512-UlIrDWvuLaDly3QZhCPnwUSI/KYmV1N9LyhuH6EDKCRS1HWZhyTG3Ja46T3D0rYfqdltKYFXbJSSRPwZpwO0cQ== - -"@slack/web-api@^6.8.0": - version "6.12.0" - resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.12.0.tgz#d0487d90e3db2f7bfabe3430fa5da0cc03d2d9cb" - integrity sha512-RPw6F8rWfGveGkZEJ4+4jUin5iazxRK2q3FpQDz/FvdgzC3nZmPyLx8WRzc6nh0w3MBjEbphNnp2VZksfhpBIQ== - dependencies: - "@slack/logger" "^3.0.0" - "@slack/types" "^2.11.0" - "@types/is-stream" "^1.1.0" - "@types/node" ">=12.0.0" - axios "^1.6.5" - eventemitter3 "^3.1.0" - form-data "^2.5.0" - is-electron "2.2.2" - is-stream "^1.1.0" - p-queue "^6.6.1" - p-retry "^4.0.0" - -"@smithy/abort-controller@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-2.2.0.tgz#18983401a5e2154b5c94057730024a7d14cbcd35" - integrity sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/config-resolver@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-2.2.0.tgz#54f40478bb61709b396960a3535866dba5422757" - integrity sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA== - dependencies: - "@smithy/node-config-provider" "^2.3.0" - "@smithy/types" "^2.12.0" - "@smithy/util-config-provider" "^2.3.0" - "@smithy/util-middleware" "^2.2.0" - tslib "^2.6.2" - -"@smithy/core@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-1.4.0.tgz#5f9f86b681b9cbf23904041dad6f0531efe8375e" - integrity sha512-uu9ZDI95Uij4qk+L6kyFjdk11zqBkcJ3Lv0sc6jZrqHvLyr0+oeekD3CnqMafBn/5PRI6uv6ulW3kNLRBUHeVw== - dependencies: - "@smithy/middleware-endpoint" "^2.5.0" - "@smithy/middleware-retry" "^2.2.0" - "@smithy/middleware-serde" "^2.3.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/util-middleware" "^2.2.0" - tslib "^2.6.2" - -"@smithy/credential-provider-imds@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-2.3.0.tgz#326ce401b82e53f3c7ee4862a066136959a06166" - integrity sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w== - dependencies: - "@smithy/node-config-provider" "^2.3.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/types" "^2.12.0" - "@smithy/url-parser" "^2.2.0" - tslib "^2.6.2" - -"@smithy/eventstream-codec@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-2.2.0.tgz#63d74fa817188995eb55e792a38060b0ede98dc4" - integrity sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw== - dependencies: - "@aws-crypto/crc32" "3.0.0" - "@smithy/types" "^2.12.0" - "@smithy/util-hex-encoding" "^2.2.0" - tslib "^2.6.2" - -"@smithy/fetch-http-handler@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-2.5.0.tgz#0b8e1562807fdf91fe7dd5cde620d7a03ddc10ac" - integrity sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw== - dependencies: - "@smithy/protocol-http" "^3.3.0" - "@smithy/querystring-builder" "^2.2.0" - "@smithy/types" "^2.12.0" - "@smithy/util-base64" "^2.3.0" - tslib "^2.6.2" - -"@smithy/hash-node@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-2.2.0.tgz#df29e1e64811be905cb3577703b0e2d0b07fc5cc" - integrity sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g== - dependencies: - "@smithy/types" "^2.12.0" - "@smithy/util-buffer-from" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" - -"@smithy/invalid-dependency@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-2.2.0.tgz#ee3d8980022cb5edb514ac187d159b3e773640f0" - integrity sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/is-array-buffer@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" - integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== - dependencies: - tslib "^2.6.2" - -"@smithy/middleware-content-length@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-2.2.0.tgz#a82e97bd83d8deab69e07fea4512563bedb9461a" - integrity sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ== - dependencies: - "@smithy/protocol-http" "^3.3.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/middleware-endpoint@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-2.5.0.tgz#9f1459e9b4cbf00fadfd99e98f88d4b1a2aeb987" - integrity sha512-OBhI9ZEAG8Xen0xsFJwwNOt44WE2CWkfYIxTognC8x42Lfsdf0VN/wCMqpdkySMDio/vts10BiovAxQp0T0faA== - dependencies: - "@smithy/middleware-serde" "^2.3.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/shared-ini-file-loader" "^2.4.0" - "@smithy/types" "^2.12.0" - "@smithy/url-parser" "^2.2.0" - "@smithy/util-middleware" "^2.2.0" - tslib "^2.6.2" - -"@smithy/middleware-retry@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-2.2.0.tgz#ff48ac01ad57394eeea15a0146a86079cf6364b7" - integrity sha512-PsjDOLpbevgn37yJbawmfVoanru40qVA8UEf2+YA1lvOefmhuhL6ZbKtGsLAWDRnE1OlAmedsbA/htH6iSZjNA== - dependencies: - "@smithy/node-config-provider" "^2.3.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/service-error-classification" "^2.1.5" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/util-middleware" "^2.2.0" - "@smithy/util-retry" "^2.2.0" - tslib "^2.6.2" - uuid "^8.3.2" - -"@smithy/middleware-serde@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-2.3.0.tgz#a7615ba646a88b6f695f2d55de13d8158181dd13" - integrity sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/middleware-stack@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-2.2.0.tgz#3fb49eae6313f16f6f30fdaf28e11a7321f34d9f" - integrity sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/node-config-provider@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-2.3.0.tgz#9fac0c94a14c5b5b8b8fa37f20c310a844ab9922" - integrity sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg== - dependencies: - "@smithy/property-provider" "^2.2.0" - "@smithy/shared-ini-file-loader" "^2.4.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/node-http-handler@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-2.5.0.tgz#7b5e0565dd23d340380489bd5fe4316d2bed32de" - integrity sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA== - dependencies: - "@smithy/abort-controller" "^2.2.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/querystring-builder" "^2.2.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/property-provider@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-2.2.0.tgz#37e3525a3fa3e11749f86a4f89f0fd7765a6edb0" - integrity sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/protocol-http@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-3.3.0.tgz#a37df7b4bb4960cdda560ce49acfd64c455e4090" - integrity sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/querystring-builder@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-2.2.0.tgz#22937e19fcd0aaa1a3e614ef8cb6f8e86756a4ef" - integrity sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A== - dependencies: - "@smithy/types" "^2.12.0" - "@smithy/util-uri-escape" "^2.2.0" - tslib "^2.6.2" - -"@smithy/querystring-parser@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-2.2.0.tgz#24a5633f4b3806ff2888d4c2f4169e105fdffd79" - integrity sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/service-error-classification@^2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-2.1.5.tgz#0568a977cc0db36299d8703a5d8609c1f600c005" - integrity sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ== - dependencies: - "@smithy/types" "^2.12.0" - -"@smithy/shared-ini-file-loader@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.4.0.tgz#1636d6eb9bff41e36ac9c60364a37fd2ffcb9947" - integrity sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA== - dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/signature-v4@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-2.2.0.tgz#8fe6a574188b71fba6056111b88d50c84babb060" - integrity sha512-+B5TNzj/fRZzVW3z8UUJOkNx15+4E0CLuvJmJUA1JUIZFp3rdJ/M2H5r2SqltaVPXL0oIxv/6YK92T9TsFGbFg== - dependencies: - "@smithy/eventstream-codec" "^2.2.0" - "@smithy/is-array-buffer" "^2.2.0" - "@smithy/types" "^2.12.0" - "@smithy/util-hex-encoding" "^2.2.0" - "@smithy/util-middleware" "^2.2.0" - "@smithy/util-uri-escape" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" - -"@smithy/smithy-client@^2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-2.5.0.tgz#8de4fff221d232dda34a8e706d6a4f2911dffe2e" - integrity sha512-DDXWHWdimtS3y/Kw1Jo46KQ0ZYsDKcldFynQERUGBPDpkW1lXOTHy491ALHjwfiBQvzsVKVxl5+ocXNIgJuX4g== - dependencies: - "@smithy/middleware-endpoint" "^2.5.0" - "@smithy/middleware-stack" "^2.2.0" - "@smithy/protocol-http" "^3.3.0" - "@smithy/types" "^2.12.0" - "@smithy/util-stream" "^2.2.0" - tslib "^2.6.2" - -"@smithy/types@^2.12.0": - version "2.12.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.12.0.tgz#c44845f8ba07e5e8c88eda5aed7e6a0c462da041" - integrity sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw== - dependencies: - tslib "^2.6.2" - -"@smithy/url-parser@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-2.2.0.tgz#6fcda6116391a4f61fef5580eb540e128359b3c0" - integrity sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ== - dependencies: - "@smithy/querystring-parser" "^2.2.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/util-base64@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-2.3.0.tgz#312dbb4d73fb94249c7261aee52de4195c2dd8e2" - integrity sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw== - dependencies: - "@smithy/util-buffer-from" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" - -"@smithy/util-body-length-browser@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-2.2.0.tgz#25620645c6b62b42594ef4a93b66e6ab70e27d2c" - integrity sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w== - dependencies: - tslib "^2.6.2" - -"@smithy/util-body-length-node@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-2.3.0.tgz#d065a9b5e305ff899536777bbfe075cdc980136f" - integrity sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw== - dependencies: - tslib "^2.6.2" - -"@smithy/util-buffer-from@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@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-config-provider@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-2.3.0.tgz#bc79f99562d12a1f8423100ca662a6fb07cde943" - integrity sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ== +"@scure/bip39@1.5.0", "@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: - tslib "^2.6.2" + "@noble/hashes" "~1.6.0" + "@scure/base" "~1.2.1" -"@smithy/util-defaults-mode-browser@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.2.0.tgz#963a9d3c3351272764dd1c5dc07c26f2c8abcb02" - integrity sha512-2okTdZaCBvOJszAPU/KSvlimMe35zLOKbQpHhamFJmR7t95HSe0K3C92jQPjKY3PmDBD+7iMkOnuW05F5OlF4g== +"@sideway/address@^4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" + integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== dependencies: - "@smithy/property-provider" "^2.2.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - bowser "^2.11.0" - tslib "^2.6.2" + "@hapi/hoek" "^9.0.0" -"@smithy/util-defaults-mode-node@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.3.0.tgz#5005058ca0a299f0948b47c288f7c3d4f36cb26e" - integrity sha512-hfKXnNLmsW9cmLb/JXKIvtuO6Cf4SuqN5PN1C2Ru/TBIws+m1wSgb+A53vo0r66xzB6E82inKG2J7qtwdi+Kkw== - dependencies: - "@smithy/config-resolver" "^2.2.0" - "@smithy/credential-provider-imds" "^2.3.0" - "@smithy/node-config-provider" "^2.3.0" - "@smithy/property-provider" "^2.2.0" - "@smithy/smithy-client" "^2.5.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== -"@smithy/util-endpoints@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-1.2.0.tgz#b8b805f47e8044c158372f69b88337703117665d" - integrity sha512-BuDHv8zRjsE5zXd3PxFXFknzBG3owCpjq8G3FcsXW3CykYXuEqM3nTSsmLzw5q+T12ZYuDlVUZKBdpNbhVtlrQ== - dependencies: - "@smithy/node-config-provider" "^2.3.0" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== -"@smithy/util-hex-encoding@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-2.2.0.tgz#87edb7c88c2f422cfca4bb21f1394ae9602c5085" - integrity sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ== - dependencies: - tslib "^2.6.2" +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@smithy/util-middleware@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-2.2.0.tgz#80cfad40f6cca9ffe42a5899b5cb6abd53a50006" - integrity sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw== +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: - "@smithy/types" "^2.12.0" - tslib "^2.6.2" + type-detect "4.0.8" -"@smithy/util-retry@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-2.2.0.tgz#e8e019537ab47ba6b2e87e723ec51ee223422d85" - integrity sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g== +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@smithy/service-error-classification" "^2.1.5" - "@smithy/types" "^2.12.0" - tslib "^2.6.2" - -"@smithy/util-stream@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-2.2.0.tgz#b1279e417992a0f74afa78d7501658f174ed7370" - integrity sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA== - dependencies: - "@smithy/fetch-http-handler" "^2.5.0" - "@smithy/node-http-handler" "^2.5.0" - "@smithy/types" "^2.12.0" - "@smithy/util-base64" "^2.3.0" - "@smithy/util-buffer-from" "^2.2.0" - "@smithy/util-hex-encoding" "^2.2.0" - "@smithy/util-utf8" "^2.3.0" - tslib "^2.6.2" + "@sinonjs/commons" "^3.0.0" -"@smithy/util-uri-escape@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-2.2.0.tgz#56f5764051a33b67bc93fdd2a869f971b0635406" - integrity sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA== +"@slack/logger@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714" + integrity sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA== dependencies: - tslib "^2.6.2" + "@types/node" ">=12.0.0" -"@smithy/util-utf8@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" - integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== +"@slack/types@^2.11.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.14.0.tgz#913946b4bcb635dad1d39ceca73699215c38cf6f" + integrity sha512-n0EGm7ENQRxlXbgKSrQZL69grzg1gHLAVd+GlRVQJ1NSORo0FrApR7wql/gaKdu2n4TO83Sq/AmeUOqD60aXUA== + +"@slack/web-api@^6.8.0": + version "6.13.0" + resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.13.0.tgz#3df65a9b7b0cef5d7bbd6860a0741ce6b8091b9b" + integrity sha512-dv65crIgdh9ZYHrevLU6XFHTQwTyDmNqEqzuIrV+Vqe/vgiG6w37oex5ePDU1RGm2IJ90H8iOvHFvzdEO/vB+g== dependencies: - "@smithy/util-buffer-from" "^2.2.0" - tslib "^2.6.2" + "@slack/logger" "^3.0.0" + "@slack/types" "^2.11.0" + "@types/is-stream" "^1.1.0" + "@types/node" ">=12.0.0" + axios "^1.7.4" + eventemitter3 "^3.1.0" + form-data "^2.5.0" + is-electron "2.2.2" + is-stream "^1.1.0" + p-queue "^6.6.1" + p-retry "^4.0.0" "@tsconfig/node10@^1.0.7": version "1.0.11" @@ -2210,12 +1210,11 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/amqplib@^0.8.2": - version "0.8.2" - resolved "https://registry.yarnpkg.com/@types/amqplib/-/amqplib-0.8.2.tgz#9c46f2c7fac381e4f81bcb612c00d54549697d22" - integrity sha512-p+TFLzo52f8UanB+Nq6gyUi65yecAcRY3nYowU6MPGFtaJvEDxcnFWrxssSTkF+ts1W3zyQDvgVICLQem5WxRA== +"@types/amqplib@^0.10.6": + version "0.10.6" + resolved "https://registry.yarnpkg.com/@types/amqplib/-/amqplib-0.10.6.tgz#f22e03a86f628d4b59751c3fd805f27b810f0cb3" + integrity sha512-vQLVypBS1JQcfTXhl1Td1EEeLdtb+vuulOb4TrzYiLyP2aYLMAEzB3pNmEA0jBm0xIXu946Y7Xwl19Eidl32SQ== dependencies: - "@types/bluebird" "*" "@types/node" "*" "@types/babel__core@^7.1.14": @@ -2245,26 +1244,21 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" - integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== + version "7.20.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== dependencies: "@babel/types" "^7.20.7" -"@types/big.js@^6.1.5": - version "6.2.2" - resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.2.2.tgz#69422ec9ef59df1330ccfde2106d9e1159a083c3" - integrity sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA== - "@types/bluebird@*": version "3.5.42" resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.42.tgz#7ec05f1ce9986d920313c1377a5662b1b563d366" integrity sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A== "@types/bn.js@^5.1.0": - version "5.1.5" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" - integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + version "5.1.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.6.tgz#9ba818eec0c85e4d3c679518428afdf611d03203" + integrity sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w== dependencies: "@types/node" "*" @@ -2277,9 +1271,9 @@ "@types/node" "*" "@types/config@^3.3.3": - version "3.3.4" - resolved "https://registry.yarnpkg.com/@types/config/-/config-3.3.4.tgz#e46d1ee74f9b0c53645f644dd5f3ba5bedc68d76" - integrity sha512-qFiTLnWy+TdPSMIXFHP+87lFXFRM4SXjRS+CSB66+56TrpLNw003y1sh7DGaaC1NGesxgKoT5FDy6dyA1Xju/g== + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/config/-/config-3.3.5.tgz#91f0a52b10212b9c4254fb0bbc4bedd77cd389b8" + integrity sha512-itq2HtXQBrNUKwMNZnb9mBRE3T99VYCdl1gjST9rq+9kFaB1iMMGuDeZnP88qid73DnpAMKH9ZolqDpS1Lz7+w== "@types/connect@*": version "3.4.38" @@ -2296,6 +1290,13 @@ "@types/bluebird" "*" "@types/node" "*" +"@types/conventional-commits-parser@^5.0.0": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz#8cb81cf170853496cbc501a3b32dcf5e46ffb61a" + integrity sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ== + dependencies: + "@types/node" "*" + "@types/cors@^2.8.12": version "2.8.17" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" @@ -2308,24 +1309,22 @@ resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.2.2.tgz#771c4a768d94eb5922cc202a3009558204df0cea" integrity sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ== -"@types/express-prometheus-middleware@^1.2.1": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@types/express-prometheus-middleware/-/express-prometheus-middleware-1.2.3.tgz#b470fb21745d519b524f16d21d6d14e7cd90d7bc" - integrity sha512-3B+/27E/pxkZOzTWHqSQOhi5IctzoEyX6F1izBxAjiC5oe6tHLDy0kNQAj31SGR3wjxfvH1dgZi9fzmKX90OBw== - dependencies: - "@types/express" "*" +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== "@types/express-serve-static-core@^4.17.33": - version "4.17.43" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz#10d8444be560cb789c4735aea5eac6e5af45df54" - integrity sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg== + version "4.19.6" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" + integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/send" "*" -"@types/express@*", "@types/express@^4.17.14", "@types/express@^4.17.21": +"@types/express@^4.17.14": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -2343,9 +1342,9 @@ "@types/node" "*" "@types/hdkey@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/hdkey/-/hdkey-2.0.3.tgz#ef32e3a2ba22a614193ca20c7bb681aeaaf1c50e" - integrity sha512-U5W3b+f3KqCC7tge4WjV5MDzzYTcwbnufyL7RwEmxH2EyxtbfaQVlS8z6PZDp8XET304EEVhLWG76rF5ygUKHQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/hdkey/-/hdkey-2.1.0.tgz#3007ab6c759099c2005240b849bce0e8a8e03f55" + integrity sha512-Mlwd/7y2JRPOoMP5snMKn+/Vvc5ThSDXnSl+EHgHxoE4wDDOFxUT/w75DmRaaI/3JpC5CMkFlO4fkpWGAk7qyA== dependencies: "@types/node" "*" @@ -2381,14 +1380,14 @@ "@types/istanbul-lib-report" "*" "@types/jest@^29.2.2": - version "29.5.12" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" - integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" -"@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -2399,19 +1398,14 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/lodash@^4.14.186": - version "4.17.0" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.0.tgz#d774355e41f372d5350a4d0714abb48194a489c3" - integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA== - -"@types/luxon@~3.3.0": - version "3.3.8" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.8.tgz#84dbf2d020a9209a272058725e168f21d331a67e" - integrity sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ== + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb" + integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg== -"@types/mime@*": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" - integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== +"@types/luxon@~3.4.0": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== "@types/mime@^1": version "1.3.5" @@ -2430,24 +1424,17 @@ resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.11.tgz#70b7131f65038ae63cfe841354c8aba363632344" integrity sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg== -"@types/node@*", "@types/node@>=12.0.0", "@types/node@>=13.7.0": - version "20.11.30" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.30.tgz#9c33467fc23167a347e73834f788f4b9f399d66f" - integrity sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw== - dependencies: - undici-types "~5.26.4" - -"@types/node@^20.10.6": - version "20.12.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" - integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== +"@types/node@*", "@types/node@>=12.0.0", "@types/node@>=13.7.0", "@types/node@^22.10.1": + version "22.10.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.1.tgz#41ffeee127b8975a05f8c4f83fb89bcb2987d766" + integrity sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ== dependencies: - undici-types "~5.26.4" + undici-types "~6.20.0" -"@types/numeral@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/numeral/-/numeral-2.0.5.tgz#388e5c4ff4b0e1787f130753cbbe83d3ba770858" - integrity sha512-kH8I7OSSwQu9DS9JYdFWbuvhVzvFRoCPCkGxNwoGgaPeDfEPJlcxNvEOypZhQ3XXHsGbfIuYcxcJxKUfJHnRfw== +"@types/normalize-package-data@^2.4.0": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== "@types/pbkdf2@^3.0.0": version "3.1.2" @@ -2456,17 +1443,10 @@ dependencies: "@types/node" "*" -"@types/pino@^7.0.5": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@types/pino/-/pino-7.0.5.tgz#1c84a81b924a6a9e263dbb581dffdbad7a3c60c4" - integrity sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg== - dependencies: - pino "*" - "@types/qs@*": - version "6.9.14" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.14.tgz#169e142bfe493895287bee382af6039795e9b75b" - integrity sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA== + version "6.9.17" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.17.tgz#fc560f60946d0aeff2f914eb41679659d3310e1a" + integrity sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ== "@types/range-parser@*": version "1.2.7" @@ -2485,11 +1465,6 @@ dependencies: "@types/node" "*" -"@types/semver@^7.3.12": - version "7.5.8" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - "@types/send@*": version "0.17.4" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" @@ -2499,13 +1474,13 @@ "@types/node" "*" "@types/serve-static@*": - version "1.15.5" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" - integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== dependencies: "@types/http-errors" "*" - "@types/mime" "*" "@types/node" "*" + "@types/send" "*" "@types/stack-utils@^2.0.0": version "2.0.3" @@ -2518,135 +1493,114 @@ integrity sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA== "@types/whatwg-url@^11.0.2": - version "11.0.4" - resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-11.0.4.tgz#ffed0dc8d89d91f62e3f368fcbda222a487c4f63" - integrity sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw== + version "11.0.5" + resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-11.0.5.tgz#aaa2546e60f0c99209ca13360c32c78caf2c409f" + integrity sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ== dependencies: "@types/webidl-conversions" "*" -"@types/whatwg-url@^8.2.1": - version "8.2.2" - resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63" - integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA== - dependencies: - "@types/node" "*" - "@types/webidl-conversions" "*" - "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.8": - version "17.0.32" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" - integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.40.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" - integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== +"@typescript-eslint/eslint-plugin@8.17.0", "@typescript-eslint/eslint-plugin@^8.11.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz#2ee073c421f4e81e02d10e731241664b6253b23c" + integrity sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w== 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" + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/type-utils" "8.17.0" + "@typescript-eslint/utils" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" 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.40.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@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" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@8.17.0", "@typescript-eslint/parser@^8.11.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.17.0.tgz#2ee972bb12fa69ac625b85813dc8d9a5a053ff52" + integrity sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg== + dependencies: + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" - integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== +"@typescript-eslint/scope-manager@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz#a3f49bf3d4d27ff8d6b2ea099ba465ef4dbcaa3a" + integrity sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg== dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" -"@typescript-eslint/type-utils@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" - integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== +"@typescript-eslint/type-utils@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz#d326569f498cdd0edf58d5bb6030b4ad914e63d3" + integrity sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw== dependencies: - "@typescript-eslint/typescript-estree" "5.62.0" - "@typescript-eslint/utils" "5.62.0" + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/utils" "8.17.0" debug "^4.3.4" - tsutils "^3.21.0" + ts-api-utils "^1.3.0" -"@typescript-eslint/types@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" - integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== +"@typescript-eslint/types@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.17.0.tgz#ef84c709ef8324e766878834970bea9a7e3b72cf" + integrity sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA== -"@typescript-eslint/typescript-estree@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== +"@typescript-eslint/typescript-estree@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz#40b5903bc929b1e8dd9c77db3cb52cfb199a2a34" + integrity sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw== dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" debug "^4.3.4" - globby "^11.1.0" + fast-glob "^3.3.2" is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" -"@typescript-eslint/utils@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== +"@typescript-eslint/utils@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.17.0.tgz#41c05105a2b6ab7592f513d2eeb2c2c0236d8908" + integrity sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w== 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.yarnpkg.com/@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" - -"@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +"@typescript-eslint/visitor-keys@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz#4dbcd0e28b9bf951f4293805bf34f98df45e1aa8" + integrity sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg== + dependencies: + "@typescript-eslint/types" "8.17.0" + eslint-visitor-keys "^4.2.0" -abitype@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" - integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== +abitype@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.7.tgz#876a0005d211e1c9132825d45bcee7b46416b284" + integrity sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw== -abitype@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.5.tgz#29d0daa3eea867ca90f7e4123144c1d1270774b6" - integrity sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw== +abitype@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.6.tgz#76410903e1d88e34f1362746e2d407513c38565b" + integrity sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A== abort-controller@^3.0.0: version "3.0.0" @@ -2663,10 +1617,10 @@ accepts@^1.3.7, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" - integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== acorn-jsx@^5.3.2: version "5.3.2" @@ -2674,19 +1628,16 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.3.2" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" - integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== - -acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" -aes-js@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" - integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== +acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== ajv@^6.12.4: version "6.12.6" @@ -2698,14 +1649,23 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -amqplib@^0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.10.3.tgz#e186a2f74521eb55ec54db6d25ae82c29c1f911a" - integrity sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw== +ajv@^8.11.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/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" + +amqplib@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.10.5.tgz#2b217a3c56f2935ee2e36206bfd7e25ee866491f" + integrity sha512-Dx5zmy0Ur+Q7LPPdhz+jx5IzmJBoHd15tOeAfQ8SuvEtyPJ20hBemhOBA4b1WeORCRa0ENM/kHCzmem1w/zHvQ== dependencies: "@acuminous/bitsyntax" "^0.1.2" buffer-more-ints "~1.0.0" - readable-stream "1.x >=1.1.9" url-parse "~1.5.10" ansi-escapes@^4.2.1: @@ -2747,6 +1707,11 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -2777,7 +1742,7 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-includes@^3.1.7: +array-includes@^3.1.8: version "3.1.8" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== @@ -2789,12 +1754,7 @@ array-includes@^3.1.7: get-intrinsic "^1.2.4" is-string "^1.0.7" -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.findlastindex@^1.2.3: +array.prototype.findlastindex@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== @@ -2847,11 +1807,21 @@ async-mutex@^0.4.0: dependencies: tslib "^2.4.0" +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/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.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" @@ -2864,19 +1834,17 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axios@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" - integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== +axios-retry@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-4.5.0.tgz#441fdc32cedf63d6abd5de5d53db3667afd4c39b" + integrity sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ== dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" + is-retry-allowed "^2.2.0" -axios@^1.1.2, axios@^1.6.5: - version "1.6.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" - integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== +axios@^1.1.2, axios@^1.7.4: + version "1.7.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.8.tgz#1997b1496b394c21953e68c14aaa51b7b5de3d6e" + integrity sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" @@ -2917,22 +1885,25 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__traverse" "^7.0.6" babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" + integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" babel-preset-jest@^29.6.3: version "29.6.3" @@ -2948,9 +1919,9 @@ balanced-match@^1.0.0: integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base-x@^3.0.2: - version "3.0.9" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + version "3.0.10" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.10.tgz#62de58653f8762b5d6f8d9fe30fa75f7b2585a75" + integrity sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ== dependencies: safe-buffer "^5.0.1" @@ -2959,25 +1930,24 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bech32@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" - integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== - -big.js@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f" - integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ== +bignumber.js@^9.0.1: + version "9.1.2" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== -bintrees@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" - integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" blakejs@^1.1.0: version "1.2.1" @@ -2989,20 +1959,25 @@ bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/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.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + version "4.12.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.1.tgz#215741fe3c9dba2d7e12c001d0cfdbae43975ba7" + integrity sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg== bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.20.2: - version "1.20.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" - integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/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" @@ -3012,16 +1987,11 @@ body-parser@1.20.2: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.11.0" + qs "6.13.0" raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" -bowser@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/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.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3037,12 +2007,12 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.1.0: version "1.1.0" @@ -3061,17 +2031,17 @@ browserify-aes@^1.2.0: inherits "^2.0.1" safe-buffer "^5.0.1" -browserslist@^4.22.2: - version "4.23.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" - integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== +browserslist@^4.24.0, browserslist@^4.24.2: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: - caniuse-lite "^1.0.30001587" - electron-to-chromium "^1.4.668" - node-releases "^2.0.14" - update-browserslist-db "^1.0.13" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" -bs-logger@0.x: +bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== @@ -3101,17 +2071,10 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -bson@^4.7.2: - version "4.7.2" - resolved "https://registry.yarnpkg.com/bson/-/bson-4.7.2.tgz#320f4ad0eaf5312dd9b45dc369cc48945e2a5f2e" - integrity sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ== - dependencies: - buffer "^5.6.0" - -bson@^6.4.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/bson/-/bson-6.5.0.tgz#fc4828d065e64e48ea442b1a23099b2e52f7ff0b" - integrity sha512-DXf1BTAS8vKyR90BO4x5v3rKVarmkdkzwOrnYDFdjAY694ILNDkmA3uRh1xXJEl+C1DAh8XCvAQ+Gh3kzubtpg== +bson@^6.10.1: + version "6.10.2" + resolved "https://registry.yarnpkg.com/bson/-/bson-6.10.2.tgz#092ba1d2abee7e126320003f88d6883c3b1897df" + integrity sha512-5afhLTjqDSA3akH56E+/2J6kTDuSIlBxyXPdQslj9hcIgOUE378xdOfZvC/9q3LifJNI6KR/juZ+d0NRNYBwXg== buffer-from@^1.0.0: version "1.1.2" @@ -3123,12 +2086,17 @@ buffer-more-ints@~1.0.0: resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz#ef4f8e2dddbad429ed3828a9c55d44f05c611422" integrity sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg== +buffer-reverse@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" + integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@^5.6.0: +buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -3144,11 +2112,21 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cachedir@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + 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.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" @@ -3175,19 +2153,12 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001587: - version "1.0.30001600" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz#93a3ee17a35aa6a9f0c6ef1b2ab49507d1ab9079" - integrity sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ== +caniuse-lite@^1.0.30001669: + version "1.0.30001686" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001686.tgz#0e04b8d90de8753188e93c9989d56cb19d902670" + integrity sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA== -cent.js@^2.0.5: - version "2.1.6" - resolved "https://registry.yarnpkg.com/cent.js/-/cent.js-2.1.6.tgz#75e72c12903d071248ade1559124a0cd5dbc2e63" - integrity sha512-iMV2ZGkFc7cxYmjUvQ9gKUImuoCs1Krv8PP23WTciVBSv9XiSa9bSQD8td+oPZ6N0ugeCJZr+352RhrNjx/n6A== - dependencies: - axios "1.3.4" - -chalk@^2.4.2: +chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3196,7 +2167,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3204,11 +2175,21 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + chokidar@^3.5.2: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" @@ -3229,18 +2210,47 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +ci-info@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.1.0.tgz#92319d2fa29d2620180ea5afed31f589bc98cf83" + integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + version "1.0.6" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.6.tgz#8fe672437d01cd6c4561af5334e0cc50ff1955f7" + integrity sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw== dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + inherits "^2.0.4" + safe-buffer "^5.2.1" cjs-module-lexer@^1.0.0, cjs-module-lexer@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" - integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + version "1.4.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" + integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== + +clean-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" + integrity sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw== + dependencies: + escape-string-regexp "^1.0.5" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^8.0.1: version "8.0.1" @@ -3256,6 +2266,11 @@ clone@2.x: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + cls-rtracer@^2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/cls-rtracer/-/cls-rtracer-2.6.3.tgz#316b39718b82b159b3c6021f6d043c7ae899a93e" @@ -3263,7 +2278,7 @@ cls-rtracer@^2.6.2: dependencies: uuid "^9.0.0" -cluster-key-slot@1.1.2, cluster-key-slot@^1.1.0: +cluster-key-slot@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== @@ -3314,23 +2329,48 @@ combined-stream@^1.0.6, combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +comment-parser@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.1.tgz#bdafead37961ac079be11eb7ec65c4d021eaf9cc" + integrity sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg== + +commitizen@^4.0.3, commitizen@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.3.1.tgz#f0e0e4b7ae3fafc92e444bbb78f2ded5a1d4311a" + integrity sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw== + dependencies: + cachedir "2.3.0" + cz-conventional-changelog "3.3.0" + dedent "0.7.0" + detect-indent "6.1.0" + find-node-modules "^2.1.2" + find-root "1.1.0" + fs-extra "9.1.0" + glob "7.2.3" + inquirer "8.2.5" + is-utf8 "^0.2.1" + lodash "4.17.21" + minimist "1.2.7" + strip-bom "4.0.0" + strip-json-comments "3.1.1" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== config@^3.3.11: - version "3.3.11" - resolved "https://registry.yarnpkg.com/config/-/config-3.3.11.tgz#009e802631363e456d4cf57767a65c1549069ba2" - integrity sha512-Dhn63ZoWCW5EMg4P0Sl/XNsj/7RLiUIA1x1npCy+m2cRwRHzLnt3UtYtxRDMZW/6oOMdWhCzaGYkOcajGgrAOA== + version "3.3.12" + resolved "https://registry.yarnpkg.com/config/-/config-3.3.12.tgz#a10ae66efcc3e48c1879fbb657c86c4ef6c7b25e" + integrity sha512-Vmx389R/QVM3foxqBzXO8t2tUikYZP64Q6vQxGrsMpREeJc/aWRnPRERXWsYzOHAumx/AOoILWe6nU3ZJL+6Sw== dependencies: json5 "^2.2.3" -confusing-browser-globals@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" - integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== - consolidate@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" @@ -3350,6 +2390,11 @@ content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== +conventional-commit-types@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz#7c9214e58eae93e85dd66dbfbafe7e4fffa2365b" + integrity sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg== + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -3360,15 +2405,17 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +core-js-compat@^3.34.0: + version "3.39.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61" + integrity sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw== + dependencies: + browserslist "^4.24.2" cors@^2.8.5: version "2.8.5" @@ -3378,6 +2425,23 @@ cors@^2.8.5: object-assign "^4" vary "^1" +cosmiconfig-typescript-loader@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.1.0.tgz#d8d02bff04e63faa2dc794d618168bd764c704be" + integrity sha512-7PtBB+6FdsOvZyJtlF3hEPpACq7RQX6BVGsgC7/lfVXnKMvNCu/XY3ykreqG5w/rBNdu2z8LCIKoF3kpHHdHlA== + dependencies: + jiti "^1.21.6" + +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -3420,23 +2484,23 @@ create-require@^1.1.0: integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cron@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/cron/-/cron-3.1.6.tgz#e7e1798a468e017c8d31459ecd7c2d088f97346c" - integrity sha512-cvFiQCeVzsA+QPM6fhjBtlKGij7tLLISnTSvFxVdnFGLdz+ZdXN37kNe0i2gefmdD17XuZA6n2uPVwzl4FxW/w== + version "3.2.1" + resolved "https://registry.yarnpkg.com/cron/-/cron-3.2.1.tgz#f319b9e46f8f4108eaa08639980f982dc22dd5f6" + integrity sha512-w2n5l49GMmmkBFEsH9FIDhjZ1n1QgTMOCMGuQtOXs5veNiosZmso6bQGuqOJSYAXXrG84WQFVneNk+Yt0Ua9iw== dependencies: - "@types/luxon" "~3.3.0" - luxon "~3.4.0" + "@types/luxon" "~3.4.0" + luxon "~3.5.0" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cross-spawn@^7.0.3, cross-spawn@^7.0.5: + version "7.0.6" + resolved "https://registry.yarnpkg.com/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" -crypto-js@^4.1.1: +crypto-js@^4.1.1, crypto-js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== @@ -3446,6 +2510,20 @@ crypto-randomuuid@^1.0.0: resolved "https://registry.yarnpkg.com/crypto-randomuuid/-/crypto-randomuuid-1.0.0.tgz#acf583e5e085e867ae23e107ff70279024f9e9e7" integrity sha512-/RC5F4l1SCqD/jazwUF6+t34Cd8zTSAGZ7rvvZu1whZUhD2a5MOGKjSGowoGcpj/cbVZk1ZODIooJEQQq3nNAA== +cz-conventional-changelog@3.3.0, cz-conventional-changelog@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz#9246947c90404149b3fe2cf7ee91acad3b7d22d2" + integrity sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw== + dependencies: + chalk "^2.4.1" + commitizen "^4.0.3" + conventional-commit-types "^3.0.0" + lodash.map "^4.5.1" + longest "^2.0.1" + word-wrap "^1.0.3" + optionalDependencies: + "@commitlint/load" ">6.1.1" + data-view-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" @@ -3479,44 +2557,44 @@ dateformat@^4.6.3: integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== dc-polyfill@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/dc-polyfill/-/dc-polyfill-0.1.4.tgz#4118cec81a8fab9a5729c41c285c715ffa42495a" - integrity sha512-8iwEduR2jR9wWYggeaYtYZWRiUe3XZPyAQtMTL1otv8X3kfR8xUIVb4l5awHEeyDrH6Je7N324lKzMKlMMN6Yw== + version "0.1.6" + resolved "https://registry.yarnpkg.com/dc-polyfill/-/dc-polyfill-0.1.6.tgz#c2940fa68ffb24a7bf127cc6cfdd15b39f0e7f02" + integrity sha512-UV33cugmCC49a5uWAApM+6Ev9ZdvIUMTrtCO9fj96TPGOQiea54oeO3tiEVdVeo3J9N2UdJEmbS4zOkkEA35uQ== dd-trace@^4.17.0: - version "4.33.0" - resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-4.33.0.tgz#b4172018834e84aabc991213084532adb0628cbe" - integrity sha512-hO+2cIIiZJ8lbjRiIMs5mdMXNcTyi/D/kGreEnh/HuZPptngsF7HpDRFxhVls1hFOg1y4uYqYCUcTT5U0KXrHA== - dependencies: - "@datadog/native-appsec" "7.1.0" - "@datadog/native-iast-rewriter" "2.3.0" - "@datadog/native-iast-taint-tracking" "1.7.0" - "@datadog/native-metrics" "^2.0.0" - "@datadog/pprof" "5.2.0" + version "4.52.0" + resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-4.52.0.tgz#8a879f2dbd41523e05841dd46c212750822eb753" + integrity sha512-aBO/bozGBGKdmktSiaobPJ0G+FF8cE7txImrs5TmJCEUUZ3xup5pT+Vk1OkntCXyQKvKJ1iR9dMQypOPUs20+Q== + dependencies: + "@datadog/libdatadog" "^0.2.2" + "@datadog/native-appsec" "8.3.0" + "@datadog/native-iast-rewriter" "2.5.0" + "@datadog/native-iast-taint-tracking" "3.2.0" + "@datadog/native-metrics" "^3.0.1" + "@datadog/pprof" "5.4.1" "@datadog/sketches-js" "^2.1.0" - "@opentelemetry/api" "^1.0.0" + "@isaacs/ttlcache" "^1.4.1" + "@opentelemetry/api" ">=1.0.0 <1.9.0" "@opentelemetry/core" "^1.14.0" crypto-randomuuid "^1.0.0" dc-polyfill "^0.1.4" ignore "^5.2.4" - import-in-the-middle "^1.7.3" + import-in-the-middle "1.11.2" int64-buffer "^0.1.9" - ipaddr.js "^2.1.0" istanbul-lib-coverage "3.2.0" jest-docblock "^29.7.0" koalas "^1.0.2" limiter "1.1.5" lodash.sortby "^4.7.0" lru-cache "^7.14.0" - methods "^1.1.2" module-details-from-path "^1.0.3" msgpack-lite "^0.1.26" - node-abort-controller "^3.1.1" opentracing ">=0.12.1" - path-to-regexp "^0.1.2" + path-to-regexp "^0.1.10" pprof-format "^2.1.0" protobufjs "^7.2.5" retry "^0.13.1" + rfdc "^1.3.1" semver "^7.5.4" shell-quote "^1.8.1" tlhunter-sorted-set "^0.1.0" @@ -3528,12 +2606,12 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4.x, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@4.x, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: - ms "2.1.2" + ms "^2.1.3" debug@^3.2.7: version "3.2.7" @@ -3542,17 +2620,15 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.3.4: - version "4.3.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== - dependencies: - ms "2.1.2" +dedent@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== dedent@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" - integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + version "1.5.3" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== deep-is@^0.1.3: version "0.1.4" @@ -3564,6 +2640,13 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -3573,7 +2656,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" -define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: +define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -3602,7 +2685,7 @@ depd@2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@^1.1.0, depd@~1.1.0: +depd@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== @@ -3612,6 +2695,16 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q== + +detect-indent@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -3627,13 +2720,6 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/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.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -3641,45 +2727,32 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dotenv@^16.0.3: - version "16.4.5" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" - integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + version "16.4.6" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.6.tgz#fc88e8a664087abf3e19d61e21f7feee1849bbb1" + integrity sha512-JhcR/+KIjkkjiU8yEpaB/USlzVi3i5whwOjpIRNGi9svKEXZSe+Qp6IWAjFjv+2GViAoDRCUv/QLNziQxsLqDg== ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.668: - version "1.4.717" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.717.tgz#99db370cae8cd090d5b01f8748e9ad369924d0f8" - integrity sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A== - -elliptic@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== +ejs@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== 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" + jake "^10.8.5" -elliptic@^6.5.4: - version "6.5.5" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" - integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== +electron-to-chromium@^1.5.41: + version "1.5.68" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.68.tgz#4f46be4d465ef00e2100d5557b66f4af70e3ce6c" + integrity sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ== + +elliptic@^6.5.7: + version "6.6.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -3704,6 +2777,11 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/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.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -3711,14 +2789,18 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -entry-point-gas-estimations@0.0.22: - version "0.0.22" - resolved "https://registry.yarnpkg.com/entry-point-gas-estimations/-/entry-point-gas-estimations-0.0.22.tgz#e269d704c816262084396f97e23afcc4a39d3e7a" - integrity sha512-rJrLmFvJbotTNoX7OHF45eFEDojLSMtS3+BjMHlHBf1dueYE2XjaglC1213DtTpI90bVZoEf17Mx/HGOWrWG0Q== +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: - viem "^2.17.3" - zod "^3.22.4" - zod-validation-error "^2.1.0" + graceful-fs "^4.2.4" + tapable "^2.2.0" + +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== error-ex@^1.3.1: version "1.3.2" @@ -3727,10 +2809,10 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: - version "1.23.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.2.tgz#693312f3940f967b8dd3eebacb590b01712622e0" - integrity sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w== +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2, es-abstract@^1.23.5: + version "1.23.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.5.tgz#f4599a4946d57ed467515ed10e4f157289cd52fb" + integrity sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ== dependencies: array-buffer-byte-length "^1.0.1" arraybuffer.prototype.slice "^1.0.3" @@ -3747,7 +2829,7 @@ es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23 function.prototype.name "^1.1.6" get-intrinsic "^1.2.4" get-symbol-description "^1.0.2" - globalthis "^1.0.3" + globalthis "^1.0.4" gopd "^1.0.1" has-property-descriptors "^1.0.2" has-proto "^1.0.3" @@ -3763,19 +2845,19 @@ es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23 is-string "^1.0.7" is-typed-array "^1.1.13" is-weakref "^1.0.2" - object-inspect "^1.13.1" + object-inspect "^1.13.3" object-keys "^1.1.1" object.assign "^4.1.5" - regexp.prototype.flags "^1.5.2" + regexp.prototype.flags "^1.5.3" 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.7" + 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.5" + typed-array-length "^1.0.6" unbox-primitive "^1.0.2" which-typed-array "^1.1.15" @@ -3791,6 +2873,11 @@ es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-module-lexer@^1.5.3: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + es-object-atoms@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" @@ -3815,18 +2902,48 @@ es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: hasown "^2.0.0" es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" -escalade@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" - integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +esbuild@~0.23.0: + version "0.23.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.1.tgz#40fdc3f9265ec0beae6f59824ade1bd3d3d2dab8" + integrity sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.23.1" + "@esbuild/android-arm" "0.23.1" + "@esbuild/android-arm64" "0.23.1" + "@esbuild/android-x64" "0.23.1" + "@esbuild/darwin-arm64" "0.23.1" + "@esbuild/darwin-x64" "0.23.1" + "@esbuild/freebsd-arm64" "0.23.1" + "@esbuild/freebsd-x64" "0.23.1" + "@esbuild/linux-arm" "0.23.1" + "@esbuild/linux-arm64" "0.23.1" + "@esbuild/linux-ia32" "0.23.1" + "@esbuild/linux-loong64" "0.23.1" + "@esbuild/linux-mips64el" "0.23.1" + "@esbuild/linux-ppc64" "0.23.1" + "@esbuild/linux-riscv64" "0.23.1" + "@esbuild/linux-s390x" "0.23.1" + "@esbuild/linux-x64" "0.23.1" + "@esbuild/netbsd-x64" "0.23.1" + "@esbuild/openbsd-arm64" "0.23.1" + "@esbuild/openbsd-x64" "0.23.1" + "@esbuild/sunos-x64" "0.23.1" + "@esbuild/win32-arm64" "0.23.1" + "@esbuild/win32-ia32" "0.23.1" + "@esbuild/win32-x64" "0.23.1" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-html@~1.0.3: version "1.0.3" @@ -3848,22 +2965,23 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-airbnb-base@^15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236" - integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== +eslint-compat-utils@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz#7fc92b776d185a70c4070d03fd26fde3d59652e4" + integrity sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q== dependencies: - confusing-browser-globals "^1.0.10" - object.assign "^4.1.2" - object.entries "^1.1.5" - semver "^6.3.0" + semver "^7.5.4" -eslint-config-airbnb-typescript@^17.0.0: - version "17.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz#fda960eee4a510f092a9a1c139035ac588937ddc" - integrity sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig== +eslint-config-eslint@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-eslint/-/eslint-config-eslint-11.0.0.tgz#5bb535544d462750879fdfaac70473838d763975" + integrity sha512-AWmzAfDUUaHYZzGQguHXzh/PkKHfrwttXIglqE0xWSkJDRhionaJVT1+niI1qKYRPn/FE5CHQIZ24L1u5rYSaw== dependencies: - eslint-config-airbnb-base "^15.0.0" + "@eslint-community/eslint-plugin-eslint-comments" "^4.3.0" + "@eslint/js" "^9.0.0" + eslint-plugin-jsdoc "^48.2.3" + eslint-plugin-n "^17.2.0" + eslint-plugin-unicorn "^52.0.0" eslint-config-prettier@^9.1.0: version "9.1.0" @@ -3879,110 +2997,176 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" -eslint-module-utils@^2.8.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" - integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== +eslint-module-utils@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" + integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== dependencies: debug "^3.2.7" -eslint-plugin-import@^2.26.0: - version "2.29.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" - integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== +eslint-plugin-es-x@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" + integrity sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.11.0" + eslint-compat-utils "^0.5.1" + +eslint-plugin-import@^2.31.0: + version "2.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" + integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== dependencies: - array-includes "^3.1.7" - array.prototype.findlastindex "^1.2.3" + "@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.8.0" - hasown "^2.0.0" - is-core-module "^2.13.1" + eslint-module-utils "^2.12.0" + hasown "^2.0.2" + is-core-module "^2.15.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.fromentries "^2.0.7" - object.groupby "^1.0.1" - object.values "^1.1.7" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" semver "^6.3.1" + string.prototype.trimend "^1.0.8" tsconfig-paths "^3.15.0" -eslint-plugin-prettier@^5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz#17cfade9e732cef32b5f5be53bd4e07afd8e67e1" - integrity sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw== +eslint-plugin-jsdoc@^48.2.3: + version "48.11.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.11.0.tgz#7c8dae6ce0d814aff54b87fdb808f02635691ade" + integrity sha512-d12JHJDPNo7IFwTOAItCeJY1hcqoIxE0lHA8infQByLilQ9xkqrRa6laWCnsuCrf+8rUnvxXY1XuTbibRBNylA== + dependencies: + "@es-joy/jsdoccomment" "~0.46.0" + are-docs-informative "^0.0.2" + comment-parser "1.4.1" + debug "^4.3.5" + escape-string-regexp "^4.0.0" + espree "^10.1.0" + esquery "^1.6.0" + parse-imports "^2.1.1" + semver "^7.6.3" + spdx-expression-parse "^4.0.0" + synckit "^0.9.1" + +eslint-plugin-n@^17.2.0: + version "17.14.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.14.0.tgz#162a7c17a7ce7e3834af537bca68ab8b6aa26edc" + integrity sha512-maxPLMEA0rPmRpoOlxEclKng4UpDe+N5BJS4t24I3UKnN109Qcivnfs37KMy84G0af3bxjog5lKctP5ObsvcTA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.1" + enhanced-resolve "^5.17.1" + eslint-plugin-es-x "^7.8.0" + get-tsconfig "^4.8.1" + globals "^15.11.0" + ignore "^5.3.2" + minimatch "^9.0.5" + semver "^7.6.3" + +eslint-plugin-prettier@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz#d1c8f972d8f60e414c25465c163d16f209411f95" + integrity sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw== dependencies: prettier-linter-helpers "^1.0.0" - synckit "^0.8.6" + synckit "^0.9.1" -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== +eslint-plugin-unicorn@^52.0.0: + version "52.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz#c7a559edd52e3932cf2b3a05c3b0efc604c1eeb8" + integrity sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng== dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" + "@babel/helper-validator-identifier" "^7.22.20" + "@eslint-community/eslint-utils" "^4.4.0" + "@eslint/eslintrc" "^2.1.4" + ci-info "^4.0.0" + clean-regexp "^1.0.0" + core-js-compat "^3.34.0" + esquery "^1.5.0" + indent-string "^4.0.0" + is-builtin-module "^3.2.1" + jsesc "^3.0.2" + pluralize "^8.0.0" + read-pkg-up "^7.0.1" + regexp-tree "^0.1.27" + regjsparser "^0.10.0" + semver "^7.5.4" + strip-indent "^3.0.0" -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.25.0: - version "8.57.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.13.0: + version "9.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.16.0.tgz#66832e66258922ac0a626f803a9273e37747f2a6" + integrity sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.9.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.16.0" + "@eslint/plugin-kit" "^0.2.3" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" + "@humanwhocodes/retry" "^0.4.1" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.2" + cross-spawn "^7.0.5" debug "^4.3.2" - doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" + file-entry-cache "^8.0.0" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" - 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.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" -espree@^9.6.0, espree@^9.6.1: +espree@^10.0.1, espree@^10.1.0, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +espree@^9.6.0: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== @@ -3996,10 +3180,10 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== +esquery@^1.5.0, esquery@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" @@ -4010,11 +3194,6 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" @@ -4030,6 +3209,13 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +ethereum-bloom-filters@^1.0.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" @@ -4051,6 +3237,16 @@ ethereum-cryptography@^0.1.3: 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.yarnpkg.com/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.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" @@ -4062,41 +3258,13 @@ ethereumjs-util@^7.1.5: ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@^5.1.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" - integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== - dependencies: - "@ethersproject/abi" "5.7.0" - "@ethersproject/abstract-provider" "5.7.0" - "@ethersproject/abstract-signer" "5.7.0" - "@ethersproject/address" "5.7.0" - "@ethersproject/base64" "5.7.0" - "@ethersproject/basex" "5.7.0" - "@ethersproject/bignumber" "5.7.0" - "@ethersproject/bytes" "5.7.0" - "@ethersproject/constants" "5.7.0" - "@ethersproject/contracts" "5.7.0" - "@ethersproject/hash" "5.7.0" - "@ethersproject/hdnode" "5.7.0" - "@ethersproject/json-wallets" "5.7.0" - "@ethersproject/keccak256" "5.7.0" - "@ethersproject/logger" "5.7.0" - "@ethersproject/networks" "5.7.1" - "@ethersproject/pbkdf2" "5.7.0" - "@ethersproject/properties" "5.7.0" - "@ethersproject/providers" "5.7.2" - "@ethersproject/random" "5.7.0" - "@ethersproject/rlp" "5.7.0" - "@ethersproject/sha2" "5.7.0" - "@ethersproject/signing-key" "5.7.0" - "@ethersproject/solidity" "5.7.0" - "@ethersproject/strings" "5.7.0" - "@ethersproject/transactions" "5.7.0" - "@ethersproject/units" "5.7.0" - "@ethersproject/wallet" "5.7.0" - "@ethersproject/web" "5.7.1" - "@ethersproject/wordlists" "5.7.0" +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/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-lite@^0.1.1: version "0.1.3" @@ -4108,6 +3276,11 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventemitter3@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -4151,6 +3324,13 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw== + dependencies: + homedir-polyfill "^1.0.1" + expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -4162,62 +3342,52 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -express-handlebars@^6.0.6: - version "6.0.7" - resolved "https://registry.yarnpkg.com/express-handlebars/-/express-handlebars-6.0.7.tgz#f779254664eff0e250362ef1c2b30587059c212a" - integrity sha512-iYeMFpc/hMD+E6FNAZA5fgWeXnXr4rslOSPkeEV6TwdmpJ5lEXuWX0u9vFYs31P2MURctQq2batR09oeNj0LIg== - dependencies: - glob "^8.1.0" - graceful-fs "^4.2.10" - handlebars "^4.7.7" - -express-prometheus-middleware@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/express-prometheus-middleware/-/express-prometheus-middleware-1.2.0.tgz#ce913ffe4282411ab0d3d2db095771292801cb18" - integrity sha512-efSwft67rdtiW40D0im1f7Rz1TCGHGzPj6lfK0MxZDcPj6z4f/Ab5VNkWPYZEjvLqZiZ7fbS00CYzpigO8tS+g== - dependencies: - response-time "^2.3.2" - url-value-parser "^2.0.0" - optionalDependencies: - prometheus-gc-stats "^0.6.2" - express@^4.18.2: - version "4.19.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" - integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + version "4.21.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" + integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.2" + body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.6.0" + cookie "0.7.1" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" + finalhandler "1.3.1" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.1" + merge-descriptors "1.0.3" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "0.1.10" proxy-addr "~2.0.7" - qs "6.11.0" + qs "6.13.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + 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" +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + fast-copy@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" @@ -4233,7 +3403,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.9: +fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -4264,12 +3434,10 @@ fast-safe-stringify@^2.1.1: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== -fast-xml-parser@4.2.5: - version "4.2.5" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f" - integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== - dependencies: - strnum "^1.0.5" +fast-uri@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== fastq@^1.6.0: version "1.17.1" @@ -4285,33 +3453,60 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: - flat-cache "^3.0.4" + escape-string-regexp "^1.0.5" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" - encodeurl "~1.0.2" + 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-node-modules@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-2.1.3.tgz#3c976cff2ca29ee94b4f9eafc613987fc4c0ee44" + integrity sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg== + dependencies: + findup-sync "^4.0.0" + merge "^2.1.1" + +find-root@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -4328,24 +3523,33 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== +findup-sync@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0" + integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^4.0.2" + resolve-dir "^1.0.1" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" + keyv "^4.5.4" flatted@^3.2.9: - version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== -follow-redirects@^1.15.0, follow-redirects@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== for-each@^0.3.3: version "0.3.3" @@ -4355,18 +3559,19 @@ for-each@^0.3.3: is-callable "^1.1.3" form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + version "2.5.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.2.tgz#dc653743d1de2fcc340ceea38079daf6e9069fd2" + integrity sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6" mime-types "^2.1.12" + safe-buffer "^5.2.1" form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -4382,12 +3587,22 @@ fresh@0.5.2, fresh@^0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +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.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -4412,19 +3627,6 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gc-stats@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/gc-stats/-/gc-stats-1.4.1.tgz#c9142dc54d02af9e93472b0bd972e6bacecc7be5" - integrity sha512-eAvDBpI6UjVIYwLxshPCJJIkPyfamIrJzBtW/103+ooJWkISS+chVnHNnsZ+ubaw2607rFeiRDNWHkNUA+ioqg== - dependencies: - nan "^2.18.0" - node-gyp-build "^4.8.0" - -generic-pool@3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" - integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -4435,7 +3637,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/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.3, get-intrinsic@^1.2.4: +get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== @@ -4465,6 +3667,13 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +get-tsconfig@^4.7.5, get-tsconfig@^4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" + integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== + dependencies: + resolve-pkg-maps "^1.0.0" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -4479,7 +3688,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.3, glob@^7.1.4: +glob@7.2.3, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -4491,16 +3700,32 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== +global-directory@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-4.0.1.tgz#4d7ac7cfd2cb73f304c53b8810891748df5e361e" + integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" + ini "4.1.1" + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg== + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" globals@^11.1.0: version "11.12.0" @@ -4514,33 +3739,32 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^15.11.0: + version "15.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.13.0.tgz#bbec719d69aafef188ecd67954aae76a696010fc" + integrity sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g== -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== 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" + define-properties "^1.2.1" + gopd "^1.0.1" -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== +gopd@^1.0.1, gopd@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.1.0.tgz#df8f0839c2d48caefc32a025a49294d39606c912" + integrity sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA== dependencies: - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.4" -graceful-fs@^4.2.10, graceful-fs@^4.2.9: +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.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -4550,19 +3774,7 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -handlebars@^4.7.7: - version "4.7.8" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" - integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.2" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - -has-bigints@^1.0.1, has-bigints@^1.0.2: +has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== @@ -4585,14 +3797,16 @@ has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: es-define-property "^1.0.0" has-proto@^1.0.1, has-proto@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.1.0.tgz#deb10494cbbe8809bce168a3b961f42969f5ed43" + integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q== + dependencies: + call-bind "^1.0.7" -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" @@ -4610,7 +3824,7 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, 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.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -4649,6 +3863,18 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -4675,7 +3901,7 @@ husky@^8.0.2: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -4692,12 +3918,12 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== -ignore@^5.2.0, ignore@^5.2.4: - version "5.3.1" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" - integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== +ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1, ignore@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -import-fresh@^3.2.1: +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -4705,29 +3931,39 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.7.3.tgz#ffa784cdd57a47d2b68d2e7dd33070ff06baee43" - integrity sha512-R2I11NRi0lI3jD2+qjqyVlVEahsejw7LDnYEbGb47QEFjczE3bZYsmWheCTQA+LFs2DzOQxR7Pms7naHW1V4bQ== +import-in-the-middle@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.2.tgz#dd848e72b63ca6cd7c34df8b8d97fc9baee6174f" + integrity sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA== dependencies: acorn "^8.8.2" - acorn-import-assertions "^1.9.0" + acorn-import-attributes "^1.9.5" cjs-module-lexer "^1.2.2" module-details-from-path "^1.0.3" import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +import-meta-resolve@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706" + integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw== + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -4736,11 +3972,42 @@ inflight@^1.0.4: 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.1: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== + +ini@^1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inquirer@8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" + integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + int64-buffer@^0.1.9: version "0.1.10" resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423" @@ -4770,24 +4037,11 @@ ioredis@^5.4.1: redis-parser "^3.0.0" standard-as-callback "^2.1.0" -ip-address@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" - integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== - dependencies: - jsbn "1.1.0" - sprintf-js "^1.1.3" - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" - integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== - is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" @@ -4801,12 +4055,19 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/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.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== dependencies: - has-bigints "^1.0.1" + has-bigints "^1.0.2" is-binary-path@~2.1.0: version "2.1.0" @@ -4815,25 +4076,32 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== +is-boolean-object@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.0.tgz#9743641e80a62c094b5941c5bb791d66a88e497a" + integrity sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw== dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + call-bind "^1.0.7" + has-tostringtag "^1.0.2" -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + +is-callable@^1.1.3, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/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.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== +is-core-module@^2.13.0, is-core-module@^2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== dependencies: - hasown "^2.0.0" + hasown "^2.0.2" is-data-view@^1.0.1: version "1.0.1" @@ -4842,7 +4110,7 @@ is-data-view@^1.0.1: 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.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -4859,6 +4127,13 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz#d74a7d0c5f3578e34a20729e69202e578d495dc2" + integrity sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA== + dependencies: + call-bind "^1.0.7" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4869,6 +4144,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4876,35 +4158,58 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/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.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== +is-number-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.0.tgz#5a867e9ecc3d294dda740d9f127835857af7eb05" + integrity sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw== dependencies: - has-tostringtag "^1.0.0" + call-bind "^1.0.7" + has-tostringtag "^1.0.2" is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.0.tgz#41b9d266e7eb7451312c64efc37e8a7d453077cf" + integrity sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA== dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + call-bind "^1.0.7" + gopd "^1.1.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-retry-allowed@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" + integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/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" @@ -4923,19 +4228,22 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== +is-string@^1.0.7, is-string@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.0.tgz#8cb83c5d57311bf8058bc6c8db294711641da45d" + integrity sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g== dependencies: - has-tostringtag "^1.0.0" + call-bind "^1.0.7" + has-tostringtag "^1.0.2" -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== +is-symbol@^1.0.4, is-symbol@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.0.tgz#ae993830a56d4781886d39f9f0a46b3e89b7b60b" + integrity sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A== dependencies: - has-symbols "^1.0.2" + call-bind "^1.0.7" + has-symbols "^1.0.3" + safe-regex-test "^1.0.3" is-typed-array@^1.1.13: version "1.1.13" @@ -4944,6 +4252,21 @@ is-typed-array@^1.1.13: dependencies: which-typed-array "^1.1.14" +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-utf8@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -4951,10 +4274,18 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/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-windows@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@^1.0.0: version "1.0.0" @@ -4971,15 +4302,10 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isows@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" - integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== - -isows@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.4.tgz#810cd0d90cc4995c26395d2aa4cfa4037ebdf061" - integrity sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ== +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== istanbul-lib-coverage@3.2.0: version "3.2.0" @@ -5003,9 +4329,9 @@ istanbul-lib-instrument@^5.0.4: semver "^6.3.0" istanbul-lib-instrument@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1" - integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: "@babel/core" "^7.23.9" "@babel/parser" "^7.23.9" @@ -5039,6 +4365,16 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jake@^10.8.5: + version "10.9.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -5397,10 +4733,15 @@ jest@^29.1.2: import-local "^3.0.2" jest-cli "^29.7.0" +jiti@^1.21.6: + version "1.21.6" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" + integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== + joi@^17.6.3: - version "17.12.2" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.2.tgz#283a664dabb80c7e52943c557aab82faea09f521" - integrity sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw== + version "17.13.3" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec" + integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA== dependencies: "@hapi/hoek" "^9.3.0" "@hapi/topo" "^5.1.0" @@ -5413,11 +4754,6 @@ joycon@^3.1.1: resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== -js-sha3@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -5438,15 +4774,20 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsbn@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" - integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== +jsdoc-type-pratt-parser@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" + integrity sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ== -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-buffer@3.0.1: version "3.0.1" @@ -5463,6 +4804,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/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.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -5480,10 +4826,19 @@ json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -kareem@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.5.1.tgz#7b8203e11819a8e77a34b3517d3ead206764d15d" - integrity sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA== +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +kareem@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.6.3.tgz#23168ec8ffb6c1abfd31b7169a6fb1dd285992ac" + integrity sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q== keccak@^3.0.0: version "3.0.4" @@ -5494,7 +4849,7 @@ keccak@^3.0.0: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keyv@^4.5.3: +keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -5558,7 +4913,17 @@ lodash.isarguments@^3.1.0: resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== -lodash.memoize@4.x: +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.map@^4.5.1: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q== + +lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== @@ -5568,21 +4933,44 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/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.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== -lodash@^4.17.21: +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@4.17.21, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + long@^5.0.0: version "5.2.3" resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== +longest@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-2.0.1.tgz#781e183296aa94f6d4d916dc335d0d17aefa23f8" + integrity sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5590,22 +4978,15 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - lru-cache@^7.14.0: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -luxon@~3.4.0: - version "3.4.4" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" - integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== +luxon@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== make-dir@^4.0.0: version "4.0.0" @@ -5614,7 +4995,7 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" -make-error@1.x, make-error@^1.1.1: +make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -5645,12 +5026,7 @@ memory-pager@^1.0.2: resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - -merge-descriptors@^1.0.1: +merge-descriptors@1.0.3, merge-descriptors@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== @@ -5660,22 +5036,43 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +merge@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-2.1.1.tgz#59ef4bf7e0b3e879186436e8481c06a6c162ca98" + integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w== + +merkletreejs@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.4.0.tgz#40514dd8b5f511b3f136aeea3ff90d74d5604112" + integrity sha512-a48Ta5kWiVNBgeEbZVMm6FB1hBlp6vEuou/XnZdlkmd2zq6NZR6Sh2j+kR1B0iOZIXrTMcigBYzZ39MLdYhm1g== + dependencies: + bignumber.js "^9.0.1" + buffer-reverse "^1.0.1" + crypto-js "^4.2.0" + treeify "^1.1.0" + web3-utils "^1.3.4" + methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + +micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" mime-db@1.52.0: @@ -5700,6 +5097,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -5710,7 +5112,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -5724,7 +5126,19 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -5734,65 +5148,45 @@ module-details-from-path@^1.0.3: resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== -mongodb-connection-string-url@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf" - integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ== - dependencies: - "@types/whatwg-url" "^8.2.1" - whatwg-url "^11.0.0" - mongodb-connection-string-url@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz#b4f87f92fd8593f3b9365f592515a06d304a1e9c" - integrity sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ== + version "3.0.1" + resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz#c13e6ac284ae401752ebafdb8cd7f16c6723b141" + integrity sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg== dependencies: "@types/whatwg-url" "^11.0.2" whatwg-url "^13.0.0" -mongodb@4.17.2: - version "4.17.2" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.17.2.tgz#237c0534e36a3449bd74c6bf6d32f87a1ca7200c" - integrity sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg== - dependencies: - bson "^4.7.2" - mongodb-connection-string-url "^2.6.0" - socks "^2.7.1" - optionalDependencies: - "@aws-sdk/credential-providers" "^3.186.0" - "@mongodb-js/saslprep" "^1.1.0" - -mongodb@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.5.0.tgz#3735b4fba085b26ca06f7744e9639bc538e93d87" - integrity sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA== +mongodb@~6.12.0: + version "6.12.0" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.12.0.tgz#8b0bda1b18cbb3f0aec8ab4119c5dc535a43c444" + integrity sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA== dependencies: - "@mongodb-js/saslprep" "^1.1.5" - bson "^6.4.0" + "@mongodb-js/saslprep" "^1.1.9" + bson "^6.10.1" mongodb-connection-string-url "^3.0.0" -mongoose@^6.6.5: - version "6.12.7" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-6.12.7.tgz#97adb534424b2a87a440a592913aae1c12068fc4" - integrity sha512-v3AkUsgHspF8/R4ph5YaF8g+36O7LbvGwlPBbi5VDx+ocT1+t5+HJ8ZqG864l7KwJtWmuWTGGSSz/jg+ydXq3g== +mongoose@^8.9.5: + version "8.9.5" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-8.9.5.tgz#90a2d70b48a66022a61d7a170ab4fad106b83cba" + integrity sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg== dependencies: - bson "^4.7.2" - kareem "2.5.1" - mongodb "4.17.2" + bson "^6.10.1" + kareem "2.6.3" + mongodb "~6.12.0" mpath "0.9.0" - mquery "4.0.3" + mquery "5.0.0" ms "2.1.3" - sift "16.0.1" + sift "17.1.3" mpath@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904" integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew== -mquery@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/mquery/-/mquery-4.0.3.tgz#4d15f938e6247d773a942c912d9748bd1965f89d" - integrity sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA== +mquery@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/mquery/-/mquery-5.0.0.tgz#a95be5dfc610b23862df34a47d3e5d60e110695d" + integrity sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg== dependencies: debug "4.x" @@ -5801,12 +5195,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/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, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -5821,15 +5210,10 @@ msgpack-lite@^0.1.26: int64-buffer "^0.1.9" isarray "^1.0.0" -nan@^2.18.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0" - integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw== - -natural-compare-lite@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" - integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== natural-compare@^1.4.0: version "1.4.0" @@ -5841,12 +5225,7 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -node-abort-controller@^3.0.1, node-abort-controller@^3.1.1: +node-abort-controller@^3.0.1: version "3.1.1" resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== @@ -5856,6 +5235,11 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/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.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + node-addon-api@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" @@ -5873,10 +5257,10 @@ node-gyp-build@<4.0, node-gyp-build@^3.9.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.9.0.tgz#53a350187dd4d5276750da21605d1cb681d09e25" integrity sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A== -node-gyp-build@^4.2.0, node-gyp-build@^4.5.0, node-gyp-build@^4.8.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" - integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== +node-gyp-build@^4.2.0, node-gyp-build@^4.5.0: + version "4.8.4" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" + integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== node-int64@^0.4.0: version "0.4.0" @@ -5884,12 +5268,10 @@ node-int64@^0.4.0: integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-mocks-http@^1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.14.1.tgz#6a387ce09229fe545dcc0154d16bc3480618e013" - integrity sha512-mfXuCGonz0A7uG1FEjnypjm34xegeN5+HI6xeGhYKecfgaZhjsmYoLE9LEFmT+53G1n8IuagPZmVnEL/xNsFaA== + version "1.16.1" + resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.16.1.tgz#9efb1d0ae5817106f256a672dabff11d99d8d8c7" + integrity sha512-Q2m5bmIE1KFeeKI6OsSn+c4XDara5NWnUJgzqnIkhiCNukYX+fqu0ADSeKOlpWtbCwgRnJ69F+7RUiQltzTKXA== dependencies: - "@types/express" "^4.17.21" - "@types/node" "^20.10.6" accepts "^1.3.7" content-disposition "^0.5.3" depd "^1.1.0" @@ -5901,15 +5283,15 @@ node-mocks-http@^1.14.1: range-parser "^1.2.0" type-is "^1.6.18" -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== nodemon@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.0.tgz#ff7394f2450eb6a5e96fe4180acd5176b29799c9" - integrity sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA== + version "3.1.7" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.7.tgz#07cb1f455f8bece6a499e0d72b5e029485521a54" + integrity sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ== dependencies: chokidar "^3.5.2" debug "^4" @@ -5922,12 +5304,15 @@ nodemon@^3.0.1: touch "^3.1.0" undefsafe "^2.0.5" -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: - abbrev "1" + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -5941,27 +5326,30 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -numeral@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" - integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/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" object-assign@^4: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.1, object-inspect@^1.13.3: + version "1.13.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" + integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.2, object.assign@^4.1.5: +object.assign@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== @@ -5971,16 +5359,7 @@ object.assign@^4.1.2, object.assign@^4.1.5: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.5: - version "1.1.8" - resolved "https://registry.yarnpkg.com/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.7: +object.fromentries@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== @@ -5990,7 +5369,7 @@ object.fromentries@^2.0.7: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.groupby@^1.0.1: +object.groupby@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== @@ -5999,7 +5378,7 @@ object.groupby@^1.0.1: define-properties "^1.2.1" es-abstract "^1.23.2" -object.values@^1.1.7: +object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== @@ -6020,11 +5399,6 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -on-headers@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -6032,7 +5406,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.2: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -6044,22 +5418,50 @@ opentracing@>=0.12.1: resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.7.tgz#25d472bd0296dc0b64d7b94cbc995219031428f5" integrity sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q== -optional@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/optional/-/optional-0.1.4.tgz#cdb1a9bedc737d2025f690ceeb50e049444fd5b3" - integrity sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw== - optionator@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" - integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: - "@aashutoshrathi/word-wrap" "^1.2.3" 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" + +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +ox@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.1.2.tgz#0f791be2ccabeaf4928e6d423498fe1c8094e560" + integrity sha512-ak/8K0Rtphg9vnRJlbOdaX9R7cmxD2MiSthjWGaQdMk3D7hrAlDoM+6Lxn7hN52Za3vrXfZ7enfke/5WjolDww== + 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-finally@^1.0.0: version "1.0.0" @@ -6129,7 +5531,15 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^5.2.0: +parse-imports@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/parse-imports/-/parse-imports-2.2.1.tgz#0a6e8b5316beb5c9905f50eb2bbb8c64a4805642" + integrity sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ== + dependencies: + es-module-lexer "^1.5.3" + slashes "^3.0.12" + +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -6139,6 +5549,11 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== + parseurl@^1.3.3, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -6164,15 +5579,15 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.7, path-to-regexp@^0.1.2: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-to-regexp@^0.1.10: + version "0.1.11" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.11.tgz#a527e662c89efc4646dbfa8100bf3e847e495761" + integrity sha512-c0t+KCuUkO/YDLPG4WWzEwx3J5F/GHXsD1h/SNZfySqAIKe/BaP95x8fWtOfRJokpS5yYHRJjMtYlXD8jxnpbw== pbkdf2@^3.0.17: version "3.1.2" @@ -6185,25 +5600,20 @@ pbkdf2@^3.0.17: safe-buffer "^5.0.1" sha.js "^2.4.8" -perf_hooks@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/perf_hooks/-/perf_hooks-0.0.1.tgz#253e7e18b71fcc0390fd3afb2cd7cf1685df040c" - integrity sha512-qG/D9iA4KDme+KF4vCObJy6Bouu3BlQnmJ8jPydVPm32NJBD9ZK1ZNgXSYaZKHkVC1sKSqUiLgFvAZPUiIEnBw== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pino-abstract-transport@^1.0.0, pino-abstract-transport@v1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz#083d98f966262164504afb989bccd05f665937a8" - integrity sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA== +pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" + integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== dependencies: readable-stream "^4.0.0" split2 "^4.0.0" @@ -6243,22 +5653,22 @@ pino-std-serializers@^6.0.0, pino-std-serializers@^6.2.2: resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== -pino@*, pino@^8.15.6, pino@^8.17.1: - version "8.19.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-8.19.0.tgz#ccc15ef736f103ec02cfbead0912bc436dc92ce4" - integrity sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA== +pino@^8.15.6, pino@^8.17.1: + version "8.21.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.21.0.tgz#e1207f3675a2722940d62da79a7a55a98409f00d" + integrity sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q== dependencies: atomic-sleep "^1.0.0" fast-redact "^3.1.1" on-exit-leak-free "^2.1.0" - pino-abstract-transport v1.1.0 + pino-abstract-transport "^1.2.0" pino-std-serializers "^6.0.0" process-warning "^3.0.0" quick-format-unescaped "^4.0.3" real-require "^0.2.0" safe-stable-stringify "^2.3.1" sonic-boom "^3.7.0" - thread-stream "^2.0.0" + thread-stream "^2.6.0" pirates@^4.0.4: version "4.0.6" @@ -6272,6 +5682,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -6294,10 +5709,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^3.1.1: - version "3.2.5" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" - integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== +prettier@^3.3.3: + version "3.4.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.1.tgz#e211d451d6452db0a291672ca9154bc8c2579f7b" + integrity sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg== pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" @@ -6318,23 +5733,6 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -prom-client@^15.1.0: - version "15.1.1" - resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-15.1.1.tgz#71ba84371241acd173181b04a436782c246f3652" - integrity sha512-GVA2H96QCg2q71rjc3VYvSrVG7OpnJxyryC7dMzvfJfpJJHzQVwF3TJLfHzKORcwJpElWs1TwXLthlJAFJxq2A== - dependencies: - "@opentelemetry/api" "^1.4.0" - tdigest "^0.1.1" - -prometheus-gc-stats@^0.6.2: - version "0.6.5" - resolved "https://registry.yarnpkg.com/prometheus-gc-stats/-/prometheus-gc-stats-0.6.5.tgz#c24309f586f4483b67438f0464cd7e3074e023a0" - integrity sha512-VsmpEGHt9mMZqlhL+96gz2LsaXEgu2SXQ/tiEqIBLPoUTyPORDNsEiH9DPPZHChdkTTBw3GRV1wGvqdIg4EktQ== - dependencies: - optional "^0.1.3" - optionalDependencies: - gc-stats "^1.4.0" - prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -6344,9 +5742,9 @@ prompts@^2.0.1: sisteransi "^1.0.5" protobufjs@^7.2.5: - version "7.2.6" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" - integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== + version "7.4.0" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" + integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -6380,14 +5778,14 @@ pstree.remy@^1.1.8: integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + version "3.0.2" + resolved "https://registry.yarnpkg.com/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, punycode@^2.1.1, punycode@^2.3.0: +punycode@^2.1.0, punycode@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -6397,12 +5795,12 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== -qs@6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: - side-channel "^1.0.4" + side-channel "^1.0.6" querystringify@^2.1.1: version "2.2.0" @@ -6442,21 +5840,30 @@ raw-body@2.5.2: unpipe "1.0.0" react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/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" -"readable-stream@1.x >=1.1.9": - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" + "@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.6.0: +readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -6500,18 +5907,6 @@ redis-parser@^3.0.0: dependencies: redis-errors "^1.0.0" -redis@^4.3.1: - version "4.6.13" - resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.13.tgz#e247267c5f3ba35ab8277b57343d3a56acb2f0a6" - integrity sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA== - dependencies: - "@redis/bloom" "1.2.0" - "@redis/client" "1.5.14" - "@redis/graph" "1.1.1" - "@redis/json" "1.0.6" - "@redis/search" "1.1.6" - "@redis/time-series" "1.0.5" - redlock@^5.0.0-beta.2: version "5.0.0-beta.2" resolved "https://registry.yarnpkg.com/redlock/-/redlock-5.0.0-beta.2.tgz#a629c07e07d001c0fdd9f2efa614144c4416fe44" @@ -6519,21 +5914,51 @@ redlock@^5.0.0-beta.2: dependencies: node-abort-controller "^3.0.1" -regexp.prototype.flags@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== +reflect.getprototypeof@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz#04311b33a1b713ca5eb7b5aed9950a86481858e5" + integrity sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g== dependencies: - call-bind "^1.0.6" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + which-builtin-type "^1.1.4" + +regexp-tree@^0.1.27: + version "0.1.27" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== + +regexp.prototype.flags@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== + dependencies: + call-bind "^1.0.7" define-properties "^1.2.1" es-errors "^1.3.0" - set-function-name "^2.0.1" + set-function-name "^2.0.2" + +regjsparser@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.10.0.tgz#b1ed26051736b436f22fdec1c8f72635f9f44892" + integrity sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA== + dependencies: + jsesc "~0.5.0" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/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.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -6546,6 +5971,14 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg== + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -6556,12 +5989,17 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve.exports@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" - integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + version "2.0.3" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== -resolve@^1.20.0, resolve@^1.22.4: +resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -6570,18 +6008,13 @@ resolve@^1.20.0, resolve@^1.22.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -response-time@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/response-time/-/response-time-2.3.2.tgz#ffa71bab952d62f7c1d49b7434355fbc68dffc5a" - integrity sha512-MUIDaDQf+CVqflfTdQ5yam+aYCkXj1PY8fjlPDQ6ppxJlmgZb864pHtA750mayywNg8tx4rS7qH9JXd/OF+3gw== +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: - depd "~1.1.0" - on-headers "~1.0.1" - -rest-api-errors@^1.2.5: - version "1.3.1" - resolved "https://registry.yarnpkg.com/rest-api-errors/-/rest-api-errors-1.3.1.tgz#a206da021bcc908b716e2208063f09e052c49b40" - integrity sha512-BLUjvPne9dREGjnF9oNdmoyjGUNd3G+I14Z0ulMyGppKnLSQw58fXk5I8X0QLZZcBV89M/D+ndHVdto9QuF8sQ== + onetime "^5.1.0" + signal-exit "^3.0.2" retry@^0.13.1: version "0.13.1" @@ -6593,12 +6026,10 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" +rfdc@^1.3.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: version "2.0.2" @@ -6615,6 +6046,11 @@ rlp@^2.2.4: dependencies: bn.js "^5.2.0" +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -6622,6 +6058,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.5.5: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" @@ -6632,7 +6075,7 @@ safe-array-concat@^1.1.2: 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.0: +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.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6652,27 +6095,27 @@ safe-regex-test@^1.0.3: is-regex "^1.1.4" safe-stable-stringify@^2.3.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scrypt-js@3.0.1, scrypt-js@^3.0.0: +scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== secp256k1@^4.0.0, secp256k1@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" - integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + version "4.0.4" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.4.tgz#58f0bfe1830fe777d9ca1ffc7574962a8189f8ab" + integrity sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw== dependencies: - elliptic "^6.5.4" - node-addon-api "^2.0.0" + elliptic "^6.5.7" + node-addon-api "^5.0.0" node-gyp-build "^4.2.0" secure-json-parse@^2.4.0: @@ -6680,22 +6123,25 @@ secure-json-parse@^2.4.0: resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" depd "2.0.0" @@ -6718,15 +6164,15 @@ serialize-error@8.1.0: dependencies: type-fest "^0.20.2" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.18.0" + send "0.19.0" set-function-length@^1.2.1: version "1.2.2" @@ -6740,7 +6186,7 @@ set-function-length@^1.2.1: 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.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== @@ -6781,11 +6227,11 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + version "1.8.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.2.tgz#d2d83e057959d53ec261311e9e9b8f51dcb2934a" + integrity sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA== -side-channel@^1.0.4: +side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== @@ -6795,12 +6241,12 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" -sift@16.0.1: - version "16.0.1" - resolved "https://registry.yarnpkg.com/sift/-/sift-16.0.1.tgz#e9c2ccc72191585008cf3e36fc447b2d2633a053" - integrity sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ== +sift@17.1.3: + version "17.1.3" + resolved "https://registry.yarnpkg.com/sift/-/sift-17.1.3.tgz#9d2000d4d41586880b0079b5183d839c7a142bf7" + integrity sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ== -signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -6822,23 +6268,20 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -smart-buffer@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +slashes@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/slashes/-/slashes-3.0.12.tgz#3d664c877ad542dc1509eaf2c50f38d483a6435a" + integrity sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA== -socks@^2.7.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.1.tgz#22c7d9dd7882649043cba0eafb49ae144e3457af" - integrity sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ== - dependencies: - ip-address "^9.0.5" - smart-buffer "^4.2.0" +solady@^0.0.235: + version "0.0.235" + resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.235.tgz#50ab6e403ed6935012df2c16803fcd58045f9a0e" + integrity sha512-JUEXLDG7ag3HmqUnrDG7ilhafH6R9bFPpwV63O2kH4UbnS2+gRGEOqqy4k01O7tHjo3MWkDD0cpG+UY9pjy/fQ== sonic-boom@^3.0.0, sonic-boom@^3.7.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.0.tgz#e442c5c23165df897d77c3c14ef3ca40dec66a66" - integrity sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA== + version "3.8.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.1.tgz#d5ba8c4e26d6176c9a1d14d549d9ff579a163422" + integrity sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg== dependencies: atomic-sleep "^1.0.0" @@ -6867,16 +6310,45 @@ sparse-bitfield@^3.0.3: dependencies: memory-pager "^1.0.2" +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/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.yarnpkg.com/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-expression-parse@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz#a23af9f3132115465dac215c099303e4ceac5794" + integrity sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ== + 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.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" + integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== + split2@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== -sprintf-js@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" - integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -6935,7 +6407,7 @@ string.prototype.trimend@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string.prototype.trimstart@^1.0.7: +string.prototype.trimstart@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== @@ -6951,11 +6423,6 @@ string_decoder@^1.1.1, string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -6963,31 +6430,40 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-bom@4.0.0, strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@^3.1.1: +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/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.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strnum@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" - integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== - supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -7014,20 +6490,18 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -synckit@^0.8.6: - version "0.8.8" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" - integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ== +synckit@^0.9.1: + version "0.9.2" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62" + integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw== dependencies: "@pkgr/core" "^0.1.0" tslib "^2.6.2" -tdigest@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" - integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA== - dependencies: - bintrees "1.0.2" +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== test-exclude@^6.0.0: version "6.0.0" @@ -7038,33 +6512,35 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -thread-stream@^2.0.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.4.1.tgz#6d588b14f0546e59d3f306614f044bc01ce43351" - integrity sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg== +thread-stream@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.7.0.tgz#d8a8e1b3fd538a6cca8ce69dbe5d3d097b601e11" + integrity sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw== dependencies: real-require "^0.2.0" +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + tlhunter-sorted-set@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/tlhunter-sorted-set/-/tlhunter-sorted-set-0.1.0.tgz#1c3eae28c0fa4dff97e9501d2e3c204b86406f4b" integrity sha512-eGYW4bjf1DtrHzUYxYfAcSytpOkA44zsr7G2n3PV7yOUR23vmkGe3LL4R+1jL9OsXtbsFOwe8XtbCrabeaEFnw== +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/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.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -7078,18 +6554,9 @@ toidentifier@1.0.1: integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== touch@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" - integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== - dependencies: - nopt "~1.0.10" - -tr46@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" - integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== - dependencies: - punycode "^2.1.1" + version "3.1.1" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" + integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== tr46@^4.1.1: version "4.1.1" @@ -7098,19 +6565,30 @@ tr46@^4.1.1: dependencies: punycode "^2.3.0" +treeify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== + +ts-api-utils@^1.3.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" + integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== + ts-jest@^29.0.3: - version "29.1.2" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" - integrity sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g== + version "29.2.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63" + integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" jest-util "^29.0.0" json5 "^2.2.3" - lodash.memoize "4.x" - make-error "1.x" - semver "^7.5.3" - yargs-parser "^21.0.1" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.6.3" + yargs-parser "^21.1.1" ts-node@^10.9.1: version "10.9.2" @@ -7141,22 +6619,20 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.11.1, tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2, tslib@^2.7.0: + 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.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== +tsx@^4.19.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.19.2.tgz#2d7814783440e0ae42354d0417d9c2989a2ae92c" + integrity sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g== dependencies: - tslib "^1.8.1" + esbuild "~0.23.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -7180,6 +6656,16 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + type-is@^1.6.18, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -7209,9 +6695,9 @@ typed-array-byte-length@^1.0.1: is-typed-array "^1.1.13" typed-array-byte-offset@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" - integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz#3fa9f22567700cc86aaf86a1e7176f74b59600f2" + integrity sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.7" @@ -7219,28 +6705,33 @@ typed-array-byte-offset@^1.0.2: gopd "^1.0.1" has-proto "^1.0.3" is-typed-array "^1.1.13" + reflect.getprototypeof "^1.0.6" -typed-array-length@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" - integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== +typed-array-length@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== 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" + reflect.getprototypeof "^1.0.6" -typescript@^5.3.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" - integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== +typescript-eslint@^8.11.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.17.0.tgz#fa4033c26b3b40f778287bc12918d985481b220b" + integrity sha512-409VXvFd/f1br1DCbuKNFqQpXICoTB+V51afcwG1pn1a3Cp92MqAUges3YjwEdQ0cMUoCIodjVDAYzyD8h3SYA== + dependencies: + "@typescript-eslint/eslint-plugin" "8.17.0" + "@typescript-eslint/parser" "8.17.0" + "@typescript-eslint/utils" "8.17.0" -uglify-js@^3.1.4: - version "3.17.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" - integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== +typescript@^5.3.3: + version "5.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" + integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== unbox-primitive@^1.0.2: version "1.0.2" @@ -7257,23 +6748,28 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.2.0" + picocolors "^1.1.0" uri-js@^4.2.2: version "4.4.1" @@ -7290,10 +6786,10 @@ url-parse@~1.5.10: querystringify "^2.1.1" requires-port "^1.0.0" -url-value-parser@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/url-value-parser/-/url-value-parser-2.2.0.tgz#f38ae8cd24604ec69bc219d66929ddbbd93a2b32" - integrity sha512-yIQdxJpgkPamPPAPuGdS7Q548rLhny42tg8d4vyTNzFqvOnwqrgHXvgehT09U7fwrzxi3RxCiXjoNUNnNOlQ8A== +utf8@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== util-deprecate@^1.0.1: version "1.0.2" @@ -7305,11 +6801,6 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - uuid@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -7321,46 +6812,41 @@ v8-compile-cache-lib@^3.0.1: integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: - version "9.2.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" - integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== + version "9.3.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/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" + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -viem@^2.17.3: - version "2.17.3" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.17.3.tgz#f15616049d8154b83e499eb5446e6d7fe6312626" - integrity sha512-FY/1uBQWfko4Esy8mU1RamvL64TLy91LZwFyQJ20E6AI3vTTEOctWfSn0pkMKa3okq4Gxs5dJE7q1hmWOQ7xcw== - dependencies: - "@adraffy/ens-normalize" "1.10.0" - "@noble/curves" "1.4.0" - "@noble/hashes" "1.4.0" - "@scure/bip32" "1.4.0" - "@scure/bip39" "1.3.0" - abitype "1.0.5" - isows "1.0.4" - ws "8.17.1" - -viem@^2.7.8: - version "2.9.3" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.9.3.tgz#7de5253b9a9907b4db0f3a4444677b2cdf743589" - integrity sha512-HVw0JLFCAerIt2pavToQdFS73a42L1Lhenh7YN8tIOtOdr/N7FhOqbzs3+PuEBJiRy/MJVrz9aWb5smulaTcWQ== - dependencies: - "@adraffy/ens-normalize" "1.10.0" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@scure/bip32" "1.3.2" - "@scure/bip39" "1.2.1" - abitype "1.0.0" - isows "1.0.3" - ws "8.13.0" +viem@^2.21.49: + version "2.21.54" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.54.tgz#76d6f86ab8809078f1ac140ac1a2beadbc86b9f6" + integrity sha512-G9mmtbua3UtnVY9BqAtWdNp+3AO+oWhD0B9KaEsZb6gcrOWgmA4rz02yqEMg+qW9m6KgKGie7q3zcHqJIw6AqA== + dependencies: + "@noble/curves" "1.7.0" + "@noble/hashes" "1.6.1" + "@scure/bip32" "1.6.0" + "@scure/bip39" "1.5.0" + abitype "1.0.7" + isows "1.0.6" + ox "0.1.2" + webauthn-p256 "0.0.10" + ws "8.18.0" walker@^1.0.8: version "1.0.8" @@ -7369,19 +6855,40 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +web3-utils@^1.3.4: + 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" + +webauthn-p256@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.10.tgz#877e75abe8348d3e14485932968edf3325fd2fdd" + integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA== + dependencies: + "@noble/curves" "^1.4.0" + "@noble/hashes" "^1.4.0" + webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -whatwg-url@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" - integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== - dependencies: - tr46 "^3.0.0" - webidl-conversions "^7.0.0" - whatwg-url@^13.0.0: version "13.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-13.0.0.tgz#b7b536aca48306394a34e44bda8e99f332410f8f" @@ -7391,20 +6898,49 @@ whatwg-url@^13.0.0: webidl-conversions "^7.0.0" which-boxed-primitive@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz#2d850d6c4ac37b95441a67890e19f3fda8b6c6d9" + integrity sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.0" + is-number-object "^1.1.0" + is-string "^1.1.0" + is-symbol "^1.1.0" + +which-builtin-type@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.0.tgz#58042ac9602d78a6d117c7e811349df1268ba63c" + integrity sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA== + dependencies: + call-bind "^1.0.7" + 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.1.0" + 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.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== 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" + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" which-typed-array@^1.1.14, which-typed-array@^1.1.15: - version "1.1.15" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" - integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + version "1.1.16" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.16.tgz#db4db429c4706feca2f01677a144278e4a8c216b" + integrity sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.7" @@ -7412,6 +6948,13 @@ which-typed-array@^1.1.14, which-typed-array@^1.1.15: gopd "^1.0.1" has-tostringtag "^1.0.2" +which@^1.2.14: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -7419,10 +6962,10 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== +word-wrap@^1.0.3, word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== wrap-ansi@^7.0.0: version "7.0.0" @@ -7446,37 +6989,22 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@7.4.6: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== - -ws@8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== - -ws@8.17.1: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +ws@8.18.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@4.0.0, yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@^21.0.1, yargs-parser@^21.1.1: +yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -7504,12 +7032,12 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zod-validation-error@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-2.1.0.tgz#208eac75237dfed47c0018d2fe8fd03501bfc9ac" - integrity sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ== +zod-validation-error@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== -zod@^3.22.4: - version "3.22.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" - integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== +zod@^3.24.1: + version "3.24.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee" + integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==